From 6618d696e44a50c9e0f076b8d3e457cc615c9c07 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Thu, 19 Aug 2021 14:11:14 -0400 Subject: [PATCH] Migrate the session table to be keyed off of libsignal IDs. --- .../ConversationSettingsRepository.kt | 9 +- .../contacts/sync/DirectoryHelper.java | 5 +- .../storage/TextSecureSessionStore.java | 84 +++------ .../securesms/database/RecipientDatabase.java | 12 +- .../securesms/database/SessionDatabase.java | 159 ++++++++---------- .../securesms/database/SignalDatabase.java | 2 - .../database/helpers/SQLCipherOpenHelper.java | 25 ++- .../helpers/SessionStoreMigrationHelper.java | 2 +- 8 files changed, 142 insertions(+), 156 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt index cc3414cb3..e32a4daa1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsRepository.kt @@ -220,7 +220,14 @@ class ConversationSettingsRepository( Preconditions.checkArgument(FeatureFlags.internalUser(), "Internal users only!") SignalExecutors.BOUNDED.execute { - DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId) + val recipient = Recipient.resolved(recipientId) + + if (recipient.hasUuid()) { + DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipient.requireUuid().toString()) + } + if (recipient.hasE164()) { + DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipient.requireE164()) + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java index 289ae20f9..81914ed5d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/DirectoryHelper.java @@ -543,8 +543,9 @@ public class DirectoryHelper { } private static boolean hasCommunicatedWith(@NonNull Context context, @NonNull Recipient recipient) { - return DatabaseFactory.getThreadDatabase(context).hasThread(recipient.getId()) || - DatabaseFactory.getSessionDatabase(context).hasSessionFor(recipient.getId()); + return DatabaseFactory.getThreadDatabase(context).hasThread(recipient.getId()) || + (recipient.hasUuid() && DatabaseFactory.getSessionDatabase(context).hasSessionFor(recipient.requireUuid().toString())) || + (recipient.hasE164() && DatabaseFactory.getSessionDatabase(context).hasSessionFor(recipient.requireE164())); } static class DirectoryResult { diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java index 15958b24c..c2ff66e7f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java @@ -7,7 +7,6 @@ import androidx.annotation.NonNull; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.SessionDatabase; -import org.thoughtcrime.securesms.database.SessionDatabase.RecipientDevice; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.whispersystems.libsignal.NoSessionException; @@ -16,9 +15,7 @@ import org.whispersystems.libsignal.protocol.CiphertextMessage; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.signalservice.api.SignalServiceSessionStore; -import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; public class TextSecureSessionStore implements SignalServiceSessionStore { @@ -35,8 +32,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public SessionRecord loadSession(@NonNull SignalProtocolAddress address) { synchronized (LOCK) { - RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); - SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId()); + SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(address); if (sessionRecord == null) { Log.w(TAG, "No existing session information found."); @@ -50,11 +46,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public List loadExistingSessions(List addresses) throws NoSessionException { synchronized (LOCK) { - List ids = addresses.stream() - .map(address -> new RecipientDevice(RecipientId.fromExternalPush(address.getName()), address.getDeviceId())) - .collect(Collectors.toList()); - - List sessionRecords = DatabaseFactory.getSessionDatabase(context).load(ids); + List sessionRecords = DatabaseFactory.getSessionDatabase(context).load(addresses); if (sessionRecords.size() != addresses.size()) { String message = "Mismatch! Asked for " + addresses.size() + " sessions, but only found " + sessionRecords.size() + "!"; @@ -69,96 +61,76 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { @Override public void storeSession(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) { synchronized (LOCK) { - RecipientId id = RecipientId.fromExternalPush(address.getName()); - DatabaseFactory.getSessionDatabase(context).store(id, address.getDeviceId(), record); + DatabaseFactory.getSessionDatabase(context).store(address, record); } } @Override public boolean containsSession(SignalProtocolAddress address) { synchronized (LOCK) { - if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { - RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); - SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(recipientId, address.getDeviceId()); + SessionRecord sessionRecord = DatabaseFactory.getSessionDatabase(context).load(address); - return sessionRecord != null && - sessionRecord.hasSenderChain() && - sessionRecord.getSessionVersion() == CiphertextMessage.CURRENT_VERSION; - } else { - return false; - } + return sessionRecord != null && + sessionRecord.hasSenderChain() && + sessionRecord.getSessionVersion() == CiphertextMessage.CURRENT_VERSION; } } @Override public void deleteSession(SignalProtocolAddress address) { synchronized (LOCK) { - if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { - RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); - DatabaseFactory.getSessionDatabase(context).delete(recipientId, address.getDeviceId()); - } else { - Log.w(TAG, "Tried to delete session for " + address.toString() + ", but none existed!"); - } + DatabaseFactory.getSessionDatabase(context).delete(address); } } @Override public void deleteAllSessions(String name) { synchronized (LOCK) { - if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) { - RecipientId recipientId = RecipientId.fromExternalPush(name); - DatabaseFactory.getSessionDatabase(context).deleteAllFor(recipientId); - } + DatabaseFactory.getSessionDatabase(context).deleteAllFor(name); } } @Override public List getSubDeviceSessions(String name) { synchronized (LOCK) { - if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(name)) { - RecipientId recipientId = RecipientId.fromExternalPush(name); - return DatabaseFactory.getSessionDatabase(context).getSubDevices(recipientId); - } else { - Log.w(TAG, "Tried to get sub device sessions for " + name + ", but none existed!"); - return Collections.emptyList(); - } + return DatabaseFactory.getSessionDatabase(context).getSubDevices(name); } } @Override public void archiveSession(SignalProtocolAddress address) { synchronized (LOCK) { - if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { - RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); - archiveSession(recipientId, address.getDeviceId()); + SessionRecord session = DatabaseFactory.getSessionDatabase(context).load(address); + if (session != null) { + session.archiveCurrentState(); + DatabaseFactory.getSessionDatabase(context).store(address, session); } } } public void archiveSession(@NonNull RecipientId recipientId, int deviceId) { synchronized (LOCK) { - SessionRecord session = DatabaseFactory.getSessionDatabase(context).load(recipientId, deviceId); - if (session != null) { - session.archiveCurrentState(); - DatabaseFactory.getSessionDatabase(context).store(recipientId, deviceId, session); + Recipient recipient = Recipient.resolved(recipientId); + + if (recipient.hasUuid()) { + archiveSession(new SignalProtocolAddress(recipient.requireUuid().toString(), deviceId)); + } + + if (recipient.hasE164()) { + archiveSession(new SignalProtocolAddress(recipient.requireE164(), deviceId)); } } } public void archiveSiblingSessions(@NonNull SignalProtocolAddress address) { synchronized (LOCK) { - if (DatabaseFactory.getRecipientDatabase(context).containsPhoneOrUuid(address.getName())) { - RecipientId recipientId = RecipientId.fromExternalPush(address.getName()); - List sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(recipientId); + List sessions = DatabaseFactory.getSessionDatabase(context).getAllFor(address.getName()); - for (SessionDatabase.SessionRow row : sessions) { - if (row.getDeviceId() != address.getDeviceId()) { - row.getRecord().archiveCurrentState(); - storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireServiceId(), row.getDeviceId()), row.getRecord()); - } + for (SessionDatabase.SessionRow row : sessions) { + if (row.getDeviceId() != address.getDeviceId()) { + row.getRecord().archiveCurrentState(); + storeSession(new SignalProtocolAddress(row.getAddress(), row.getDeviceId()), row.getRecord()); } - } else { - Log.w(TAG, "Tried to archive sibling sessions for " + address.toString() + ", but none existed!"); } } } @@ -169,7 +141,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { for (SessionDatabase.SessionRow row : sessions) { row.getRecord().archiveCurrentState(); - storeSession(new SignalProtocolAddress(Recipient.resolved(row.getRecipientId()).requireServiceId(), row.getDeviceId()), row.getRecord()); + storeSession(new SignalProtocolAddress(row.getAddress(), row.getDeviceId()), row.getRecord()); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index a9344a1e2..8bac0e8cf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -2961,17 +2961,19 @@ public class RecipientDatabase extends Database { } // Sessions - boolean hasE164Session = DatabaseFactory.getSessionDatabase(context).getAllFor(byE164).size() > 0; - boolean hasUuidSession = DatabaseFactory.getSessionDatabase(context).getAllFor(byUuid).size() > 0; + SessionDatabase sessionDatabase = DatabaseFactory.getSessionDatabase(context); + + boolean hasE164Session = sessionDatabase.getAllFor(e164Settings.e164).size() > 0; + boolean hasUuidSession = sessionDatabase.getAllFor(uuidSettings.uuid.toString()).size() > 0; if (hasE164Session && hasUuidSession) { Log.w(TAG, "Had a session for both users. Deleting the E164.", true); - db.delete(SessionDatabase.TABLE_NAME, SessionDatabase.RECIPIENT_ID + " = ?", SqlUtil.buildArgs(byE164)); + sessionDatabase.deleteAllFor(e164Settings.e164); } else if (hasE164Session && !hasUuidSession) { Log.w(TAG, "Had a session for E164, but not UUID. Re-assigning to the UUID.", true); ContentValues values = new ContentValues(); - values.put(SessionDatabase.RECIPIENT_ID, byUuid.serialize()); - db.update(SessionDatabase.TABLE_NAME, values, SessionDatabase.RECIPIENT_ID + " = ?", SqlUtil.buildArgs(byE164)); + values.put(SessionDatabase.ADDRESS, uuidSettings.uuid.toString()); + db.update(SessionDatabase.TABLE_NAME, values, SessionDatabase.ADDRESS + " = ?", SqlUtil.buildArgs(e164Settings.e164)); } else if (!hasE164Session && hasUuidSession) { Log.w(TAG, "Had a session for UUID, but not E164. No action necessary.", true); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java index d3e1d7e56..5f94f3874 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SessionDatabase.java @@ -1,18 +1,20 @@ package org.thoughtcrime.securesms.database; -import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import net.zetetic.database.sqlcipher.SQLiteStatement; + import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper; import org.signal.core.util.logging.Log; -import org.thoughtcrime.securesms.recipients.RecipientId; +import org.thoughtcrime.securesms.util.CursorUtil; import org.thoughtcrime.securesms.util.SqlUtil; +import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.signalservice.api.push.SignalServiceAddress; @@ -27,40 +29,42 @@ public class SessionDatabase extends Database { public static final String TABLE_NAME = "sessions"; - private static final String ID = "_id"; - public static final String RECIPIENT_ID = "address"; - public static final String DEVICE = "device"; - public static final String RECORD = "record"; + private static final String ID = "_id"; + public static final String ADDRESS = "address"; + public static final String DEVICE = "device"; + public static final String RECORD = "record"; - public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + - "(" + ID + " INTEGER PRIMARY KEY, " + RECIPIENT_ID + " INTEGER NOT NULL, " + - DEVICE + " INTEGER NOT NULL, " + RECORD + " BLOB NOT NULL, " + - "UNIQUE(" + RECIPIENT_ID + "," + DEVICE + ") ON CONFLICT REPLACE);"; + public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + ADDRESS + " TEXT NOT NULL, " + + DEVICE + " INTEGER NOT NULL, " + + RECORD + " BLOB NOT NULL, " + + "UNIQUE(" + ADDRESS + "," + DEVICE + "));"; SessionDatabase(Context context, SQLCipherOpenHelper databaseHelper) { super(context, databaseHelper); } - public void store(@NonNull RecipientId recipientId, int deviceId, @NonNull SessionRecord record) { - SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); + public void store(@NonNull SignalProtocolAddress address, @NonNull SessionRecord record) { + SQLiteDatabase db = databaseHelper.getSignalWritableDatabase(); - ContentValues values = new ContentValues(); - values.put(RECIPIENT_ID, recipientId.serialize()); - values.put(DEVICE, deviceId); - values.put(RECORD, record.serialize()); - - database.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_REPLACE); + try (SQLiteStatement statement = db.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " + DEVICE + ", " + RECORD + ") VALUES (?, ?, ?) " + + "ON CONFLICT (" + ADDRESS + ", " + DEVICE + ") DO UPDATE SET " + RECORD + " = excluded." + RECORD)) + { + statement.bindString(1, address.getName()); + statement.bindLong(2, address.getDeviceId()); + statement.bindBlob(3, record.serialize()); + statement.execute(); + } } - public @Nullable SessionRecord load(@NonNull RecipientId recipientId, int deviceId) { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); + public @Nullable SessionRecord load(@NonNull SignalProtocolAddress address) { + SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); + String[] projection = new String[] { RECORD }; + String selection = ADDRESS + " = ? AND " + DEVICE + " = ?"; + String[] args = SqlUtil.buildArgs(address.getName(), address.getDeviceId()); - try (Cursor cursor = database.query(TABLE_NAME, new String[]{RECORD}, - RECIPIENT_ID + " = ? AND " + DEVICE + " = ?", - new String[] {recipientId.serialize(), String.valueOf(deviceId)}, - null, null, null)) - { - if (cursor != null && cursor.moveToFirst()) { + try (Cursor cursor = database.query(TABLE_NAME, projection, selection, args, null, null, null)) { + if (cursor.moveToFirst()) { try { return new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))); } catch (IOException e) { @@ -72,17 +76,17 @@ public class SessionDatabase extends Database { return null; } - public @NonNull List load(@NonNull List ids) { + public @NonNull List load(@NonNull List addresses) { SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - List sessions = new ArrayList<>(ids.size()); + List sessions = new ArrayList<>(addresses.size()); database.beginTransaction(); try { - String[] projection = new String[]{RECORD}; - String query = RECIPIENT_ID + " = ? AND " + DEVICE + " = ?"; + String[] projection = new String[] { RECORD }; + String query = ADDRESS + " = ? AND " + DEVICE + " = ?"; - for (RecipientDevice id : ids) { - String[] args = SqlUtil.buildArgs(id.getRecipientId(), id.getDevice()); + for (SignalProtocolAddress address : addresses) { + String[] args = SqlUtil.buildArgs(address.getName(), address.getDeviceId()); try (Cursor cursor = database.query(TABLE_NAME, projection, query, args, null, null, null)) { if (cursor.moveToFirst()) { @@ -102,19 +106,15 @@ public class SessionDatabase extends Database { return sessions; } - public @NonNull List getAllFor(@NonNull RecipientId recipientId) { + public @NonNull List getAllFor(@NonNull String addressName) { SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); List results = new LinkedList<>(); - try (Cursor cursor = database.query(TABLE_NAME, null, - RECIPIENT_ID + " = ?", - new String[] {recipientId.serialize()}, - null, null, null)) - { - while (cursor != null && cursor.moveToNext()) { + try (Cursor cursor = database.query(TABLE_NAME, null, ADDRESS + " = ?", SqlUtil.buildArgs(addressName), null, null, null)) { + while (cursor.moveToNext()) { try { - results.add(new SessionRow(recipientId, - cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)), + results.add(new SessionRow(CursorUtil.requireString(cursor, ADDRESS), + CursorUtil.requireInt(cursor, DEVICE), new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))))); } catch (IOException e) { Log.w(TAG, e); @@ -130,10 +130,10 @@ public class SessionDatabase extends Database { List results = new LinkedList<>(); try (Cursor cursor = database.query(TABLE_NAME, null, null, null, null, null, null)) { - while (cursor != null && cursor.moveToNext()) { + while (cursor.moveToNext()) { try { - results.add(new SessionRow(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))), - cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)), + results.add(new SessionRow(CursorUtil.requireString(cursor, ADDRESS), + CursorUtil.requireInt(cursor, DEVICE), new SessionRecord(cursor.getBlob(cursor.getColumnIndexOrThrow(RECORD))))); } catch (IOException e) { Log.w(TAG, e); @@ -144,16 +144,15 @@ public class SessionDatabase extends Database { return results; } - public @NonNull List getSubDevices(@NonNull RecipientId recipientId) { - SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - List results = new LinkedList<>(); + public @NonNull List getSubDevices(@NonNull String addressName) { + SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); + List results = new LinkedList<>(); + String[] projection = new String[] { DEVICE }; + String selection = ADDRESS + " = ?"; + String[] args = SqlUtil.buildArgs(addressName); - try (Cursor cursor = database.query(TABLE_NAME, new String[] {DEVICE}, - RECIPIENT_ID + " = ?", - new String[] {recipientId.serialize()}, - null, null, null)) - { - while (cursor != null && cursor.moveToNext()) { + try (Cursor cursor = database.query(TABLE_NAME, projection, selection, args, null, null, null)) { + while (cursor.moveToNext()) { int device = cursor.getInt(cursor.getColumnIndexOrThrow(DEVICE)); if (device != SignalServiceAddress.DEFAULT_DEVICE_ID) { @@ -165,41 +164,43 @@ public class SessionDatabase extends Database { return results; } - public void delete(@NonNull RecipientId recipientId, int deviceId) { + public void delete(@NonNull SignalProtocolAddress address) { SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); + String selection = ADDRESS + " = ? AND " + DEVICE + " = ?"; + String[] args = SqlUtil.buildArgs(address.getName(), address.getDeviceId()); - database.delete(TABLE_NAME, RECIPIENT_ID + " = ? AND " + DEVICE + " = ?", - new String[] {recipientId.serialize(), String.valueOf(deviceId)}); + + database.delete(TABLE_NAME, selection, args); } - public void deleteAllFor(@NonNull RecipientId recipientId) { + public void deleteAllFor(@NonNull String addressName) { SQLiteDatabase database = databaseHelper.getSignalWritableDatabase(); - database.delete(TABLE_NAME, RECIPIENT_ID + " = ?", new String[] {recipientId.serialize()}); + database.delete(TABLE_NAME, ADDRESS + " = ?", SqlUtil.buildArgs(addressName)); } - public boolean hasSessionFor(@NonNull RecipientId recipientId) { + public boolean hasSessionFor(@NonNull String addressName) { SQLiteDatabase database = databaseHelper.getSignalReadableDatabase(); - String query = RECIPIENT_ID + " = ?"; - String[] args = SqlUtil.buildArgs(recipientId); + String query = ADDRESS + " = ?"; + String[] args = SqlUtil.buildArgs(addressName); - try (Cursor cursor = database.query(TABLE_NAME, new String[] { ID }, query, args, null, null, null, "1")) { - return cursor != null && cursor.moveToFirst(); + try (Cursor cursor = database.query(TABLE_NAME, new String[] { "1" }, query, args, null, null, null, "1")) { + return cursor.moveToFirst(); } } public static final class SessionRow { - private final RecipientId recipientId; + private final String address; private final int deviceId; private final SessionRecord record; - public SessionRow(@NonNull RecipientId recipientId, int deviceId, SessionRecord record) { - this.recipientId = recipientId; - this.deviceId = deviceId; - this.record = record; + public SessionRow(@NonNull String address, int deviceId, SessionRecord record) { + this.address = address; + this.deviceId = deviceId; + this.record = record; } - public RecipientId getRecipientId() { - return recipientId; + public @NonNull String getAddress() { + return address; } public int getDeviceId() { @@ -210,22 +211,4 @@ public class SessionDatabase extends Database { return record; } } - - public static final class RecipientDevice { - private final RecipientId recipientId; - private final int device; - - public RecipientDevice(@NonNull RecipientId recipientId, int device) { - this.recipientId = recipientId; - this.device = device; - } - - public @NonNull RecipientId getRecipientId() { - return recipientId; - } - - public int getDevice() { - return device; - } - } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.java index a7104cb10..ed278cbba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.java @@ -1,7 +1,5 @@ package org.thoughtcrime.securesms.database; -import androidx.annotation.NonNull; - import net.zetetic.database.sqlcipher.SQLiteDatabase; /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 3bd5878e0..c8a1e98fc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -210,8 +210,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab private static final int ABANDONED_ATTACHMENT_CLEANUP = 110; private static final int AVATAR_PICKER = 111; private static final int THREAD_CLEANUP = 112; + private static final int SESSION_MIGRATION = 113; - private static final int DATABASE_VERSION = 112; + private static final int DATABASE_VERSION = 113; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -1965,6 +1966,28 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper implements SignalDatab db.delete("part", "mid != -8675309 AND mid NOT IN (SELECT _id FROM mms)", null); } + if (oldVersion < SESSION_MIGRATION) { + long start = System.currentTimeMillis(); + + db.execSQL("CREATE TABLE sessions_tmp (_id INTEGER PRIMARY KEY AUTOINCREMENT, " + + "address TEXT NOT NULL, " + + "device INTEGER NOT NULL, " + + "record BLOB NOT NULL, " + + "UNIQUE(address, device))"); + + db.execSQL("INSERT INTO sessions_tmp (address, device, record) " + + "SELECT COALESCE(recipient.uuid, recipient.phone) AS new_address, " + + "sessions.device, " + + "sessions.record " + + "FROM sessions INNER JOIN recipient ON sessions.address = recipient._id " + + "WHERE new_address NOT NULL"); + + db.execSQL("DROP TABLE sessions"); + db.execSQL("ALTER TABLE sessions_tmp RENAME TO sessions"); + + Log.d(TAG, "Session migration took " + (System.currentTimeMillis() - start) + " ms"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java index e67c81099..ca91f9b59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SessionStoreMigrationHelper.java @@ -73,7 +73,7 @@ class SessionStoreMigrationHelper { ContentValues contentValues = new ContentValues(); - contentValues.put(SessionDatabase.RECIPIENT_ID, address); + contentValues.put(SessionDatabase.ADDRESS, address); contentValues.put(SessionDatabase.DEVICE, deviceId); contentValues.put(SessionDatabase.RECORD, sessionRecord.serialize());