2018-01-24 19:17:44 -08:00
package org.thoughtcrime.securesms.database.helpers ;
2018-03-18 15:06:51 -07:00
import android.content.ContentValues ;
2018-01-24 19:17:44 -08:00
import android.content.Context ;
2018-03-18 15:06:51 -07:00
import android.database.Cursor ;
2018-08-16 09:47:43 -07:00
import android.net.Uri ;
2018-04-06 18:15:24 -07:00
import android.os.SystemClock ;
2020-08-19 10:06:26 +10:00
import androidx.annotation.NonNull ;
2018-06-21 16:48:46 -07:00
import android.text.TextUtils ;
2018-08-16 09:47:43 -07:00
2018-01-24 19:17:44 -08:00
import net.sqlcipher.database.SQLiteDatabase ;
import net.sqlcipher.database.SQLiteDatabaseHook ;
import net.sqlcipher.database.SQLiteOpenHelper ;
2018-02-15 20:33:10 -08:00
import org.thoughtcrime.securesms.ApplicationContext ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.crypto.DatabaseSecret ;
import org.thoughtcrime.securesms.crypto.MasterSecret ;
2019-06-18 09:57:36 +10:00
import org.thoughtcrime.securesms.database.Address ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.database.AttachmentDatabase ;
import org.thoughtcrime.securesms.database.DraftDatabase ;
import org.thoughtcrime.securesms.database.GroupDatabase ;
import org.thoughtcrime.securesms.database.GroupReceiptDatabase ;
import org.thoughtcrime.securesms.database.IdentityDatabase ;
2019-06-18 09:57:36 +10:00
import org.thoughtcrime.securesms.database.JobDatabase ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.database.MmsDatabase ;
2018-02-15 20:33:10 -08:00
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.database.PushDatabase ;
import org.thoughtcrime.securesms.database.RecipientDatabase ;
2018-04-06 18:15:24 -07:00
import org.thoughtcrime.securesms.database.SearchDatabase ;
2018-02-18 16:43:18 -08:00
import org.thoughtcrime.securesms.database.SessionDatabase ;
2018-02-15 20:33:10 -08:00
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.database.SmsDatabase ;
2019-04-17 10:21:30 -04:00
import org.thoughtcrime.securesms.database.StickerDatabase ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.database.ThreadDatabase ;
2018-02-15 20:33:10 -08:00
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob ;
2019-06-18 09:57:36 +10:00
import org.thoughtcrime.securesms.logging.Log ;
2020-05-11 16:19:26 +10:00
import org.thoughtcrime.securesms.loki.database.LokiAPIDatabase ;
2020-05-22 10:41:31 +10:00
import org.thoughtcrime.securesms.loki.database.LokiMessageDatabase ;
2020-05-11 16:19:26 +10:00
import org.thoughtcrime.securesms.loki.database.LokiPreKeyBundleDatabase ;
import org.thoughtcrime.securesms.loki.database.LokiPreKeyRecordDatabase ;
2020-05-22 10:41:31 +10:00
import org.thoughtcrime.securesms.loki.database.LokiThreadDatabase ;
2020-05-11 16:19:26 +10:00
import org.thoughtcrime.securesms.loki.database.LokiUserDatabase ;
2020-08-06 16:32:14 +10:00
import org.thoughtcrime.securesms.loki.database.SharedSenderKeysDatabase ;
2018-08-16 09:47:43 -07:00
import org.thoughtcrime.securesms.notifications.NotificationChannels ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.service.KeyCachingService ;
2019-12-12 10:07:17 +11:00
import org.thoughtcrime.securesms.util.GroupUtil ;
2018-01-24 19:17:44 -08:00
import org.thoughtcrime.securesms.util.TextSecurePreferences ;
2020-07-15 12:24:43 +10:00
import org.whispersystems.signalservice.loki.api.opengroups.PublicChat ;
2018-01-24 19:17:44 -08:00
2018-03-18 15:06:51 -07:00
import java.io.File ;
2018-01-24 19:17:44 -08:00
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
2018-02-16 11:10:35 -08:00
@SuppressWarnings ( " unused " )
2018-01-24 19:17:44 -08:00
private static final String TAG = SQLCipherOpenHelper . class . getSimpleName ( ) ;
2018-03-18 15:06:51 -07:00
private static final int RECIPIENT_CALL_RINGTONE_VERSION = 2 ;
private static final int MIGRATE_PREKEYS_VERSION = 3 ;
private static final int MIGRATE_SESSIONS_VERSION = 4 ;
private static final int NO_MORE_IMAGE_THUMBNAILS_VERSION = 5 ;
2018-03-18 16:04:33 -07:00
private static final int ATTACHMENT_DIMENSIONS = 6 ;
2018-04-02 16:17:32 -07:00
private static final int QUOTED_REPLIES = 7 ;
2018-04-26 17:03:54 -07:00
private static final int SHARED_CONTACTS = 8 ;
2018-04-06 18:15:24 -07:00
private static final int FULL_TEXT_SEARCH = 9 ;
2018-06-21 16:48:46 -07:00
private static final int BAD_IMPORT_CLEANUP = 10 ;
2018-08-11 09:55:52 -04:00
private static final int QUOTE_MISSING = 11 ;
2018-08-16 09:47:43 -07:00
private static final int NOTIFICATION_CHANNELS = 12 ;
2018-05-22 02:13:10 -07:00
private static final int SECRET_SENDER = 13 ;
2018-11-08 23:33:37 -08:00
private static final int ATTACHMENT_CAPTIONS = 14 ;
2018-11-26 11:50:31 -08:00
private static final int ATTACHMENT_CAPTIONS_FIX = 15 ;
2019-01-15 00:41:05 -08:00
private static final int PREVIEWS = 16 ;
2019-02-01 09:06:59 -08:00
private static final int CONVERSATION_SEARCH = 17 ;
2019-01-13 23:30:54 -08:00
private static final int SELF_ATTACHMENT_CLEANUP = 18 ;
2019-04-12 16:22:38 -03:00
private static final int RECIPIENT_FORCE_SMS_SELECTION = 19 ;
2019-03-28 08:56:35 -07:00
private static final int JOBMANAGER_STRIKES_BACK = 20 ;
2019-04-17 10:21:30 -04:00
private static final int STICKERS = 21 ;
2019-08-26 15:58:31 +10:00
private static final int lokiV1 = 22 ;
2019-09-09 12:53:31 +10:00
private static final int lokiV2 = 23 ;
2019-09-19 12:52:37 +10:00
private static final int lokiV3 = 24 ;
2019-10-08 13:23:03 +11:00
private static final int lokiV4 = 25 ;
2019-12-12 10:07:17 +11:00
private static final int lokiV5 = 26 ;
2020-01-31 15:29:31 +11:00
private static final int lokiV6 = 27 ;
2020-03-05 14:07:42 +11:00
private static final int lokiV7 = 28 ;
2020-05-22 10:41:31 +10:00
private static final int lokiV8 = 29 ;
2020-05-29 12:01:43 +10:00
private static final int lokiV9 = 30 ;
2020-07-16 09:44:00 +10:00
private static final int lokiV10 = 31 ;
2020-07-29 09:04:24 +10:00
private static final int lokiV11 = 32 ;
2020-08-06 16:32:14 +10:00
private static final int lokiV12 = 33 ;
2018-02-16 11:10:35 -08:00
2020-08-06 16:32:14 +10:00
private static final int DATABASE_VERSION = lokiV12 ; // Loki - onUpgrade(...) must be updated to use Loki version numbers if Signal makes any database changes
2018-01-24 19:17:44 -08:00
private static final String DATABASE_NAME = " signal.db " ;
private final Context context ;
private final DatabaseSecret databaseSecret ;
public SQLCipherOpenHelper ( @NonNull Context context , @NonNull DatabaseSecret databaseSecret ) {
super ( context , DATABASE_NAME , null , DATABASE_VERSION , new SQLiteDatabaseHook ( ) {
@Override
public void preKey ( SQLiteDatabase db ) {
db . rawExecSQL ( " PRAGMA cipher_default_kdf_iter = 1; " ) ;
db . rawExecSQL ( " PRAGMA cipher_default_page_size = 4096; " ) ;
}
@Override
public void postKey ( SQLiteDatabase db ) {
db . rawExecSQL ( " PRAGMA kdf_iter = '1'; " ) ;
db . rawExecSQL ( " PRAGMA cipher_page_size = 4096; " ) ;
}
} ) ;
this . context = context . getApplicationContext ( ) ;
this . databaseSecret = databaseSecret ;
}
@Override
public void onCreate ( SQLiteDatabase db ) {
db . execSQL ( SmsDatabase . CREATE_TABLE ) ;
db . execSQL ( MmsDatabase . CREATE_TABLE ) ;
db . execSQL ( AttachmentDatabase . CREATE_TABLE ) ;
db . execSQL ( ThreadDatabase . CREATE_TABLE ) ;
db . execSQL ( IdentityDatabase . CREATE_TABLE ) ;
db . execSQL ( DraftDatabase . CREATE_TABLE ) ;
db . execSQL ( PushDatabase . CREATE_TABLE ) ;
db . execSQL ( GroupDatabase . CREATE_TABLE ) ;
db . execSQL ( RecipientDatabase . CREATE_TABLE ) ;
db . execSQL ( GroupReceiptDatabase . CREATE_TABLE ) ;
2018-02-15 20:33:10 -08:00
db . execSQL ( OneTimePreKeyDatabase . CREATE_TABLE ) ;
db . execSQL ( SignedPreKeyDatabase . CREATE_TABLE ) ;
2018-02-18 16:43:18 -08:00
db . execSQL ( SessionDatabase . CREATE_TABLE ) ;
2018-04-06 18:15:24 -07:00
for ( String sql : SearchDatabase . CREATE_TABLE ) {
db . execSQL ( sql ) ;
}
2019-03-28 08:56:35 -07:00
for ( String sql : JobDatabase . CREATE_TABLE ) {
db . execSQL ( sql ) ;
}
2019-04-17 10:21:30 -04:00
db . execSQL ( StickerDatabase . CREATE_TABLE ) ;
2019-08-07 16:48:54 +10:00
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateSnodePoolTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateOnionRequestPathTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateSwarmTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateLastMessageHashValueTable2Command ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateReceivedMessageHashValuesTable2Command ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateOpenGroupAuthTokenTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateLastMessageServerIDTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateLastDeletionServerIDTableCommand ( ) ) ;
2020-06-01 10:33:47 +10:00
db . execSQL ( LokiAPIDatabase . getCreateDeviceLinkCacheCommand ( ) ) ;
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateUserCountTableCommand ( ) ) ;
2020-06-01 10:33:47 +10:00
db . execSQL ( LokiAPIDatabase . getCreateSessionRequestTimestampCacheCommand ( ) ) ;
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateSessionRequestSentTimestampTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateSessionRequestProcessedTimestampTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateOpenGroupPublicKeyTableCommand ( ) ) ;
2019-06-04 15:00:27 +10:00
db . execSQL ( LokiPreKeyBundleDatabase . getCreateTableCommand ( ) ) ;
2019-06-24 14:10:09 +10:00
db . execSQL ( LokiPreKeyRecordDatabase . getCreateTableCommand ( ) ) ;
2020-07-15 14:26:20 +10:00
db . execSQL ( LokiMessageDatabase . getCreateMessageIDTableCommand ( ) ) ;
2019-10-08 13:23:03 +11:00
db . execSQL ( LokiMessageDatabase . getCreateMessageToThreadMappingTableCommand ( ) ) ;
2020-03-05 14:07:42 +11:00
db . execSQL ( LokiMessageDatabase . getCreateErrorMessageTableCommand ( ) ) ;
2019-07-22 09:38:12 +10:00
db . execSQL ( LokiThreadDatabase . getCreateSessionResetTableCommand ( ) ) ;
2019-10-15 16:06:38 +11:00
db . execSQL ( LokiThreadDatabase . getCreatePublicChatTableCommand ( ) ) ;
2019-09-09 12:53:31 +10:00
db . execSQL ( LokiUserDatabase . getCreateDisplayNameTableCommand ( ) ) ;
db . execSQL ( LokiUserDatabase . getCreateServerDisplayNameTableCommand ( ) ) ;
2020-08-10 11:40:43 +10:00
db . execSQL ( SharedSenderKeysDatabase . getCreateClosedGroupRatchetTableCommand ( ) ) ;
db . execSQL ( SharedSenderKeysDatabase . getCreateClosedGroupPrivateKeyTableCommand ( ) ) ;
2018-01-24 19:17:44 -08:00
executeStatements ( db , SmsDatabase . CREATE_INDEXS ) ;
executeStatements ( db , MmsDatabase . CREATE_INDEXS ) ;
executeStatements ( db , AttachmentDatabase . CREATE_INDEXS ) ;
executeStatements ( db , ThreadDatabase . CREATE_INDEXS ) ;
executeStatements ( db , DraftDatabase . CREATE_INDEXS ) ;
executeStatements ( db , GroupDatabase . CREATE_INDEXS ) ;
executeStatements ( db , GroupReceiptDatabase . CREATE_INDEXES ) ;
2019-04-17 10:21:30 -04:00
executeStatements ( db , StickerDatabase . CREATE_INDEXES ) ;
2018-01-24 19:17:44 -08:00
if ( context . getDatabasePath ( ClassicOpenHelper . NAME ) . exists ( ) ) {
ClassicOpenHelper legacyHelper = new ClassicOpenHelper ( context ) ;
android . database . sqlite . SQLiteDatabase legacyDb = legacyHelper . getWritableDatabase ( ) ;
2018-02-01 16:01:24 -08:00
SQLCipherMigrationHelper . migratePlaintext ( context , legacyDb , db ) ;
2018-01-24 19:17:44 -08:00
MasterSecret masterSecret = KeyCachingService . getMasterSecret ( context ) ;
2018-02-01 16:01:24 -08:00
if ( masterSecret ! = null ) SQLCipherMigrationHelper . migrateCiphertext ( context , masterSecret , legacyDb , db , null ) ;
2018-01-24 19:17:44 -08:00
else TextSecurePreferences . setNeedsSqlCipherMigration ( context , true ) ;
2018-02-15 20:33:10 -08:00
if ( ! PreKeyMigrationHelper . migratePreKeys ( context , db ) ) {
2019-03-28 08:56:35 -07:00
ApplicationContext . getInstance ( context ) . getJobManager ( ) . add ( new RefreshPreKeysJob ( ) ) ;
2018-02-15 20:33:10 -08:00
}
2018-02-18 16:43:18 -08:00
SessionStoreMigrationHelper . migrateSessions ( context , db ) ;
2018-02-15 20:33:10 -08:00
PreKeyMigrationHelper . cleanUpPreKeys ( context ) ;
2018-01-24 19:17:44 -08:00
}
}
@Override
2019-12-04 11:28:12 +11:00
public void onConfigure ( SQLiteDatabase db ) {
super . onConfigure ( db ) ;
2020-05-11 16:54:31 +10:00
// Loki - Enable write ahead logging mode and increase the cache size.
// This should be disabled if we ever run into serious race condition bugs.
2019-12-04 11:28:12 +11:00
db . enableWriteAheadLogging ( ) ;
db . execSQL ( " PRAGMA cache_size = 10000 " ) ;
}
2018-01-24 19:17:44 -08:00
@Override
public void onUpgrade ( SQLiteDatabase db , int oldVersion , int newVersion ) {
2018-08-02 09:25:33 -04:00
Log . i ( TAG , " Upgrading database: " + oldVersion + " , " + newVersion ) ;
2018-01-24 19:17:44 -08:00
2018-02-15 20:33:10 -08:00
db . beginTransaction ( ) ;
try {
if ( oldVersion < RECIPIENT_CALL_RINGTONE_VERSION ) {
db . execSQL ( " ALTER TABLE recipient_preferences ADD COLUMN call_ringtone TEXT DEFAULT NULL " ) ;
db . execSQL ( " ALTER TABLE recipient_preferences ADD COLUMN call_vibrate INTEGER DEFAULT " + RecipientDatabase . VibrateState . DEFAULT . getId ( ) ) ;
}
if ( oldVersion < MIGRATE_PREKEYS_VERSION ) {
db . execSQL ( " CREATE TABLE signed_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL, signature TEXT NOT NULL, timestamp INTEGER DEFAULT 0) " ) ;
db . execSQL ( " CREATE TABLE one_time_prekeys (_id INTEGER PRIMARY KEY, key_id INTEGER UNIQUE, public_key TEXT NOT NULL, private_key TEXT NOT NULL) " ) ;
if ( ! PreKeyMigrationHelper . migratePreKeys ( context , db ) ) {
2019-03-28 08:56:35 -07:00
ApplicationContext . getInstance ( context ) . getJobManager ( ) . add ( new RefreshPreKeysJob ( ) ) ;
2018-02-15 20:33:10 -08:00
}
2018-02-18 16:43:18 -08:00
}
2018-02-15 20:33:10 -08:00
2018-02-18 16:43:18 -08:00
if ( oldVersion < MIGRATE_SESSIONS_VERSION ) {
db . execSQL ( " CREATE TABLE sessions (_id INTEGER PRIMARY KEY, address TEXT NOT NULL, device INTEGER NOT NULL, record BLOB NOT NULL, UNIQUE(address, device) ON CONFLICT REPLACE) " ) ;
SessionStoreMigrationHelper . migrateSessions ( context , db ) ;
2018-02-15 20:33:10 -08:00
}
2018-03-18 15:06:51 -07:00
if ( oldVersion < NO_MORE_IMAGE_THUMBNAILS_VERSION ) {
ContentValues update = new ContentValues ( ) ;
update . put ( " thumbnail " , ( String ) null ) ;
update . put ( " aspect_ratio " , ( String ) null ) ;
update . put ( " thumbnail_random " , ( String ) null ) ;
try ( Cursor cursor = db . query ( " part " , new String [ ] { " _id " , " ct " , " thumbnail " } , " thumbnail IS NOT NULL " , null , null , null , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
long id = cursor . getLong ( cursor . getColumnIndexOrThrow ( " _id " ) ) ;
String contentType = cursor . getString ( cursor . getColumnIndexOrThrow ( " ct " ) ) ;
if ( contentType ! = null & & ! contentType . startsWith ( " video " ) ) {
String thumbnailPath = cursor . getString ( cursor . getColumnIndexOrThrow ( " thumbnail " ) ) ;
File thumbnailFile = new File ( thumbnailPath ) ;
thumbnailFile . delete ( ) ;
db . update ( " part " , update , " _id = ? " , new String [ ] { String . valueOf ( id ) } ) ;
}
}
}
}
2018-03-18 16:04:33 -07:00
if ( oldVersion < ATTACHMENT_DIMENSIONS ) {
db . execSQL ( " ALTER TABLE part ADD COLUMN width INTEGER DEFAULT 0 " ) ;
db . execSQL ( " ALTER TABLE part ADD COLUMN height INTEGER DEFAULT 0 " ) ;
}
2018-04-02 16:17:32 -07:00
if ( oldVersion < QUOTED_REPLIES ) {
db . execSQL ( " ALTER TABLE mms ADD COLUMN quote_id INTEGER DEFAULT 0 " ) ;
db . execSQL ( " ALTER TABLE mms ADD COLUMN quote_author TEXT " ) ;
db . execSQL ( " ALTER TABLE mms ADD COLUMN quote_body TEXT " ) ;
db . execSQL ( " ALTER TABLE mms ADD COLUMN quote_attachment INTEGER DEFAULT -1 " ) ;
db . execSQL ( " ALTER TABLE part ADD COLUMN quote INTEGER DEFAULT 0 " ) ;
}
2018-04-26 17:03:54 -07:00
if ( oldVersion < SHARED_CONTACTS ) {
db . execSQL ( " ALTER TABLE mms ADD COLUMN shared_contacts TEXT " ) ;
}
2018-04-06 18:15:24 -07:00
if ( oldVersion < FULL_TEXT_SEARCH ) {
2019-02-01 09:06:59 -08:00
db . execSQL ( " CREATE VIRTUAL TABLE sms_fts USING fts5(body, content=sms, content_rowid=_id) " ) ;
db . execSQL ( " CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN \ n " +
" INSERT INTO sms_fts(rowid, body) VALUES (new._id, new.body); \ n " +
" END; " ) ;
db . execSQL ( " CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN \ n " +
" INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body); \ n " +
" END; \ n " ) ;
db . execSQL ( " CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN \ n " +
" INSERT INTO sms_fts(sms_fts, rowid, body) VALUES('delete', old._id, old.body); \ n " +
" INSERT INTO sms_fts(rowid, body) VALUES(new._id, new.body); \ n " +
" END; " ) ;
db . execSQL ( " CREATE VIRTUAL TABLE mms_fts USING fts5(body, content=mms, content_rowid=_id) " ) ;
db . execSQL ( " CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN \ n " +
" INSERT INTO mms_fts(rowid, body) VALUES (new._id, new.body); \ n " +
" END; " ) ;
db . execSQL ( " CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN \ n " +
" INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body); \ n " +
" END; \ n " ) ;
db . execSQL ( " CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN \ n " +
" INSERT INTO mms_fts(mms_fts, rowid, body) VALUES('delete', old._id, old.body); \ n " +
" INSERT INTO mms_fts(rowid, body) VALUES(new._id, new.body); \ n " +
" END; " ) ;
2018-04-06 18:15:24 -07:00
Log . i ( TAG , " Beginning to build search index. " ) ;
long start = SystemClock . elapsedRealtime ( ) ;
2018-07-02 18:10:11 -07:00
db . execSQL ( " INSERT INTO sms_fts (rowid, body) SELECT _id, body FROM sms " ) ;
2018-04-06 18:15:24 -07:00
long smsFinished = SystemClock . elapsedRealtime ( ) ;
Log . i ( TAG , " Indexing SMS completed in " + ( smsFinished - start ) + " ms " ) ;
2018-07-02 18:10:11 -07:00
db . execSQL ( " INSERT INTO mms_fts (rowid, body) SELECT _id, body FROM mms " ) ;
2018-04-06 18:15:24 -07:00
long mmsFinished = SystemClock . elapsedRealtime ( ) ;
Log . i ( TAG , " Indexing MMS completed in " + ( mmsFinished - smsFinished ) + " ms " ) ;
Log . i ( TAG , " Indexing finished. Total time: " + ( mmsFinished - start ) + " ms " ) ;
}
2018-06-21 16:48:46 -07:00
if ( oldVersion < BAD_IMPORT_CLEANUP ) {
String trimmedCondition = " NOT IN (SELECT _id FROM mms) " ;
db . delete ( " group_receipts " , " mms_id " + trimmedCondition , null ) ;
String [ ] columns = new String [ ] { " _id " , " unique_id " , " _data " , " thumbnail " } ;
try ( Cursor cursor = db . query ( " part " , columns , " mid " + trimmedCondition , null , null , null , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
db . delete ( " part " , " _id = ? AND unique_id = ? " , new String [ ] { String . valueOf ( cursor . getLong ( 0 ) ) , String . valueOf ( cursor . getLong ( 1 ) ) } ) ;
String data = cursor . getString ( 2 ) ;
String thumbnail = cursor . getString ( 3 ) ;
if ( ! TextUtils . isEmpty ( data ) ) {
new File ( data ) . delete ( ) ;
}
if ( ! TextUtils . isEmpty ( thumbnail ) ) {
new File ( thumbnail ) . delete ( ) ;
}
}
}
}
2018-09-07 16:08:45 -07:00
// Note: This column only being checked due to upgrade issues as described in #8184
if ( oldVersion < QUOTE_MISSING & & ! columnExists ( db , " mms " , " quote_missing " ) ) {
2018-08-11 09:55:52 -04:00
db . execSQL ( " ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0 " ) ;
}
2018-09-07 16:08:45 -07:00
// Note: The column only being checked due to upgrade issues as described in #8184
if ( oldVersion < NOTIFICATION_CHANNELS & & ! columnExists ( db , " recipient_preferences " , " notification_channel " ) ) {
2018-08-16 09:47:43 -07:00
db . execSQL ( " ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL " ) ;
2018-08-22 13:19:59 -07:00
NotificationChannels . create ( context ) ;
2018-08-16 09:47:43 -07:00
try ( Cursor cursor = db . rawQuery ( " SELECT recipient_ids, system_display_name, signal_profile_name, notification, vibrate FROM recipient_preferences WHERE notification NOT NULL OR vibrate != 0 " , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
String addressString = cursor . getString ( cursor . getColumnIndexOrThrow ( " recipient_ids " ) ) ;
Address address = Address . fromExternal ( context , addressString ) ;
String systemName = cursor . getString ( cursor . getColumnIndexOrThrow ( " system_display_name " ) ) ;
String profileName = cursor . getString ( cursor . getColumnIndexOrThrow ( " signal_profile_name " ) ) ;
String messageSound = cursor . getString ( cursor . getColumnIndexOrThrow ( " notification " ) ) ;
Uri messageSoundUri = messageSound ! = null ? Uri . parse ( messageSound ) : null ;
int vibrateState = cursor . getInt ( cursor . getColumnIndexOrThrow ( " vibrate " ) ) ;
2018-08-22 13:19:59 -07:00
String displayName = NotificationChannels . getChannelDisplayNameFor ( context , systemName , profileName , address ) ;
2018-08-16 09:47:43 -07:00
boolean vibrateEnabled = vibrateState = = 0 ? TextSecurePreferences . isNotificationVibrateEnabled ( context ) : vibrateState = = 1 ;
2018-08-22 14:19:37 -07:00
if ( address . isGroup ( ) ) {
try ( Cursor groupCursor = db . rawQuery ( " SELECT title FROM groups WHERE group_id = ? " , new String [ ] { address . toGroupString ( ) } ) ) {
if ( groupCursor ! = null & & groupCursor . moveToFirst ( ) ) {
String title = groupCursor . getString ( groupCursor . getColumnIndexOrThrow ( " title " ) ) ;
if ( ! TextUtils . isEmpty ( title ) ) {
displayName = title ;
}
}
}
}
2018-08-16 09:47:43 -07:00
String channelId = NotificationChannels . createChannelFor ( context , address , displayName , messageSoundUri , vibrateEnabled ) ;
ContentValues values = new ContentValues ( 1 ) ;
values . put ( " notification_channel " , channelId ) ;
db . update ( " recipient_preferences " , values , " recipient_ids = ? " , new String [ ] { addressString } ) ;
}
}
}
2018-05-22 02:13:10 -07:00
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 " ) ;
2018-10-11 16:45:22 -07:00
db . execSQL ( " ALTER TABLE group_receipts ADD COLUMN unidentified INTEGER DEFAULT 0 " ) ;
db . execSQL ( " ALTER TABLE mms ADD COLUMN unidentified INTEGER DEFAULT 0 " ) ;
db . execSQL ( " ALTER TABLE sms ADD COLUMN unidentified INTEGER DEFAULT 0 " ) ;
2018-05-22 02:13:10 -07:00
}
2018-11-08 23:33:37 -08:00
if ( oldVersion < ATTACHMENT_CAPTIONS ) {
db . execSQL ( " ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL " ) ;
}
2018-11-26 11:50:31 -08:00
// 4.30.8 included a migration, but not a correct CREATE_TABLE statement, so we need to add
// this column if it isn't present.
if ( oldVersion < ATTACHMENT_CAPTIONS_FIX ) {
if ( ! columnExists ( db , " part " , " caption " ) ) {
db . execSQL ( " ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL " ) ;
}
}
2019-01-15 00:41:05 -08:00
if ( oldVersion < PREVIEWS ) {
db . execSQL ( " ALTER TABLE mms ADD COLUMN previews TEXT " ) ;
}
2019-02-01 09:06:59 -08:00
if ( oldVersion < CONVERSATION_SEARCH ) {
db . execSQL ( " DROP TABLE sms_fts " ) ;
db . execSQL ( " DROP TABLE mms_fts " ) ;
db . execSQL ( " DROP TRIGGER sms_ai " ) ;
db . execSQL ( " DROP TRIGGER sms_au " ) ;
db . execSQL ( " DROP TRIGGER sms_ad " ) ;
db . execSQL ( " DROP TRIGGER mms_ai " ) ;
db . execSQL ( " DROP TRIGGER mms_au " ) ;
db . execSQL ( " DROP TRIGGER mms_ad " ) ;
db . execSQL ( " CREATE VIRTUAL TABLE sms_fts USING fts5(body, thread_id UNINDEXED, content=sms, content_rowid=_id) " ) ;
db . execSQL ( " CREATE TRIGGER sms_ai AFTER INSERT ON sms BEGIN \ n " +
" INSERT INTO sms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id); \ n " +
" END; " ) ;
db . execSQL ( " CREATE TRIGGER sms_ad AFTER DELETE ON sms BEGIN \ n " +
" INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); \ n " +
" END; \ n " ) ;
db . execSQL ( " CREATE TRIGGER sms_au AFTER UPDATE ON sms BEGIN \ n " +
" INSERT INTO sms_fts(sms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); \ n " +
" INSERT INTO sms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id); \ n " +
" END; " ) ;
db . execSQL ( " CREATE VIRTUAL TABLE mms_fts USING fts5(body, thread_id UNINDEXED, content=mms, content_rowid=_id) " ) ;
db . execSQL ( " CREATE TRIGGER mms_ai AFTER INSERT ON mms BEGIN \ n " +
" INSERT INTO mms_fts(rowid, body, thread_id) VALUES (new._id, new.body, new.thread_id); \ n " +
" END; " ) ;
db . execSQL ( " CREATE TRIGGER mms_ad AFTER DELETE ON mms BEGIN \ n " +
" INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); \ n " +
" END; \ n " ) ;
db . execSQL ( " CREATE TRIGGER mms_au AFTER UPDATE ON mms BEGIN \ n " +
" INSERT INTO mms_fts(mms_fts, rowid, body, thread_id) VALUES('delete', old._id, old.body, old.thread_id); \ n " +
" INSERT INTO mms_fts(rowid, body, thread_id) VALUES(new._id, new.body, new.thread_id); \ n " +
" END; " ) ;
Log . i ( TAG , " Beginning to build search index. " ) ;
long start = SystemClock . elapsedRealtime ( ) ;
db . execSQL ( " INSERT INTO sms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM sms " ) ;
long smsFinished = SystemClock . elapsedRealtime ( ) ;
Log . i ( TAG , " Indexing SMS completed in " + ( smsFinished - start ) + " ms " ) ;
db . execSQL ( " INSERT INTO mms_fts (rowid, body, thread_id) SELECT _id, body, thread_id FROM mms " ) ;
long mmsFinished = SystemClock . elapsedRealtime ( ) ;
Log . i ( TAG , " Indexing MMS completed in " + ( mmsFinished - smsFinished ) + " ms " ) ;
Log . i ( TAG , " Indexing finished. Total time: " + ( mmsFinished - start ) + " ms " ) ;
}
2019-01-13 23:30:54 -08:00
if ( oldVersion < SELF_ATTACHMENT_CLEANUP ) {
String localNumber = TextSecurePreferences . getLocalNumber ( context ) ;
2019-02-15 19:25:20 -08:00
if ( ! TextUtils . isEmpty ( localNumber ) ) {
try ( Cursor threadCursor = db . rawQuery ( " SELECT _id FROM thread WHERE recipient_ids = ? " , new String [ ] { localNumber } ) ) {
if ( threadCursor ! = null & & threadCursor . moveToFirst ( ) ) {
long threadId = threadCursor . getLong ( 0 ) ;
ContentValues updateValues = new ContentValues ( 1 ) ;
2019-01-13 23:30:54 -08:00
2019-02-15 19:25:20 -08:00
updateValues . put ( " pending_push " , 0 ) ;
2019-01-13 23:30:54 -08:00
2019-02-15 19:25:20 -08:00
int count = db . update ( " part " , updateValues , " mid IN (SELECT _id FROM mms WHERE thread_id = ?) " , new String [ ] { String . valueOf ( threadId ) } ) ;
Log . i ( TAG , " Updated " + count + " self-sent attachments. " ) ;
}
2019-01-13 23:30:54 -08:00
}
}
}
2019-04-12 16:22:38 -03:00
if ( oldVersion < RECIPIENT_FORCE_SMS_SELECTION ) {
db . execSQL ( " ALTER TABLE recipient_preferences ADD COLUMN force_sms_selection INTEGER DEFAULT 0 " ) ;
}
2019-03-28 08:56:35 -07:00
if ( oldVersion < JOBMANAGER_STRIKES_BACK ) {
db . execSQL ( " CREATE TABLE job_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" job_spec_id TEXT UNIQUE, " +
" factory_key TEXT, " +
" queue_key TEXT, " +
" create_time INTEGER, " +
" next_run_attempt_time INTEGER, " +
" run_attempt INTEGER, " +
" max_attempts INTEGER, " +
" max_backoff INTEGER, " +
" max_instances INTEGER, " +
" lifespan INTEGER, " +
" serialized_data TEXT, " +
" is_running INTEGER) " ) ;
db . execSQL ( " CREATE TABLE constraint_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" job_spec_id TEXT, " +
" factory_key TEXT, " +
" UNIQUE(job_spec_id, factory_key)) " ) ;
db . execSQL ( " CREATE TABLE dependency_spec(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" job_spec_id TEXT, " +
" depends_on_job_spec_id TEXT, " +
" UNIQUE(job_spec_id, depends_on_job_spec_id)) " ) ;
}
2019-04-17 10:21:30 -04:00
if ( oldVersion < STICKERS ) {
db . execSQL ( " CREATE TABLE sticker (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
" pack_id TEXT NOT NULL, " +
" pack_key TEXT NOT NULL, " +
" pack_title TEXT NOT NULL, " +
" pack_author TEXT NOT NULL, " +
" sticker_id INTEGER, " +
" cover INTEGER, " +
" emoji TEXT NOT NULL, " +
" last_used INTEGER, " +
" installed INTEGER, " +
" file_path TEXT NOT NULL, " +
" file_length INTEGER, " +
" file_random BLOB, " +
" UNIQUE(pack_id, sticker_id, cover) ON CONFLICT IGNORE) " ) ;
db . execSQL ( " CREATE INDEX IF NOT EXISTS sticker_pack_id_index ON sticker (pack_id); " ) ;
db . execSQL ( " CREATE INDEX IF NOT EXISTS sticker_sticker_id_index ON sticker (sticker_id); " ) ;
db . execSQL ( " ALTER TABLE part ADD COLUMN sticker_pack_id TEXT " ) ;
db . execSQL ( " ALTER TABLE part ADD COLUMN sticker_pack_key TEXT " ) ;
db . execSQL ( " ALTER TABLE part ADD COLUMN sticker_id INTEGER DEFAULT -1 " ) ;
db . execSQL ( " CREATE INDEX IF NOT EXISTS part_sticker_pack_id_index ON part (sticker_pack_id) " ) ;
}
2019-08-26 15:58:31 +10:00
if ( oldVersion < lokiV1 ) {
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateOpenGroupAuthTokenTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateLastMessageServerIDTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateLastDeletionServerIDTableCommand ( ) ) ;
2019-08-26 15:58:31 +10:00
}
2019-09-09 12:53:31 +10:00
if ( oldVersion < lokiV2 ) {
db . execSQL ( LokiUserDatabase . getCreateServerDisplayNameTableCommand ( ) ) ;
}
2019-09-19 12:52:37 +10:00
if ( oldVersion < lokiV3 ) {
2020-06-01 10:33:47 +10:00
db . execSQL ( LokiAPIDatabase . getCreateDeviceLinkCacheCommand ( ) ) ;
2019-10-15 16:06:38 +11:00
db . execSQL ( LokiThreadDatabase . getCreatePublicChatTableCommand ( ) ) ;
2019-10-17 10:39:59 +11:00
db . execSQL ( " ALTER TABLE groups ADD COLUMN avatar_url TEXT " ) ;
db . execSQL ( " ALTER TABLE part ADD COLUMN url TEXT " ) ;
2019-09-19 12:52:37 +10:00
}
2019-10-08 13:23:03 +11:00
if ( oldVersion < lokiV4 ) {
db . execSQL ( LokiMessageDatabase . getCreateMessageToThreadMappingTableCommand ( ) ) ;
}
2019-12-12 10:07:17 +11:00
if ( oldVersion < lokiV5 ) {
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateUserCountTableCommand ( ) ) ;
2020-01-22 10:46:04 +11:00
}
2020-01-31 15:29:31 +11:00
if ( oldVersion < lokiV6 ) {
2019-12-12 10:07:17 +11:00
// Migrate public chats from __textsecure_group__ to __loki_public_chat_group__
try ( Cursor lokiPublicChatCursor = db . rawQuery ( " SELECT public_chat FROM loki_public_chat_database " , null ) ) {
while ( lokiPublicChatCursor ! = null & & lokiPublicChatCursor . moveToNext ( ) ) {
String chatString = lokiPublicChatCursor . getString ( 0 ) ;
2020-07-15 12:24:43 +10:00
PublicChat publicChat = PublicChat . fromJSON ( chatString ) ;
2019-12-12 10:07:17 +11:00
if ( publicChat ! = null ) {
byte [ ] groupId = publicChat . getId ( ) . getBytes ( ) ;
String oldId = GroupUtil . getEncodedId ( groupId , false ) ;
2020-05-22 09:53:04 +10:00
String newId = GroupUtil . getEncodedOpenGroupId ( groupId ) ;
2019-12-12 10:07:17 +11:00
ContentValues threadUpdate = new ContentValues ( ) ;
threadUpdate . put ( " recipient_ids " , newId ) ;
db . update ( " thread " , threadUpdate , " recipient_ids = ? " , new String [ ] { oldId } ) ;
ContentValues groupUpdate = new ContentValues ( ) ;
groupUpdate . put ( " group_id " , newId ) ;
db . update ( " groups " , groupUpdate , " group_id = ? " , new String [ ] { oldId } ) ;
}
}
}
2020-05-11 16:54:31 +10:00
// Migrate RSS feeds from __textsecure_group__ to __loki_rss_feed_group__
2019-12-12 10:07:17 +11:00
String [ ] rssFeedIds = new String [ ] { " loki.network.feed " , " loki.network.messenger-updates.feed " } ;
for ( String groupId : rssFeedIds ) {
String oldId = GroupUtil . getEncodedId ( groupId . getBytes ( ) , false ) ;
String newId = GroupUtil . getEncodedRSSFeedId ( groupId . getBytes ( ) ) ;
ContentValues threadUpdate = new ContentValues ( ) ;
threadUpdate . put ( " recipient_ids " , newId ) ;
db . update ( " thread " , threadUpdate , " recipient_ids = ? " , new String [ ] { oldId } ) ;
ContentValues groupUpdate = new ContentValues ( ) ;
groupUpdate . put ( " group_id " , newId ) ;
db . update ( " groups " , groupUpdate , " group_id = ? " , new String [ ] { oldId } ) ;
}
2019-12-13 10:25:53 +11:00
// Add admin field in groups
db . execSQL ( " ALTER TABLE groups ADD COLUMN admins TEXT " ) ;
2019-12-12 10:07:17 +11:00
}
2020-03-05 14:07:42 +11:00
if ( oldVersion < lokiV7 ) {
db . execSQL ( LokiMessageDatabase . getCreateErrorMessageTableCommand ( ) ) ;
}
2020-05-22 10:41:31 +10:00
if ( oldVersion < lokiV8 ) {
2020-06-01 10:33:47 +10:00
db . execSQL ( LokiAPIDatabase . getCreateSessionRequestTimestampCacheCommand ( ) ) ;
2020-05-22 10:41:31 +10:00
}
2020-05-29 12:01:43 +10:00
if ( oldVersion < lokiV9 ) {
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateSnodePoolTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateOnionRequestPathTableCommand ( ) ) ;
2020-05-29 12:01:43 +10:00
}
2020-07-16 09:44:00 +10:00
if ( oldVersion < lokiV10 ) {
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateSessionRequestSentTimestampTableCommand ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateSessionRequestProcessedTimestampTableCommand ( ) ) ;
2020-07-16 09:44:00 +10:00
}
2020-07-29 09:04:24 +10:00
if ( oldVersion < lokiV11 ) {
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateOpenGroupPublicKeyTableCommand ( ) ) ;
2020-07-29 09:04:24 +10:00
}
2020-08-06 16:32:14 +10:00
if ( oldVersion < lokiV12 ) {
2020-08-10 11:40:43 +10:00
db . execSQL ( LokiAPIDatabase . getCreateLastMessageHashValueTable2Command ( ) ) ;
db . execSQL ( LokiAPIDatabase . getCreateReceivedMessageHashValuesTable2Command ( ) ) ;
db . execSQL ( SharedSenderKeysDatabase . getCreateClosedGroupRatchetTableCommand ( ) ) ;
db . execSQL ( SharedSenderKeysDatabase . getCreateClosedGroupPrivateKeyTableCommand ( ) ) ;
2020-08-06 16:32:14 +10:00
}
2018-02-15 20:33:10 -08:00
db . setTransactionSuccessful ( ) ;
} finally {
db . endTransaction ( ) ;
}
if ( oldVersion < MIGRATE_PREKEYS_VERSION ) {
PreKeyMigrationHelper . cleanUpPreKeys ( context ) ;
2018-02-16 11:10:35 -08:00
}
2018-01-24 19:17:44 -08:00
}
public SQLiteDatabase getReadableDatabase ( ) {
return getReadableDatabase ( databaseSecret . asString ( ) ) ;
}
public SQLiteDatabase getWritableDatabase ( ) {
return getWritableDatabase ( databaseSecret . asString ( ) ) ;
}
2018-04-02 06:27:50 -07:00
public void markCurrent ( SQLiteDatabase db ) {
db . setVersion ( DATABASE_VERSION ) ;
}
2018-01-24 19:17:44 -08:00
private void executeStatements ( SQLiteDatabase db , String [ ] statements ) {
for ( String statement : statements )
db . execSQL ( statement ) ;
}
2018-09-07 16:08:45 -07:00
private static boolean columnExists ( @NonNull SQLiteDatabase db , @NonNull String table , @NonNull String column ) {
try ( Cursor cursor = db . rawQuery ( " PRAGMA table_info( " + table + " ) " , null ) ) {
int nameColumnIndex = cursor . getColumnIndexOrThrow ( " name " ) ;
2018-01-24 19:17:44 -08:00
2018-09-07 16:08:45 -07:00
while ( cursor . moveToNext ( ) ) {
String name = cursor . getString ( nameColumnIndex ) ;
if ( name . equals ( column ) ) {
return true ;
}
}
}
return false ;
}
2018-01-24 19:17:44 -08:00
}