mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-20 16:49:40 +01:00
Migrate the session table to be keyed off of libsignal IDs.
This commit is contained in:
committed by
Alex Hart
parent
c24dfdce34
commit
6618d696e4
@@ -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 {
|
||||
|
||||
@@ -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<SessionRecord> load(@NonNull List<RecipientDevice> ids) {
|
||||
public @NonNull List<SessionRecord> load(@NonNull List<SignalProtocolAddress> addresses) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
List<SessionRecord> sessions = new ArrayList<>(ids.size());
|
||||
List<SessionRecord> 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<SessionRow> getAllFor(@NonNull RecipientId recipientId) {
|
||||
public @NonNull List<SessionRow> getAllFor(@NonNull String addressName) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
List<SessionRow> 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<SessionRow> 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<Integer> getSubDevices(@NonNull RecipientId recipientId) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
List<Integer> results = new LinkedList<>();
|
||||
public @NonNull List<Integer> getSubDevices(@NonNull String addressName) {
|
||||
SQLiteDatabase database = databaseHelper.getSignalReadableDatabase();
|
||||
List<Integer> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.thoughtcrime.securesms.database;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user