Support for sealed sender - Part 1

This commit is contained in:
Moxie Marlinspike
2018-05-22 02:13:10 -07:00
committed by Greyson Parrelli
parent b7b9554364
commit 5f31762220
67 changed files with 1679 additions and 801 deletions

View File

@@ -17,11 +17,12 @@ public class GroupReceiptDatabase extends Database {
public static final String TABLE_NAME = "group_receipts";
private static final String ID = "_id";
public static final String MMS_ID = "mms_id";
private static final String ADDRESS = "address";
private static final String STATUS = "status";
private static final String TIMESTAMP = "timestamp";
private static final String ID = "_id";
public static final String MMS_ID = "mms_id";
private static final String ADDRESS = "address";
private static final String STATUS = "status";
private static final String TIMESTAMP = "timestamp";
private static final String UNIDENTIFIED = "unidentified";
public static final int STATUS_UNKNOWN = -1;
public static final int STATUS_UNDELIVERED = 0;
@@ -29,7 +30,7 @@ public class GroupReceiptDatabase extends Database {
public static final int STATUS_READ = 2;
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
MMS_ID + " INTEGER, " + ADDRESS + " TEXT, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER);";
MMS_ID + " INTEGER, " + ADDRESS + " TEXT, " + STATUS + " INTEGER, " + TIMESTAMP + " INTEGER, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXES = {
"CREATE INDEX IF NOT EXISTS group_receipt_mms_id_index ON " + TABLE_NAME + " (" + MMS_ID + ");",
@@ -63,6 +64,16 @@ public class GroupReceiptDatabase extends Database {
new String[] {String.valueOf(mmsId), address.serialize(), String.valueOf(status)});
}
public void setUnidentified(Address address, long mmsId, boolean unidentified) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues(1);
values.put(UNIDENTIFIED, unidentified ? 1 : 0);
db.update(TABLE_NAME, values, MMS_ID + " = ? AND " + ADDRESS + " = ?",
new String[] {String.valueOf(mmsId), address.serialize()});
}
public @NonNull List<GroupReceiptInfo> getGroupReceiptInfo(long mmsId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<GroupReceiptInfo> results = new LinkedList<>();
@@ -71,7 +82,8 @@ public class GroupReceiptDatabase extends Database {
while (cursor != null && cursor.moveToNext()) {
results.add(new GroupReceiptInfo(Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS))),
cursor.getInt(cursor.getColumnIndexOrThrow(STATUS)),
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP))));
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED)) == 1));
}
}
@@ -92,11 +104,13 @@ public class GroupReceiptDatabase extends Database {
private final Address address;
private final int status;
private final long timestamp;
private final boolean unidentified;
public GroupReceiptInfo(Address address, int status, long timestamp) {
this.address = address;
this.status = status;
this.timestamp = timestamp;
GroupReceiptInfo(Address address, int status, long timestamp, boolean unidentified) {
this.address = address;
this.status = status;
this.timestamp = timestamp;
this.unidentified = unidentified;
}
public Address getAddress() {
@@ -110,5 +124,9 @@ public class GroupReceiptDatabase extends Database {
public long getTimestamp() {
return timestamp;
}
public boolean isUnidentified() {
return unidentified;
}
}
}

View File

@@ -23,7 +23,6 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import org.thoughtcrime.securesms.logging.Log;
import android.util.Pair;
import com.annimon.stream.Stream;
@@ -52,6 +51,7 @@ import org.thoughtcrime.securesms.database.model.NotificationMmsMessageRecord;
import org.thoughtcrime.securesms.database.model.Quote;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
@@ -125,7 +125,7 @@ public class MmsDatabase extends MessagingDatabase {
EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " INTEGER DEFAULT 0, " +
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + QUOTE_ID + " INTEGER DEFAULT 0, " +
QUOTE_AUTHOR + " TEXT, " + QUOTE_BODY + " TEXT, " + QUOTE_ATTACHMENT + " INTEGER DEFAULT -1, " +
QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT);";
QUOTE_MISSING + " INTEGER DEFAULT 0, " + SHARED_CONTACTS + " TEXT, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS mms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@@ -145,7 +145,7 @@ public class MmsDatabase extends MessagingDatabase {
MESSAGE_SIZE, STATUS, TRANSACTION_ID,
BODY, PART_COUNT, ADDRESS, ADDRESS_DEVICE_ID,
DELIVERY_RECEIPT_COUNT, READ_RECEIPT_COUNT, MISMATCHED_IDENTITIES, NETWORK_FAILURE, SUBSCRIPTION_ID,
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, SHARED_CONTACTS,
EXPIRES_IN, EXPIRE_STARTED, NOTIFIED, QUOTE_ID, QUOTE_AUTHOR, QUOTE_BODY, QUOTE_ATTACHMENT, QUOTE_MISSING, SHARED_CONTACTS, UNIDENTIFIED,
"json_group_array(json_object(" +
"'" + AttachmentDatabase.ROW_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.ROW_ID + ", " +
"'" + AttachmentDatabase.UNIQUE_ID + "', " + AttachmentDatabase.TABLE_NAME + "." + AttachmentDatabase.UNIQUE_ID + ", " +
@@ -403,6 +403,14 @@ public class MmsDatabase extends MessagingDatabase {
notifyConversationListeners(threadId);
}
public void markUnidentified(long messageId, boolean unidentified) {
ContentValues contentValues = new ContentValues();
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)});
}
@Override
public void markExpireStarted(long messageId) {
markExpireStarted(messageId, System.currentTimeMillis());
@@ -575,6 +583,8 @@ public class MmsDatabase extends MessagingDatabase {
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS));
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(THREAD_ID));
int distributionType = DatabaseFactory.getThreadDatabase(context).getDistributionType(threadId);
String mismatchDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.MISMATCHED_IDENTITIES));
String networkDocument = cursor.getString(cursor.getColumnIndexOrThrow(MmsDatabase.NETWORK_FAILURE));
long quoteId = cursor.getLong(cursor.getColumnIndexOrThrow(QUOTE_ID));
String quoteAuthor = cursor.getString(cursor.getColumnIndexOrThrow(QUOTE_AUTHOR));
@@ -585,20 +595,38 @@ public class MmsDatabase extends MessagingDatabase {
Set<Attachment> contactAttachments = new HashSet<>(Stream.of(contacts).map(Contact::getAvatarAttachment).filter(a -> a != null).toList());
List<Attachment> attachments = Stream.of(associatedAttachments).filterNot(Attachment::isQuote).filterNot(contactAttachments::contains).map(a -> (Attachment)a).toList();
Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false);
QuoteModel quote = null;
Recipient recipient = Recipient.from(context, Address.fromSerialized(address), false);
List<NetworkFailure> networkFailures = new LinkedList<>();
List<IdentityKeyMismatch> mismatches = new LinkedList<>();
QuoteModel quote = null;
if (quoteId > 0 && (!TextUtils.isEmpty(quoteText) || !quoteAttachments.isEmpty())) {
quote = new QuoteModel(quoteId, Address.fromSerialized(quoteAuthor), quoteText, quoteMissing, quoteAttachments);
}
if (!TextUtils.isEmpty(mismatchDocument)) {
try {
mismatches = JsonUtils.fromJson(mismatchDocument, IdentityKeyMismatchList.class).getList();
} catch (IOException e) {
Log.w(TAG, e);
}
}
if (!TextUtils.isEmpty(networkDocument)) {
try {
networkFailures = JsonUtils.fromJson(networkDocument, NetworkFailureList.class).getList();
} catch (IOException e) {
Log.w(TAG, e);
}
}
if (body != null && (Types.isGroupQuit(outboxType) || Types.isGroupUpdate(outboxType))) {
return new OutgoingGroupMediaMessage(recipient, body, attachments, timestamp, 0, quote, contacts);
} else if (Types.isExpirationTimerUpdate(outboxType)) {
return new OutgoingExpirationUpdateMessage(recipient, timestamp, expiresIn);
}
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts);
OutgoingMediaMessage message = new OutgoingMediaMessage(recipient, body, attachments, timestamp, subscriptionId, expiresIn, distributionType, quote, contacts, networkFailures, mismatches);
if (Types.isSecureType(outboxType)) {
return new OutgoingSecureMediaMessage(message);
@@ -730,6 +758,7 @@ public class MmsDatabase extends MessagingDatabase {
contentValues.put(SUBSCRIPTION_ID, retrieved.getSubscriptionId());
contentValues.put(EXPIRES_IN, retrieved.getExpiresIn());
contentValues.put(READ, retrieved.isExpirationUpdate() ? 1 : 0);
contentValues.put(UNIDENTIFIED, retrieved.isUnidentified());
if (!contentValues.containsKey(DATE_SENT)) {
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
@@ -1181,7 +1210,7 @@ public class MmsDatabase extends MessagingDatabase {
message.getOutgoingQuote().isOriginalMissing(),
new SlideDeck(context, message.getOutgoingQuote().getAttachments())) :
null,
message.getSharedContacts());
message.getSharedContacts(), false);
}
}
@@ -1269,6 +1298,7 @@ public class MmsDatabase extends MessagingDatabase {
int subscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.SUBSCRIPTION_ID));
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.EXPIRE_STARTED));
boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.UNIDENTIFIED)) == 1;
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
readReceiptCount = 0;
@@ -1287,7 +1317,7 @@ public class MmsDatabase extends MessagingDatabase {
addressDeviceId, dateSent, dateReceived, deliveryReceiptCount,
threadId, body, slideDeck, partCount, box, mismatches,
networkFailures, subscriptionId, expiresIn, expireStarted,
readReceiptCount, quote, contacts);
readReceiptCount, quote, contacts, unidentified);
}
private Recipient getRecipientFor(String serialized) {

View File

@@ -19,6 +19,7 @@ public interface MmsSmsColumns {
public static final String EXPIRES_IN = "expires_in";
public static final String EXPIRE_STARTED = "expire_started";
public static final String NOTIFIED = "notified";
public static final String UNIDENTIFIED = "unidentified";
public static class Types {
protected static final long TOTAL_MASK = 0xFFFFFFFF;

View File

@@ -49,7 +49,9 @@ public class MmsSmsDatabase extends Database {
MmsSmsColumns.NORMALIZED_DATE_SENT,
MmsSmsColumns.NORMALIZED_DATE_RECEIVED,
MmsDatabase.MESSAGE_TYPE, MmsDatabase.MESSAGE_BOX,
SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
SmsDatabase.STATUS,
MmsSmsColumns.UNIDENTIFIED,
MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY,
MmsDatabase.STATUS,
@@ -232,6 +234,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
MmsDatabase.UNIDENTIFIED,
MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT,
MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsSmsColumns.SUBSCRIPTION_ID, MmsSmsColumns.EXPIRES_IN, MmsSmsColumns.EXPIRE_STARTED,
@@ -256,6 +259,7 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.MESSAGE_BOX, SmsDatabase.STATUS, MmsDatabase.PART_COUNT,
MmsDatabase.CONTENT_LOCATION, MmsDatabase.TRANSACTION_ID,
MmsDatabase.MESSAGE_SIZE, MmsDatabase.EXPIRY, MmsDatabase.STATUS,
MmsDatabase.UNIDENTIFIED,
MmsSmsColumns.DELIVERY_RECEIPT_COUNT, MmsSmsColumns.READ_RECEIPT_COUNT,
MmsSmsColumns.MISMATCHED_IDENTITIES,
MmsSmsColumns.SUBSCRIPTION_ID, MmsSmsColumns.EXPIRES_IN, MmsSmsColumns.EXPIRE_STARTED,
@@ -266,7 +270,8 @@ public class MmsSmsDatabase extends Database {
MmsDatabase.QUOTE_BODY,
MmsDatabase.QUOTE_MISSING,
MmsDatabase.QUOTE_ATTACHMENT,
MmsDatabase.SHARED_CONTACTS};
MmsDatabase.SHARED_CONTACTS,
MmsDatabase.UNIDENTIFIED};
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
@@ -304,6 +309,7 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add(MmsDatabase.EXPIRY);
mmsColumnsPresent.add(MmsDatabase.NOTIFIED);
mmsColumnsPresent.add(MmsDatabase.STATUS);
mmsColumnsPresent.add(MmsDatabase.UNIDENTIFIED);
mmsColumnsPresent.add(MmsDatabase.NETWORK_FAILURE);
mmsColumnsPresent.add(AttachmentDatabase.ROW_ID);
@@ -351,6 +357,7 @@ public class MmsSmsDatabase extends Database {
smsColumnsPresent.add(SmsDatabase.DATE_SENT);
smsColumnsPresent.add(SmsDatabase.DATE_RECEIVED);
smsColumnsPresent.add(SmsDatabase.STATUS);
smsColumnsPresent.add(SmsDatabase.UNIDENTIFIED);
@SuppressWarnings("deprecation")
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery(TRANSPORT, mmsProjection, mmsColumnsPresent, 4, MMS_TRANSPORT, selection, null, MmsDatabase.TABLE_NAME + "." + MmsDatabase.ID, null);

View File

@@ -20,17 +20,20 @@ public class PushDatabase extends Database {
private static final String TAG = PushDatabase.class.getSimpleName();
private static final String TABLE_NAME = "push";
public static final String ID = "_id";
public static final String TYPE = "type";
public static final String SOURCE = "source";
public static final String DEVICE_ID = "device_id";
public static final String LEGACY_MSG = "body";
public static final String CONTENT = "content";
public static final String TIMESTAMP = "timestamp";
private static final String TABLE_NAME = "push";
public static final String ID = "_id";
public static final String TYPE = "type";
public static final String SOURCE = "source";
public static final String DEVICE_ID = "device_id";
public static final String LEGACY_MSG = "body";
public static final String CONTENT = "content";
public static final String TIMESTAMP = "timestamp";
public static final String SERVER_TIMESTAMP = "server_timestamp";
public static final String SERVER_GUID = "server_guid";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER);";
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DEVICE_ID + " INTEGER, " + LEGACY_MSG + " TEXT, " + CONTENT + " TEXT, " + TIMESTAMP + " INTEGER, " +
SERVER_TIMESTAMP + " INTEGER DEFAULT 0, " + SERVER_GUID + " TEXT DEFAULT NULL);";
public PushDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
@@ -49,6 +52,8 @@ public class PushDatabase extends Database {
values.put(LEGACY_MSG, envelope.hasLegacyMessage() ? Base64.encodeBytes(envelope.getLegacyMessage()) : "");
values.put(CONTENT, envelope.hasContent() ? Base64.encodeBytes(envelope.getContent()) : "");
values.put(TIMESTAMP, envelope.getTimestamp());
values.put(SERVER_TIMESTAMP, envelope.getServerTimestamp());
values.put(SERVER_GUID, envelope.getUuid());
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
}
@@ -69,10 +74,11 @@ public class PushDatabase extends Database {
return new SignalServiceEnvelope(cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)),
cursor.getString(cursor.getColumnIndexOrThrow(SOURCE)),
cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID)),
"",
cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP)),
Util.isEmpty(legacyMessage) ? null : Base64.decode(legacyMessage),
Util.isEmpty(content) ? null : Base64.decode(content));
Util.isEmpty(content) ? null : Base64.decode(content),
cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP)),
cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID)));
}
} catch (IOException e) {
Log.w(TAG, e);
@@ -135,16 +141,19 @@ public class PushDatabase extends Database {
if (cursor == null || !cursor.moveToNext())
return null;
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
int deviceId = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE_ID));
String legacyMessage = cursor.getString(cursor.getColumnIndexOrThrow(LEGACY_MSG));
String content = cursor.getString(cursor.getColumnIndexOrThrow(CONTENT));
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
long serverTimestamp = cursor.getLong(cursor.getColumnIndexOrThrow(SERVER_TIMESTAMP));
String serverGuid = cursor.getString(cursor.getColumnIndexOrThrow(SERVER_GUID));
return new SignalServiceEnvelope(type, source, deviceId, "", timestamp,
return new SignalServiceEnvelope(type, source, deviceId, timestamp,
legacyMessage != null ? Base64.decode(legacyMessage) : null,
content != null ? Base64.decode(content) : null);
content != null ? Base64.decode(content) : null,
serverTimestamp, serverGuid);
} catch (IOException e) {
throw new AssertionError(e);
}

View File

@@ -6,7 +6,6 @@ import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.thoughtcrime.securesms.logging.Log;
import com.annimon.stream.Stream;
@@ -14,6 +13,7 @@ import net.sqlcipher.database.SQLiteDatabase;
import org.thoughtcrime.securesms.color.MaterialColor;
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Util;
@@ -33,34 +33,36 @@ public class RecipientDatabase extends Database {
private static final String TAG = RecipientDatabase.class.getSimpleName();
static final String TABLE_NAME = "recipient_preferences";
private static final String ID = "_id";
static final String ADDRESS = "recipient_ids";
private static final String BLOCK = "block";
private static final String NOTIFICATION = "notification";
private static final String VIBRATE = "vibrate";
private static final String MUTE_UNTIL = "mute_until";
private static final String COLOR = "color";
private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
private static final String EXPIRE_MESSAGES = "expire_messages";
private static final String REGISTERED = "registered";
private static final String PROFILE_KEY = "profile_key";
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
private static final String SYSTEM_PHOTO_URI = "system_contact_photo";
private static final String SYSTEM_PHONE_LABEL = "system_phone_label";
private static final String SYSTEM_CONTACT_URI = "system_contact_uri";
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
private static final String PROFILE_SHARING = "profile_sharing_approval";
private static final String CALL_RINGTONE = "call_ringtone";
private static final String CALL_VIBRATE = "call_vibrate";
private static final String NOTIFICATION_CHANNEL = "notification_channel";
static final String TABLE_NAME = "recipient_preferences";
private static final String ID = "_id";
static final String ADDRESS = "recipient_ids";
private static final String BLOCK = "block";
private static final String NOTIFICATION = "notification";
private static final String VIBRATE = "vibrate";
private static final String MUTE_UNTIL = "mute_until";
private static final String COLOR = "color";
private static final String SEEN_INVITE_REMINDER = "seen_invite_reminder";
private static final String DEFAULT_SUBSCRIPTION_ID = "default_subscription_id";
private static final String EXPIRE_MESSAGES = "expire_messages";
private static final String REGISTERED = "registered";
private static final String PROFILE_KEY = "profile_key";
private static final String SYSTEM_DISPLAY_NAME = "system_display_name";
private static final String SYSTEM_PHOTO_URI = "system_contact_photo";
private static final String SYSTEM_PHONE_LABEL = "system_phone_label";
private static final String SYSTEM_CONTACT_URI = "system_contact_uri";
private static final String SIGNAL_PROFILE_NAME = "signal_profile_name";
private static final String SIGNAL_PROFILE_AVATAR = "signal_profile_avatar";
private static final String PROFILE_SHARING = "profile_sharing_approval";
private static final String CALL_RINGTONE = "call_ringtone";
private static final String CALL_VIBRATE = "call_vibrate";
private static final String NOTIFICATION_CHANNEL = "notification_channel";
private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode";
private static final String[] RECIPIENT_PROJECTION = new String[] {
BLOCK, NOTIFICATION, CALL_RINGTONE, VIBRATE, CALL_VIBRATE, MUTE_UNTIL, COLOR, SEEN_INVITE_REMINDER, DEFAULT_SUBSCRIPTION_ID, EXPIRE_MESSAGES, REGISTERED,
PROFILE_KEY, SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_CONTACT_URI,
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL
SIGNAL_PROFILE_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL,
UNIDENTIFIED_ACCESS_MODE
};
static final List<String> TYPED_RECIPIENT_PROJECTION = Stream.of(RECIPIENT_PROJECTION)
@@ -103,6 +105,24 @@ public class RecipientDatabase extends Database {
}
}
public enum UnidentifiedAccessMode {
UNKNOWN(0), DISABLED(1), ENABLED(2), UNRESTRICTED(3);
private final int mode;
UnidentifiedAccessMode(int mode) {
this.mode = mode;
}
public int getMode() {
return mode;
}
public static UnidentifiedAccessMode fromMode(int mode) {
return values()[mode];
}
}
public static final String CREATE_TABLE =
"CREATE TABLE " + TABLE_NAME +
" (" + ID + " INTEGER PRIMARY KEY, " +
@@ -126,7 +146,8 @@ public class RecipientDatabase extends Database {
PROFILE_SHARING + " INTEGER DEFAULT 0, " +
CALL_RINGTONE + " TEXT DEFAULT NULL, " +
CALL_VIBRATE + " INTEGER DEFAULT " + VibrateState.DEFAULT.getId() + ", " +
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL);";
NOTIFICATION_CHANNEL + " TEXT DEFAULT NULL, " +
UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0);";
public RecipientDatabase(Context context, SQLCipherOpenHelper databaseHelper) {
super(context, databaseHelper);
@@ -169,29 +190,30 @@ public class RecipientDatabase extends Database {
}
Optional<RecipientSettings> getRecipientSettings(@NonNull Cursor cursor) {
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
int callVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(CALL_VIBRATE));
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR));
boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1;
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED));
String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY));
String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
String systemContactPhoto = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHOTO_URI));
String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL));
String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI));
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCK)) == 1;
String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION));
String callRingtone = cursor.getString(cursor.getColumnIndexOrThrow(CALL_RINGTONE));
int messageVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(VIBRATE));
int callVibrateState = cursor.getInt(cursor.getColumnIndexOrThrow(CALL_VIBRATE));
long muteUntil = cursor.getLong(cursor.getColumnIndexOrThrow(MUTE_UNTIL));
String serializedColor = cursor.getString(cursor.getColumnIndexOrThrow(COLOR));
boolean seenInviteReminder = cursor.getInt(cursor.getColumnIndexOrThrow(SEEN_INVITE_REMINDER)) == 1;
int defaultSubscriptionId = cursor.getInt(cursor.getColumnIndexOrThrow(DEFAULT_SUBSCRIPTION_ID));
int expireMessages = cursor.getInt(cursor.getColumnIndexOrThrow(EXPIRE_MESSAGES));
int registeredState = cursor.getInt(cursor.getColumnIndexOrThrow(REGISTERED));
String profileKeyString = cursor.getString(cursor.getColumnIndexOrThrow(PROFILE_KEY));
String systemDisplayName = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_DISPLAY_NAME));
String systemContactPhoto = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHOTO_URI));
String systemPhoneLabel = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_PHONE_LABEL));
String systemContactUri = cursor.getString(cursor.getColumnIndexOrThrow(SYSTEM_CONTACT_URI));
String signalProfileName = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_NAME));
String signalProfileAvatar = cursor.getString(cursor.getColumnIndexOrThrow(SIGNAL_PROFILE_AVATAR));
boolean profileSharing = cursor.getInt(cursor.getColumnIndexOrThrow(PROFILE_SHARING)) == 1;
String notificationChannel = cursor.getString(cursor.getColumnIndexOrThrow(NOTIFICATION_CHANNEL));
int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE));
MaterialColor color;
byte[] profileKey = null;
byte[] profileKey = null;
try {
color = serializedColor == null ? null : MaterialColor.fromSerialized(serializedColor);
@@ -219,7 +241,7 @@ public class RecipientDatabase extends Database {
profileKey, systemDisplayName, systemContactPhoto,
systemPhoneLabel, systemContactUri,
signalProfileName, signalProfileAvatar, profileSharing,
notificationChannel));
notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode)));
}
public BulkOperationsHandle resetAllSystemContactInfo() {
@@ -309,6 +331,13 @@ public class RecipientDatabase extends Database {
recipient.resolve().setExpireMessages(expiration);
}
public void setUnidentifiedAccessMode(@NonNull Recipient recipient, @NonNull UnidentifiedAccessMode unidentifiedAccessMode) {
ContentValues values = new ContentValues(1);
values.put(UNIDENTIFIED_ACCESS_MODE, unidentifiedAccessMode.getMode());
updateOrInsert(recipient.getAddress(), values);
recipient.resolve().setUnidentifiedAccessMode(unidentifiedAccessMode);
}
public void setProfileKey(@NonNull Recipient recipient, @Nullable byte[] profileKey) {
ContentValues values = new ContentValues(1);
values.put(PROFILE_KEY, profileKey == null ? null : Base64.encodeBytes(profileKey));
@@ -506,26 +535,27 @@ public class RecipientDatabase extends Database {
}
public static class RecipientSettings {
private final boolean blocked;
private final long muteUntil;
private final VibrateState messageVibrateState;
private final VibrateState callVibrateState;
private final Uri messageRingtone;
private final Uri callRingtone;
private final MaterialColor color;
private final boolean seenInviteReminder;
private final int defaultSubscriptionId;
private final int expireMessages;
private final RegisteredState registered;
private final byte[] profileKey;
private final String systemDisplayName;
private final String systemContactPhoto;
private final String systemPhoneLabel;
private final String systemContactUri;
private final String signalProfileName;
private final String signalProfileAvatar;
private final boolean profileSharing;
private final String notificationChannel;
private final boolean blocked;
private final long muteUntil;
private final VibrateState messageVibrateState;
private final VibrateState callVibrateState;
private final Uri messageRingtone;
private final Uri callRingtone;
private final MaterialColor color;
private final boolean seenInviteReminder;
private final int defaultSubscriptionId;
private final int expireMessages;
private final RegisteredState registered;
private final byte[] profileKey;
private final String systemDisplayName;
private final String systemContactPhoto;
private final String systemPhoneLabel;
private final String systemContactUri;
private final String signalProfileName;
private final String signalProfileAvatar;
private final boolean profileSharing;
private final String notificationChannel;
private final UnidentifiedAccessMode unidentifiedAccessMode;
RecipientSettings(boolean blocked, long muteUntil,
@NonNull VibrateState messageVibrateState,
@@ -545,28 +575,30 @@ public class RecipientDatabase extends Database {
@Nullable String signalProfileName,
@Nullable String signalProfileAvatar,
boolean profileSharing,
@Nullable String notificationChannel)
@Nullable String notificationChannel,
@NonNull UnidentifiedAccessMode unidentifiedAccessMode)
{
this.blocked = blocked;
this.muteUntil = muteUntil;
this.messageVibrateState = messageVibrateState;
this.callVibrateState = callVibrateState;
this.messageRingtone = messageRingtone;
this.callRingtone = callRingtone;
this.color = color;
this.seenInviteReminder = seenInviteReminder;
this.defaultSubscriptionId = defaultSubscriptionId;
this.expireMessages = expireMessages;
this.registered = registered;
this.profileKey = profileKey;
this.systemDisplayName = systemDisplayName;
this.systemContactPhoto = systemContactPhoto;
this.systemPhoneLabel = systemPhoneLabel;
this.systemContactUri = systemContactUri;
this.signalProfileName = signalProfileName;
this.signalProfileAvatar = signalProfileAvatar;
this.profileSharing = profileSharing;
this.notificationChannel = notificationChannel;
this.blocked = blocked;
this.muteUntil = muteUntil;
this.messageVibrateState = messageVibrateState;
this.callVibrateState = callVibrateState;
this.messageRingtone = messageRingtone;
this.callRingtone = callRingtone;
this.color = color;
this.seenInviteReminder = seenInviteReminder;
this.defaultSubscriptionId = defaultSubscriptionId;
this.expireMessages = expireMessages;
this.registered = registered;
this.profileKey = profileKey;
this.systemDisplayName = systemDisplayName;
this.systemContactPhoto = systemContactPhoto;
this.systemPhoneLabel = systemPhoneLabel;
this.systemContactUri = systemContactUri;
this.signalProfileName = signalProfileName;
this.signalProfileAvatar = signalProfileAvatar;
this.profileSharing = profileSharing;
this.notificationChannel = notificationChannel;
this.unidentifiedAccessMode = unidentifiedAccessMode;
}
public @Nullable MaterialColor getColor() {
@@ -613,7 +645,7 @@ public class RecipientDatabase extends Database {
return registered;
}
public byte[] getProfileKey() {
public @Nullable byte[] getProfileKey() {
return profileKey;
}
@@ -648,6 +680,10 @@ public class RecipientDatabase extends Database {
public @Nullable String getNotificationChannel() {
return notificationChannel;
}
public @NonNull UnidentifiedAccessMode getUnidentifiedAccessMode() {
return unidentifiedAccessMode;
}
}
public static class RecipientReader implements Closeable {

View File

@@ -22,7 +22,6 @@ import android.content.Context;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import org.thoughtcrime.securesms.logging.Log;
import android.util.Pair;
import com.annimon.stream.Stream;
@@ -38,6 +37,7 @@ import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.database.model.SmsMessageRecord;
import org.thoughtcrime.securesms.jobmanager.JobManager;
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.sms.IncomingGroupMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
@@ -81,7 +81,7 @@ public class SmsDatabase extends MessagingDatabase {
DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0," + SUBJECT + " TEXT, " + BODY + " TEXT, " +
MISMATCHED_IDENTITIES + " TEXT DEFAULT NULL, " + SERVICE_CENTER + " TEXT, " + SUBSCRIPTION_ID + " INTEGER DEFAULT -1, " +
EXPIRES_IN + " INTEGER DEFAULT 0, " + EXPIRE_STARTED + " INTEGER DEFAULT 0, " + NOTIFIED + " DEFAULT 0, " +
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0);";
READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNIDENTIFIED + " INTEGER DEFAULT 0);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@@ -99,7 +99,7 @@ public class SmsDatabase extends MessagingDatabase {
PROTOCOL, READ, STATUS, TYPE,
REPLY_PATH_PRESENT, SUBJECT, BODY, SERVICE_CENTER, DELIVERY_RECEIPT_COUNT,
MISMATCHED_IDENTITIES, SUBSCRIPTION_ID, EXPIRES_IN, EXPIRE_STARTED,
NOTIFIED, READ_RECEIPT_COUNT
NOTIFIED, READ_RECEIPT_COUNT, UNIDENTIFIED
};
private static final EarlyReceiptCache earlyDeliveryReceiptCache = new EarlyReceiptCache();
@@ -243,6 +243,14 @@ public class SmsDatabase extends MessagingDatabase {
updateTypeBitmask(id, Types.TOTAL_MASK, Types.MISSED_CALL_TYPE);
}
public void markUnidentified(long id, boolean unidentified) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(UNIDENTIFIED, unidentified ? 1 : 0);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(id)});
}
@Override
public void markExpireStarted(long id) {
markExpireStarted(id, System.currentTimeMillis());
@@ -559,6 +567,7 @@ public class SmsDatabase extends MessagingDatabase {
values.put(READ, unread ? 0 : 1);
values.put(SUBSCRIPTION_ID, message.getSubscriptionId());
values.put(EXPIRES_IN, message.getExpiresIn());
values.put(UNIDENTIFIED, message.isUnidentified());
if (!TextUtils.isEmpty(message.getPseudoSubject()))
values.put(SUBJECT, message.getPseudoSubject());
@@ -818,7 +827,7 @@ public class SmsDatabase extends MessagingDatabase {
0, message.isSecureMessage() ? MmsSmsColumns.Types.getOutgoingEncryptedMessageType() : MmsSmsColumns.Types.getOutgoingSmsMessageType(),
threadId, 0, new LinkedList<IdentityKeyMismatch>(),
message.getSubscriptionId(), message.getExpiresIn(),
System.currentTimeMillis(), 0);
System.currentTimeMillis(), 0, false);
}
}
@@ -858,6 +867,7 @@ public class SmsDatabase extends MessagingDatabase {
long expiresIn = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRES_IN));
long expireStarted = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.EXPIRE_STARTED));
String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY));
boolean unidentified = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.UNIDENTIFIED)) == 1;
if (!TextSecurePreferences.isReadReceiptsEnabled(context)) {
readReceiptCount = 0;
@@ -871,7 +881,7 @@ public class SmsDatabase extends MessagingDatabase {
addressDeviceId,
dateSent, dateReceived, deliveryReceiptCount, type,
threadId, status, mismatches, subscriptionId,
expiresIn, expireStarted, readReceiptCount);
expiresIn, expireStarted, readReceiptCount, unidentified);
}
private List<IdentityKeyMismatch> getMismatches(String document) {

View File

@@ -56,8 +56,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
private static final int BAD_IMPORT_CLEANUP = 10;
private static final int QUOTE_MISSING = 11;
private static final int NOTIFICATION_CHANNELS = 12;
private static final int SECRET_SENDER = 13;
private static final int DATABASE_VERSION = 12;
private static final int DATABASE_VERSION = 13;
private static final String DATABASE_NAME = "signal.db";
private final Context context;
@@ -284,6 +285,12 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper {
}
}
if (oldVersion < SECRET_SENDER) {
db.execSQL("ALTER TABLE recipient_preferences ADD COLUMN unidentified_access_mode INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE push ADD COLUMN server_timestamp INTEGER DEFAULT 0");
db.execSQL("ALTER TABLE push ADD COLUMN server_guid TEXT DEFAULT NULL");
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();

View File

@@ -55,11 +55,12 @@ public class MediaMmsMessageRecord extends MmsMessageRecord {
List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> failures, int subscriptionId,
long expiresIn, long expireStarted, int readReceiptCount,
@Nullable Quote quote, @Nullable List<Contact> contacts)
@Nullable Quote quote, @Nullable List<Contact> contacts,
boolean unidentified)
{
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent,
dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox, mismatches, failures,
subscriptionId, expiresIn, expireStarted, slideDeck, readReceiptCount, quote, contacts);
subscriptionId, expiresIn, expireStarted, slideDeck, readReceiptCount, quote, contacts, unidentified);
this.context = context.getApplicationContext();
this.partCount = partCount;

View File

@@ -53,6 +53,7 @@ public abstract class MessageRecord extends DisplayRecord {
private final int subscriptionId;
private final long expiresIn;
private final long expireStarted;
private final boolean unidentified;
MessageRecord(Context context, long id, String body, Recipient conversationRecipient,
Recipient individualRecipient, int recipientDeviceId,
@@ -61,7 +62,7 @@ public abstract class MessageRecord extends DisplayRecord {
List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> networkFailures,
int subscriptionId, long expiresIn, long expireStarted,
int readReceiptCount)
int readReceiptCount, boolean unidentified)
{
super(context, body, conversationRecipient, dateSent, dateReceived,
threadId, deliveryStatus, deliveryReceiptCount, type, readReceiptCount);
@@ -73,6 +74,7 @@ public abstract class MessageRecord extends DisplayRecord {
this.subscriptionId = subscriptionId;
this.expiresIn = expiresIn;
this.expireStarted = expireStarted;
this.unidentified = unidentified;
}
public abstract boolean isMms();
@@ -242,4 +244,8 @@ public abstract class MessageRecord extends DisplayRecord {
public long getExpireStarted() {
return expireStarted;
}
public boolean isUnidentified() {
return unidentified;
}
}

View File

@@ -27,9 +27,9 @@ public abstract class MmsMessageRecord extends MessageRecord {
long type, List<IdentityKeyMismatch> mismatches,
List<NetworkFailure> networkFailures, int subscriptionId, long expiresIn,
long expireStarted, @NonNull SlideDeck slideDeck, int readReceiptCount,
@Nullable Quote quote, @NonNull List<Contact> contacts)
@Nullable Quote quote, @NonNull List<Contact> contacts, boolean unidentified)
{
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount);
super(context, id, body, conversationRecipient, individualRecipient, recipientDeviceId, dateSent, dateReceived, threadId, deliveryStatus, deliveryReceiptCount, type, mismatches, networkFailures, subscriptionId, expiresIn, expireStarted, readReceiptCount, unidentified);
this.slideDeck = slideDeck;
this.quote = quote;

View File

@@ -56,7 +56,7 @@ public class NotificationMmsMessageRecord extends MmsMessageRecord {
super(context, id, "", conversationRecipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, Status.STATUS_NONE, deliveryReceiptCount, mailbox,
new LinkedList<IdentityKeyMismatch>(), new LinkedList<NetworkFailure>(), subscriptionId,
0, 0, slideDeck, readReceiptCount, null, Collections.emptyList());
0, 0, slideDeck, readReceiptCount, null, Collections.emptyList(), false);
this.contentLocation = contentLocation;
this.messageSize = messageSize;

View File

@@ -47,12 +47,12 @@ public class SmsMessageRecord extends MessageRecord {
long type, long threadId,
int status, List<IdentityKeyMismatch> mismatches,
int subscriptionId, long expiresIn, long expireStarted,
int readReceiptCount)
int readReceiptCount, boolean unidentified)
{
super(context, id, body, recipient, individualRecipient, recipientDeviceId,
dateSent, dateReceived, threadId, status, deliveryReceiptCount, type,
mismatches, new LinkedList<>(), subscriptionId,
expiresIn, expireStarted, readReceiptCount);
expiresIn, expireStarted, readReceiptCount, unidentified);
}
public long getType() {