2018-01-25 04:17:44 +01:00
package org.thoughtcrime.securesms.database.helpers ;
2019-09-25 14:26:30 +02:00
import android.app.NotificationChannel ;
import android.app.NotificationManager ;
2018-03-18 23:06:51 +01:00
import android.content.ContentValues ;
2018-01-25 04:17:44 +01:00
import android.content.Context ;
2018-03-18 23:06:51 +01:00
import android.database.Cursor ;
2018-08-16 18:47:43 +02:00
import android.net.Uri ;
2019-09-25 14:26:30 +02:00
import android.os.Build ;
2018-04-07 03:15:24 +02:00
import android.os.SystemClock ;
2020-02-10 16:06:12 +01:00
import android.preference.PreferenceManager ;
2018-06-22 01:48:46 +02:00
import android.text.TextUtils ;
2018-08-16 18:47:43 +02:00
2019-12-20 21:12:22 +01:00
import androidx.annotation.NonNull ;
2019-09-25 14:26:30 +02:00
import com.annimon.stream.Stream ;
2019-11-08 17:29:37 +01:00
import com.bumptech.glide.Glide ;
2019-09-25 14:26:30 +02:00
2018-01-25 04:17:44 +01:00
import net.sqlcipher.database.SQLiteDatabase ;
import net.sqlcipher.database.SQLiteDatabaseHook ;
import net.sqlcipher.database.SQLiteOpenHelper ;
2020-03-26 20:38:27 +01:00
import org.thoughtcrime.securesms.profiles.AvatarHelper ;
2020-03-25 22:58:33 +01:00
import org.thoughtcrime.securesms.profiles.ProfileName ;
2020-03-26 20:38:27 +01:00
import org.thoughtcrime.securesms.recipients.RecipientId ;
2020-03-25 22:58:33 +01:00
import org.thoughtcrime.securesms.storage.StorageSyncHelper ;
2018-01-25 04:17:44 +01:00
import org.thoughtcrime.securesms.crypto.DatabaseSecret ;
import org.thoughtcrime.securesms.crypto.MasterSecret ;
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-04-17 16:21:30 +02:00
import org.thoughtcrime.securesms.database.JobDatabase ;
2020-01-10 07:08:39 +01:00
import org.thoughtcrime.securesms.database.KeyValueDatabase ;
2020-01-22 15:22:19 +01:00
import org.thoughtcrime.securesms.database.MegaphoneDatabase ;
2018-01-25 04:17:44 +01:00
import org.thoughtcrime.securesms.database.MmsDatabase ;
2018-02-16 05:33:10 +01:00
import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase ;
2018-01-25 04:17:44 +01:00
import org.thoughtcrime.securesms.database.PushDatabase ;
import org.thoughtcrime.securesms.database.RecipientDatabase ;
2018-04-07 03:15:24 +02:00
import org.thoughtcrime.securesms.database.SearchDatabase ;
2018-02-19 01:43:18 +01:00
import org.thoughtcrime.securesms.database.SessionDatabase ;
2018-02-16 05:33:10 +01:00
import org.thoughtcrime.securesms.database.SignedPreKeyDatabase ;
2018-01-25 04:17:44 +01:00
import org.thoughtcrime.securesms.database.SmsDatabase ;
2019-04-17 16:21:30 +02:00
import org.thoughtcrime.securesms.database.StickerDatabase ;
2019-09-26 16:12:51 +02:00
import org.thoughtcrime.securesms.database.StorageKeyDatabase ;
2018-01-25 04:17:44 +01:00
import org.thoughtcrime.securesms.database.ThreadDatabase ;
2019-10-15 21:47:54 +02:00
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies ;
2020-03-26 15:00:17 +01:00
import org.thoughtcrime.securesms.groups.GroupId ;
2018-02-16 05:33:10 +01:00
import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob ;
2019-04-17 16:21:30 +02:00
import org.thoughtcrime.securesms.logging.Log ;
2018-08-16 18:47:43 +02:00
import org.thoughtcrime.securesms.notifications.NotificationChannels ;
2019-08-07 20:22:51 +02:00
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter ;
2018-01-25 04:17:44 +01:00
import org.thoughtcrime.securesms.service.KeyCachingService ;
2020-02-10 16:06:12 +01:00
import org.thoughtcrime.securesms.util.Base64 ;
2020-03-26 20:38:27 +01:00
import org.thoughtcrime.securesms.util.FileUtils ;
2019-09-25 14:26:30 +02:00
import org.thoughtcrime.securesms.util.ServiceUtil ;
2019-11-04 16:48:58 +01:00
import org.thoughtcrime.securesms.util.SqlUtil ;
2018-01-25 04:17:44 +01:00
import org.thoughtcrime.securesms.util.TextSecurePreferences ;
2020-02-10 16:06:12 +01:00
import org.thoughtcrime.securesms.util.Util ;
2018-01-25 04:17:44 +01:00
2020-03-26 20:38:27 +01:00
import java.io.ByteArrayInputStream ;
2018-03-18 23:06:51 +01:00
import java.io.File ;
2020-03-26 20:38:27 +01:00
import java.io.FileInputStream ;
import java.io.IOException ;
2019-09-25 14:26:30 +02:00
import java.util.List ;
2018-03-18 23:06:51 +01:00
2018-01-25 04:17:44 +01:00
public class SQLCipherOpenHelper extends SQLiteOpenHelper {
2018-02-16 20:10:35 +01:00
@SuppressWarnings ( "unused" )
2018-01-25 04:17:44 +01:00
private static final String TAG = SQLCipherOpenHelper . class . getSimpleName ( ) ;
2018-03-18 23:06:51 +01: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-19 00:04:33 +01:00
private static final int ATTACHMENT_DIMENSIONS = 6 ;
2018-04-03 01:17:32 +02:00
private static final int QUOTED_REPLIES = 7 ;
2018-04-27 02:03:54 +02:00
private static final int SHARED_CONTACTS = 8 ;
2018-04-07 03:15:24 +02:00
private static final int FULL_TEXT_SEARCH = 9 ;
2018-06-22 01:48:46 +02:00
private static final int BAD_IMPORT_CLEANUP = 10 ;
2018-08-11 15:55:52 +02:00
private static final int QUOTE_MISSING = 11 ;
2018-08-16 18:47:43 +02:00
private static final int NOTIFICATION_CHANNELS = 12 ;
2018-05-22 11:13:10 +02:00
private static final int SECRET_SENDER = 13 ;
2018-11-09 08:33:37 +01:00
private static final int ATTACHMENT_CAPTIONS = 14 ;
2018-11-26 20:50:31 +01:00
private static final int ATTACHMENT_CAPTIONS_FIX = 15 ;
2019-01-15 09:41:05 +01:00
private static final int PREVIEWS = 16 ;
2019-02-01 18:06:59 +01:00
private static final int CONVERSATION_SEARCH = 17 ;
2019-01-14 08:30:54 +01:00
private static final int SELF_ATTACHMENT_CLEANUP = 18 ;
2019-04-12 21:22:38 +02:00
private static final int RECIPIENT_FORCE_SMS_SELECTION = 19 ;
2019-03-28 16:56:35 +01:00
private static final int JOBMANAGER_STRIKES_BACK = 20 ;
2019-04-17 16:21:30 +02:00
private static final int STICKERS = 21 ;
2019-06-11 08:18:45 +02:00
private static final int REVEALABLE_MESSAGES = 22 ;
2019-08-01 01:33:56 +02:00
private static final int VIEW_ONCE_ONLY = 23 ;
2019-08-07 20:22:51 +02:00
private static final int RECIPIENT_IDS = 24 ;
2019-08-27 00:09:01 +02:00
private static final int RECIPIENT_SEARCH = 25 ;
2019-10-02 17:23:21 +02:00
private static final int RECIPIENT_CLEANUP = 26 ;
2019-10-15 16:07:20 +02:00
private static final int MMS_RECIPIENT_CLEANUP = 27 ;
2019-09-26 22:29:38 +02:00
private static final int ATTACHMENT_HASHING = 28 ;
2019-09-25 14:26:30 +02:00
private static final int NOTIFICATION_RECIPIENT_IDS = 29 ;
2019-10-17 14:26:08 +02:00
private static final int BLUR_HASH = 30 ;
2019-10-19 18:31:06 +02:00
private static final int MMS_RECIPIENT_CLEANUP_2 = 31 ;
2019-10-21 18:11:12 +02:00
private static final int ATTACHMENT_TRANSFORM_PROPERTIES = 32 ;
2019-10-24 01:16:13 +02:00
private static final int ATTACHMENT_CLEAR_HASHES = 33 ;
2019-11-08 17:29:37 +01:00
private static final int ATTACHMENT_CLEAR_HASHES_2 = 34 ;
2019-09-07 05:40:06 +02:00
private static final int UUIDS = 35 ;
2019-10-29 01:16:11 +01:00
private static final int USERNAMES = 36 ;
2019-12-03 22:57:21 +01:00
private static final int REACTIONS = 37 ;
2019-09-26 16:12:51 +02:00
private static final int STORAGE_SERVICE = 38 ;
2019-12-06 01:10:37 +01:00
private static final int REACTIONS_UNREAD_INDEX = 39 ;
2019-12-28 16:08:11 +01:00
private static final int RESUMABLE_DOWNLOADS = 40 ;
2020-01-10 07:08:39 +01:00
private static final int KEY_VALUE_STORE = 41 ;
2020-01-08 21:56:51 +01:00
private static final int ATTACHMENT_DISPLAY_ORDER = 42 ;
2019-12-20 21:12:22 +01:00
private static final int SPLIT_PROFILE_NAMES = 43 ;
2020-01-21 18:34:58 +01:00
private static final int STICKER_PACK_ORDER = 44 ;
2020-01-22 15:22:19 +01:00
private static final int MEGAPHONES = 45 ;
2020-01-30 17:33:34 +01:00
private static final int MEGAPHONE_FIRST_APPEARANCE = 46 ;
2020-02-10 16:06:12 +01:00
private static final int PROFILE_KEY_TO_DB = 47 ;
2020-02-11 00:40:22 +01:00
private static final int PROFILE_KEY_CREDENTIALS = 48 ;
2020-02-13 19:22:21 +01:00
private static final int ATTACHMENT_FILE_INDEX = 49 ;
2020-02-10 19:42:43 +01:00
private static final int STORAGE_SERVICE_ACTIVE = 50 ;
2020-02-14 17:00:32 +01:00
private static final int GROUPS_V2_RECIPIENT_CAPABILITY = 51 ;
2020-03-01 17:41:46 +01:00
private static final int TRANSFER_FILE_CLEANUP = 52 ;
2020-03-25 22:58:33 +01:00
private static final int PROFILE_DATA_MIGRATION = 53 ;
2020-03-26 20:38:27 +01:00
private static final int AVATAR_LOCATION_MIGRATION = 54 ;
2020-03-27 19:55:44 +01:00
private static final int GROUPS_V2 = 55 ;
2019-12-03 22:57:21 +01:00
2020-03-27 19:55:44 +01:00
private static final int DATABASE_VERSION = 55 ;
2018-01-25 04:17:44 +01: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-16 05:33:10 +01:00
db . execSQL ( OneTimePreKeyDatabase . CREATE_TABLE ) ;
db . execSQL ( SignedPreKeyDatabase . CREATE_TABLE ) ;
2018-02-19 01:43:18 +01:00
db . execSQL ( SessionDatabase . CREATE_TABLE ) ;
2019-04-17 16:21:30 +02:00
db . execSQL ( StickerDatabase . CREATE_TABLE ) ;
2019-09-26 16:12:51 +02:00
db . execSQL ( StorageKeyDatabase . CREATE_TABLE ) ;
2020-01-10 07:08:39 +01:00
db . execSQL ( KeyValueDatabase . CREATE_TABLE ) ;
2020-01-22 15:22:19 +01:00
db . execSQL ( MegaphoneDatabase . CREATE_TABLE ) ;
2019-04-17 16:21:30 +02:00
executeStatements ( db , SearchDatabase . CREATE_TABLE ) ;
executeStatements ( db , JobDatabase . CREATE_TABLE ) ;
2018-01-25 04:17:44 +01:00
2019-09-26 16:12:51 +02:00
executeStatements ( db , RecipientDatabase . CREATE_INDEXS ) ;
2018-01-25 04:17:44 +01: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 16:21:30 +02:00
executeStatements ( db , StickerDatabase . CREATE_INDEXES ) ;
2019-09-26 16:12:51 +02:00
executeStatements ( db , StorageKeyDatabase . CREATE_INDEXES ) ;
2018-01-25 04:17:44 +01:00
if ( context . getDatabasePath ( ClassicOpenHelper . NAME ) . exists ( ) ) {
ClassicOpenHelper legacyHelper = new ClassicOpenHelper ( context ) ;
android . database . sqlite . SQLiteDatabase legacyDb = legacyHelper . getWritableDatabase ( ) ;
2018-02-02 01:01:24 +01:00
SQLCipherMigrationHelper . migratePlaintext ( context , legacyDb , db ) ;
2018-01-25 04:17:44 +01:00
MasterSecret masterSecret = KeyCachingService . getMasterSecret ( context ) ;
2018-02-02 01:01:24 +01:00
if ( masterSecret ! = null ) SQLCipherMigrationHelper . migrateCiphertext ( context , masterSecret , legacyDb , db , null ) ;
2018-01-25 04:17:44 +01:00
else TextSecurePreferences . setNeedsSqlCipherMigration ( context , true ) ;
2018-02-16 05:33:10 +01:00
if ( ! PreKeyMigrationHelper . migratePreKeys ( context , db ) ) {
2019-10-15 21:47:54 +02:00
ApplicationDependencies . getJobManager ( ) . add ( new RefreshPreKeysJob ( ) ) ;
2018-02-16 05:33:10 +01:00
}
2018-02-19 01:43:18 +01:00
SessionStoreMigrationHelper . migrateSessions ( context , db ) ;
2018-02-16 05:33:10 +01:00
PreKeyMigrationHelper . cleanUpPreKeys ( context ) ;
2018-01-25 04:17:44 +01:00
}
}
@Override
public void onUpgrade ( SQLiteDatabase db , int oldVersion , int newVersion ) {
2018-08-02 15:25:33 +02:00
Log . i ( TAG , "Upgrading database: " + oldVersion + ", " + newVersion ) ;
2019-09-26 16:12:51 +02:00
long startTime = System . currentTimeMillis ( ) ;
2018-01-25 04:17:44 +01:00
2018-02-16 05:33:10 +01: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-10-15 21:47:54 +02:00
ApplicationDependencies . getJobManager ( ) . add ( new RefreshPreKeysJob ( ) ) ;
2018-02-16 05:33:10 +01:00
}
2018-02-19 01:43:18 +01:00
}
2018-02-16 05:33:10 +01:00
2018-02-19 01:43:18 +01: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-16 05:33:10 +01:00
}
2018-03-18 23:06:51 +01: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-19 00:04:33 +01: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-03 01:17:32 +02: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-27 02:03:54 +02:00
if ( oldVersion < SHARED_CONTACTS ) {
db . execSQL ( "ALTER TABLE mms ADD COLUMN shared_contacts TEXT" ) ;
}
2018-04-07 03:15:24 +02:00
if ( oldVersion < FULL_TEXT_SEARCH ) {
2019-02-01 18:06:59 +01: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-07 03:15:24 +02:00
Log . i ( TAG , "Beginning to build search index." ) ;
long start = SystemClock . elapsedRealtime ( ) ;
2018-07-03 03:10:11 +02:00
db . execSQL ( "INSERT INTO sms_fts (rowid, body) SELECT _id, body FROM sms" ) ;
2018-04-07 03:15:24 +02:00
long smsFinished = SystemClock . elapsedRealtime ( ) ;
Log . i ( TAG , "Indexing SMS completed in " + ( smsFinished - start ) + " ms" ) ;
2018-07-03 03:10:11 +02:00
db . execSQL ( "INSERT INTO mms_fts (rowid, body) SELECT _id, body FROM mms" ) ;
2018-04-07 03:15:24 +02: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-22 01:48:46 +02: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-08 01:08:45 +02:00
// Note: This column only being checked due to upgrade issues as described in #8184
2019-11-04 16:48:58 +01:00
if ( oldVersion < QUOTE_MISSING & & ! SqlUtil . columnExists ( db , "mms" , "quote_missing" ) ) {
2018-08-11 15:55:52 +02:00
db . execSQL ( "ALTER TABLE mms ADD COLUMN quote_missing INTEGER DEFAULT 0" ) ;
}
2018-09-08 01:08:45 +02:00
// Note: The column only being checked due to upgrade issues as described in #8184
2019-11-04 16:48:58 +01:00
if ( oldVersion < NOTIFICATION_CHANNELS & & ! SqlUtil . columnExists ( db , "recipient_preferences" , "notification_channel" ) ) {
2018-08-16 18:47:43 +02:00
db . execSQL ( "ALTER TABLE recipient_preferences ADD COLUMN notification_channel TEXT DEFAULT NULL" ) ;
2018-08-22 22:19:59 +02:00
NotificationChannels . create ( context ) ;
2018-08-16 18:47:43 +02: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 ( ) ) {
2019-08-07 20:22:51 +02:00
String rawAddress = cursor . getString ( cursor . getColumnIndexOrThrow ( "recipient_ids" ) ) ;
String address = PhoneNumberFormatter . get ( context ) . format ( rawAddress ) ;
2018-08-16 18:47:43 +02:00
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" ) ) ;
2019-10-09 18:57:36 +02:00
String displayName = NotificationChannels . getChannelDisplayNameFor ( context , systemName , profileName , null , address ) ;
2018-08-16 18:47:43 +02:00
boolean vibrateEnabled = vibrateState = = 0 ? TextSecurePreferences . isNotificationVibrateEnabled ( context ) : vibrateState = = 1 ;
2020-03-26 15:00:17 +01:00
if ( GroupId . isEncodedGroup ( address ) ) {
2019-08-07 20:22:51 +02:00
try ( Cursor groupCursor = db . rawQuery ( "SELECT title FROM groups WHERE group_id = ?" , new String [ ] { address } ) ) {
2018-08-22 23:19:37 +02:00
if ( groupCursor ! = null & & groupCursor . moveToFirst ( ) ) {
String title = groupCursor . getString ( groupCursor . getColumnIndexOrThrow ( "title" ) ) ;
if ( ! TextUtils . isEmpty ( title ) ) {
displayName = title ;
}
}
}
}
2019-09-25 14:26:30 +02:00
String channelId = NotificationChannels . createChannelFor ( context , "contact_" + address + "_" + System . currentTimeMillis ( ) , displayName , messageSoundUri , vibrateEnabled ) ;
2018-08-16 18:47:43 +02:00
ContentValues values = new ContentValues ( 1 ) ;
values . put ( "notification_channel" , channelId ) ;
2019-08-07 20:22:51 +02:00
db . update ( "recipient_preferences" , values , "recipient_ids = ?" , new String [ ] { rawAddress } ) ;
2018-08-16 18:47:43 +02:00
}
}
}
2018-05-22 11:13:10 +02: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-12 01:45:22 +02: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 11:13:10 +02:00
}
2018-11-09 08:33:37 +01:00
if ( oldVersion < ATTACHMENT_CAPTIONS ) {
db . execSQL ( "ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL" ) ;
}
2018-11-26 20:50:31 +01: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 ) {
2019-11-04 16:48:58 +01:00
if ( ! SqlUtil . columnExists ( db , "part" , "caption" ) ) {
2018-11-26 20:50:31 +01:00
db . execSQL ( "ALTER TABLE part ADD COLUMN caption TEXT DEFAULT NULL" ) ;
}
}
2019-01-15 09:41:05 +01:00
if ( oldVersion < PREVIEWS ) {
db . execSQL ( "ALTER TABLE mms ADD COLUMN previews TEXT" ) ;
}
2019-02-01 18:06:59 +01: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-14 08:30:54 +01:00
if ( oldVersion < SELF_ATTACHMENT_CLEANUP ) {
String localNumber = TextSecurePreferences . getLocalNumber ( context ) ;
2019-02-16 04:25:20 +01: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-14 08:30:54 +01:00
2019-02-16 04:25:20 +01:00
updateValues . put ( "pending_push" , 0 ) ;
2019-01-14 08:30:54 +01:00
2019-02-16 04:25:20 +01: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-14 08:30:54 +01:00
}
}
}
2019-04-12 21:22:38 +02:00
if ( oldVersion < RECIPIENT_FORCE_SMS_SELECTION ) {
db . execSQL ( "ALTER TABLE recipient_preferences ADD COLUMN force_sms_selection INTEGER DEFAULT 0" ) ;
}
2019-03-28 16:56:35 +01: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 16:21:30 +02: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-06-11 08:18:45 +02:00
if ( oldVersion < REVEALABLE_MESSAGES ) {
db . execSQL ( "ALTER TABLE mms ADD COLUMN reveal_duration INTEGER DEFAULT 0" ) ;
db . execSQL ( "ALTER TABLE mms ADD COLUMN reveal_start_time INTEGER DEFAULT 0" ) ;
db . execSQL ( "ALTER TABLE thread ADD COLUMN snippet_content_type TEXT DEFAULT NULL" ) ;
db . execSQL ( "ALTER TABLE thread ADD COLUMN snippet_extras TEXT DEFAULT NULL" ) ;
}
2019-08-01 01:33:56 +02:00
if ( oldVersion < VIEW_ONCE_ONLY ) {
db . execSQL ( "UPDATE mms SET reveal_duration = 1 WHERE reveal_duration > 0" ) ;
db . execSQL ( "UPDATE mms SET reveal_start_time = 0" ) ;
}
2019-08-07 20:22:51 +02:00
if ( oldVersion < RECIPIENT_IDS ) {
RecipientIdMigrationHelper . execute ( db ) ;
}
2019-08-27 00:09:01 +02:00
if ( oldVersion < RECIPIENT_SEARCH ) {
db . execSQL ( "ALTER TABLE recipient ADD COLUMN system_phone_type INTEGER DEFAULT -1" ) ;
String localNumber = TextSecurePreferences . getLocalNumber ( context ) ;
if ( ! TextUtils . isEmpty ( localNumber ) ) {
try ( Cursor cursor = db . query ( "recipient" , null , "phone = ?" , new String [ ] { localNumber } , null , null , null ) ) {
if ( cursor = = null | | ! cursor . moveToFirst ( ) ) {
ContentValues values = new ContentValues ( ) ;
values . put ( "phone" , localNumber ) ;
values . put ( "registered" , 1 ) ;
values . put ( "profile_sharing" , 1 ) ;
db . insert ( "recipient" , null , values ) ;
} else {
2020-03-25 22:58:33 +01:00
db . execSQL ( "UPDATE recipient SET registered = ?, profile_sharing = ? WHERE phone = ?" ,
new String [ ] { "1" , "1" , localNumber } ) ;
2019-08-27 00:09:01 +02:00
}
}
}
}
2019-10-02 17:23:21 +02:00
if ( oldVersion < RECIPIENT_CLEANUP ) {
RecipientIdCleanupHelper . execute ( db ) ;
}
2019-10-15 16:07:20 +02:00
if ( oldVersion < MMS_RECIPIENT_CLEANUP ) {
ContentValues values = new ContentValues ( 1 ) ;
values . put ( "address" , "-1" ) ;
int count = db . update ( "mms" , values , "address = ?" , new String [ ] { "0" } ) ;
Log . i ( TAG , "MMS recipient cleanup updated " + count + " rows." ) ;
}
2019-09-26 22:29:38 +02:00
if ( oldVersion < ATTACHMENT_HASHING ) {
db . execSQL ( "ALTER TABLE part ADD COLUMN data_hash TEXT DEFAULT NULL" ) ;
db . execSQL ( "CREATE INDEX IF NOT EXISTS part_data_hash_index ON part (data_hash)" ) ;
}
2019-09-25 14:26:30 +02:00
if ( oldVersion < NOTIFICATION_RECIPIENT_IDS & & Build . VERSION . SDK_INT > = 26 ) {
NotificationManager notificationManager = ServiceUtil . getNotificationManager ( context ) ;
List < NotificationChannel > channels = Stream . of ( notificationManager . getNotificationChannels ( ) )
. filter ( c - > c . getId ( ) . startsWith ( "contact_" ) )
. toList ( ) ;
Log . i ( TAG , "Migrating " + channels . size ( ) + " channels to use RecipientId's." ) ;
for ( NotificationChannel oldChannel : channels ) {
notificationManager . deleteNotificationChannel ( oldChannel . getId ( ) ) ;
2019-09-07 05:40:06 +02:00
int startIndex = "contact_" . length ( ) ;
int endIndex = oldChannel . getId ( ) . lastIndexOf ( "_" ) ;
String address = oldChannel . getId ( ) . substring ( startIndex , endIndex ) ;
2019-09-25 14:26:30 +02:00
String recipientId ;
try ( Cursor cursor = db . query ( "recipient" , new String [ ] { "_id" } , "phone = ? OR email = ? OR group_id = ?" , new String [ ] { address , address , address } , null , null , null ) ) {
if ( cursor ! = null & & cursor . moveToFirst ( ) ) {
2019-09-07 05:40:06 +02:00
recipientId = cursor . getString ( cursor . getColumnIndexOrThrow ( "_id" ) ) ;
2019-09-25 14:26:30 +02:00
} else {
Log . w ( TAG , "Couldn't find recipient for address: " + address ) ;
continue ;
}
}
String newId = "contact_" + recipientId + "_" + System . currentTimeMillis ( ) ;
NotificationChannel newChannel = new NotificationChannel ( newId , oldChannel . getName ( ) , oldChannel . getImportance ( ) ) ;
Log . i ( TAG , "Updating channel ID from '" + oldChannel . getId ( ) + "' to '" + newChannel . getId ( ) + "'." ) ;
newChannel . setGroup ( oldChannel . getGroup ( ) ) ;
newChannel . setSound ( oldChannel . getSound ( ) , oldChannel . getAudioAttributes ( ) ) ;
newChannel . setBypassDnd ( oldChannel . canBypassDnd ( ) ) ;
newChannel . enableVibration ( oldChannel . shouldVibrate ( ) ) ;
newChannel . setVibrationPattern ( oldChannel . getVibrationPattern ( ) ) ;
newChannel . setLockscreenVisibility ( oldChannel . getLockscreenVisibility ( ) ) ;
newChannel . setShowBadge ( oldChannel . canShowBadge ( ) ) ;
newChannel . setLightColor ( oldChannel . getLightColor ( ) ) ;
newChannel . enableLights ( oldChannel . shouldShowLights ( ) ) ;
notificationManager . createNotificationChannel ( newChannel ) ;
ContentValues contentValues = new ContentValues ( 1 ) ;
contentValues . put ( "notification_channel" , newChannel . getId ( ) ) ;
db . update ( "recipient" , contentValues , "_id = ?" , new String [ ] { recipientId } ) ;
}
}
2019-10-17 14:26:08 +02:00
if ( oldVersion < BLUR_HASH ) {
db . execSQL ( "ALTER TABLE part ADD COLUMN blur_hash TEXT DEFAULT NULL" ) ;
}
2019-10-19 18:31:06 +02:00
if ( oldVersion < MMS_RECIPIENT_CLEANUP_2 ) {
ContentValues values = new ContentValues ( 1 ) ;
values . put ( "address" , "-1" ) ;
int count = db . update ( "mms" , values , "address = ? OR address IS NULL" , new String [ ] { "0" } ) ;
Log . i ( TAG , "MMS recipient cleanup 2 updated " + count + " rows." ) ;
}
2019-10-21 18:11:12 +02:00
if ( oldVersion < ATTACHMENT_TRANSFORM_PROPERTIES ) {
db . execSQL ( "ALTER TABLE part ADD COLUMN transform_properties TEXT DEFAULT NULL" ) ;
}
2019-10-24 01:16:13 +02:00
if ( oldVersion < ATTACHMENT_CLEAR_HASHES ) {
db . execSQL ( "UPDATE part SET data_hash = null" ) ;
}
2019-11-08 17:29:37 +01:00
if ( oldVersion < ATTACHMENT_CLEAR_HASHES_2 ) {
db . execSQL ( "UPDATE part SET data_hash = null" ) ;
Glide . get ( context ) . clearDiskCache ( ) ;
}
2019-09-07 05:40:06 +02:00
if ( oldVersion < UUIDS ) {
db . execSQL ( "ALTER TABLE recipient ADD COLUMN uuid_supported INTEGER DEFAULT 0" ) ;
db . execSQL ( "ALTER TABLE push ADD COLUMN source_uuid TEXT DEFAULT NULL" ) ;
}
2019-10-29 01:16:11 +01:00
if ( oldVersion < USERNAMES ) {
db . execSQL ( "ALTER TABLE recipient ADD COLUMN username TEXT DEFAULT NULL" ) ;
db . execSQL ( "CREATE UNIQUE INDEX IF NOT EXISTS recipient_username_index ON recipient (username)" ) ;
}
2019-12-03 22:57:21 +01:00
if ( oldVersion < REACTIONS ) {
db . execSQL ( "ALTER TABLE sms ADD COLUMN reactions BLOB DEFAULT NULL" ) ;
db . execSQL ( "ALTER TABLE mms ADD COLUMN reactions BLOB DEFAULT NULL" ) ;
db . execSQL ( "ALTER TABLE sms ADD COLUMN reactions_unread INTEGER DEFAULT 0" ) ;
db . execSQL ( "ALTER TABLE mms ADD COLUMN reactions_unread INTEGER DEFAULT 0" ) ;
db . execSQL ( "ALTER TABLE sms ADD COLUMN reactions_last_seen INTEGER DEFAULT -1" ) ;
db . execSQL ( "ALTER TABLE mms ADD COLUMN reactions_last_seen INTEGER DEFAULT -1" ) ;
}
2019-09-26 16:12:51 +02:00
if ( oldVersion < STORAGE_SERVICE ) {
db . execSQL ( "CREATE TABLE storage_key (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"type INTEGER, " +
"key TEXT UNIQUE)" ) ;
db . execSQL ( "CREATE INDEX IF NOT EXISTS storage_key_type_index ON storage_key (type)" ) ;
db . execSQL ( "ALTER TABLE recipient ADD COLUMN system_info_pending INTEGER DEFAULT 0" ) ;
db . execSQL ( "ALTER TABLE recipient ADD COLUMN storage_service_key TEXT DEFAULT NULL" ) ;
db . execSQL ( "ALTER TABLE recipient ADD COLUMN dirty INTEGER DEFAULT 0" ) ;
db . execSQL ( "CREATE UNIQUE INDEX recipient_storage_service_key ON recipient (storage_service_key)" ) ;
db . execSQL ( "CREATE INDEX recipient_dirty_index ON recipient (dirty)" ) ;
}
2019-12-06 01:10:37 +01:00
if ( oldVersion < REACTIONS_UNREAD_INDEX ) {
db . execSQL ( "CREATE INDEX IF NOT EXISTS sms_reactions_unread_index ON sms (reactions_unread);" ) ;
db . execSQL ( "CREATE INDEX IF NOT EXISTS mms_reactions_unread_index ON mms (reactions_unread);" ) ;
}
2019-12-28 16:08:11 +01:00
if ( oldVersion < RESUMABLE_DOWNLOADS ) {
db . execSQL ( "ALTER TABLE part ADD COLUMN transfer_file TEXT DEFAULT NULL" ) ;
}
2020-01-10 07:08:39 +01:00
if ( oldVersion < KEY_VALUE_STORE ) {
db . execSQL ( "CREATE TABLE key_value (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"key TEXT UNIQUE, " +
"value TEXT, " +
"type INTEGER)" ) ;
}
2020-01-08 21:56:51 +01:00
if ( oldVersion < ATTACHMENT_DISPLAY_ORDER ) {
db . execSQL ( "ALTER TABLE part ADD COLUMN display_order INTEGER DEFAULT 0" ) ;
}
2019-12-20 21:12:22 +01:00
if ( oldVersion < SPLIT_PROFILE_NAMES ) {
db . execSQL ( "ALTER TABLE recipient ADD COLUMN profile_family_name TEXT DEFAULT NULL" ) ;
db . execSQL ( "ALTER TABLE recipient ADD COLUMN profile_joined_name TEXT DEFAULT NULL" ) ;
}
2020-01-21 18:34:58 +01:00
if ( oldVersion < STICKER_PACK_ORDER ) {
db . execSQL ( "ALTER TABLE sticker ADD COLUMN pack_order INTEGER DEFAULT 0" ) ;
}
2020-01-22 15:22:19 +01:00
if ( oldVersion < MEGAPHONES ) {
db . execSQL ( "CREATE TABLE megaphone (_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"event TEXT UNIQUE, " +
"seen_count INTEGER, " +
"last_seen INTEGER, " +
"finished INTEGER)" ) ;
}
2020-01-30 17:33:34 +01:00
if ( oldVersion < MEGAPHONE_FIRST_APPEARANCE ) {
db . execSQL ( "ALTER TABLE megaphone ADD COLUMN first_visible INTEGER DEFAULT 0" ) ;
}
2020-02-10 16:06:12 +01:00
if ( oldVersion < PROFILE_KEY_TO_DB ) {
String localNumber = TextSecurePreferences . getLocalNumber ( context ) ;
if ( ! TextUtils . isEmpty ( localNumber ) ) {
String encodedProfileKey = PreferenceManager . getDefaultSharedPreferences ( context ) . getString ( "pref_profile_key" , null ) ;
byte [ ] profileKey = encodedProfileKey ! = null ? Base64 . decodeOrThrow ( encodedProfileKey ) : Util . getSecretBytes ( 32 ) ;
ContentValues values = new ContentValues ( 1 ) ;
values . put ( "profile_key" , Base64 . encodeBytes ( profileKey ) ) ;
if ( db . update ( "recipient" , values , "phone = ?" , new String [ ] { localNumber } ) = = 0 ) {
throw new AssertionError ( "No rows updated!" ) ;
}
}
}
2020-02-11 00:40:22 +01:00
if ( oldVersion < PROFILE_KEY_CREDENTIALS ) {
db . execSQL ( "ALTER TABLE recipient ADD COLUMN profile_key_credential TEXT DEFAULT NULL" ) ;
}
2020-02-13 19:22:21 +01:00
if ( oldVersion < ATTACHMENT_FILE_INDEX ) {
db . execSQL ( "CREATE INDEX IF NOT EXISTS part_data_index ON part (_data)" ) ;
}
2020-02-10 19:42:43 +01:00
if ( oldVersion < STORAGE_SERVICE_ACTIVE ) {
db . execSQL ( "ALTER TABLE recipient ADD COLUMN group_type INTEGER DEFAULT 0" ) ;
db . execSQL ( "CREATE INDEX IF NOT EXISTS recipient_group_type_index ON recipient (group_type)" ) ;
db . execSQL ( "UPDATE recipient set group_type = 1 WHERE group_id NOT NULL AND group_id LIKE '__signal_mms_group__%'" ) ;
db . execSQL ( "UPDATE recipient set group_type = 2 WHERE group_id NOT NULL AND group_id LIKE '__textsecure_group__%'" ) ;
try ( Cursor cursor = db . rawQuery ( "SELECT _id FROM recipient WHERE registered = 1 or group_type = 2" , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
String id = cursor . getString ( cursor . getColumnIndexOrThrow ( "_id" ) ) ;
ContentValues values = new ContentValues ( 1 ) ;
values . put ( "dirty" , 2 ) ;
values . put ( "storage_service_key" , Base64 . encodeBytes ( StorageSyncHelper . generateKey ( ) ) ) ;
db . update ( "recipient" , values , "_id = ?" , new String [ ] { id } ) ;
}
}
}
2020-02-14 17:00:32 +01:00
if ( oldVersion < GROUPS_V2_RECIPIENT_CAPABILITY ) {
db . execSQL ( "ALTER TABLE recipient ADD COLUMN gv2_capability INTEGER DEFAULT 0" ) ;
}
2020-03-01 17:41:46 +01:00
if ( oldVersion < TRANSFER_FILE_CLEANUP ) {
File partsDirectory = context . getDir ( "parts" , Context . MODE_PRIVATE ) ;
if ( partsDirectory . exists ( ) ) {
File [ ] transferFiles = partsDirectory . listFiles ( ( dir , name ) - > name . startsWith ( "transfer" ) ) ;
int deleteCount = 0 ;
Log . i ( TAG , "Found " + transferFiles . length + " dangling transfer files." ) ;
for ( File file : transferFiles ) {
if ( file . delete ( ) ) {
Log . i ( TAG , "Deleted " + file . getName ( ) ) ;
deleteCount + + ;
}
}
Log . i ( TAG , "Deleted " + deleteCount + " dangling transfer files." ) ;
} else {
Log . w ( TAG , "Part directory did not exist. Skipping." ) ;
}
}
2020-03-25 22:58:33 +01:00
if ( oldVersion < PROFILE_DATA_MIGRATION ) {
String localNumber = TextSecurePreferences . getLocalNumber ( context ) ;
if ( localNumber ! = null ) {
String encodedProfileName = PreferenceManager . getDefaultSharedPreferences ( context ) . getString ( "pref_profile_name" , null ) ;
ProfileName profileName = ProfileName . fromSerialized ( encodedProfileName ) ;
db . execSQL ( "UPDATE recipient SET signal_profile_name = ?, profile_family_name = ?, profile_joined_name = ? WHERE phone = ?" ,
new String [ ] { profileName . getGivenName ( ) , profileName . getFamilyName ( ) , profileName . toString ( ) , localNumber } ) ;
}
}
2020-03-26 20:38:27 +01:00
if ( oldVersion < AVATAR_LOCATION_MIGRATION ) {
File oldAvatarDirectory = new File ( context . getFilesDir ( ) , "avatars" ) ;
File [ ] results = oldAvatarDirectory . listFiles ( ) ;
if ( results ! = null ) {
Log . i ( TAG , "Preparing to migrate " + results . length + " avatars." ) ;
for ( File file : results ) {
if ( Util . isLong ( file . getName ( ) ) ) {
try {
AvatarHelper . setAvatar ( context , RecipientId . from ( file . getName ( ) ) , new FileInputStream ( file ) ) ;
} catch ( IOException e ) {
Log . w ( TAG , "Failed to copy file " + file . getName ( ) + "! Skipping." ) ;
}
} else {
Log . w ( TAG , "Invalid avatar name '" + file . getName ( ) + "'! Skipping." ) ;
}
}
} else {
Log . w ( TAG , "No avatar directory files found." ) ;
}
if ( ! FileUtils . deleteDirectory ( oldAvatarDirectory ) ) {
Log . w ( TAG , "Failed to delete avatar directory." ) ;
}
try ( Cursor cursor = db . rawQuery ( "SELECT recipient_id, avatar FROM groups" , null ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
RecipientId recipientId = RecipientId . from ( cursor . getLong ( cursor . getColumnIndexOrThrow ( "recipient_id" ) ) ) ;
byte [ ] avatar = cursor . getBlob ( cursor . getColumnIndexOrThrow ( "avatar" ) ) ;
try {
AvatarHelper . setAvatar ( context , recipientId , avatar ! = null ? new ByteArrayInputStream ( avatar ) : null ) ;
} catch ( IOException e ) {
Log . w ( TAG , "Failed to copy avatar for " + recipientId + "! Skipping." , e ) ;
}
}
}
db . execSQL ( "UPDATE groups SET avatar_id = 0 WHERE avatar IS NULL" ) ;
db . execSQL ( "UPDATE groups SET avatar = NULL" ) ;
}
2020-03-27 19:55:44 +01:00
if ( oldVersion < GROUPS_V2 ) {
db . execSQL ( "ALTER TABLE groups ADD COLUMN master_key" ) ;
db . execSQL ( "ALTER TABLE groups ADD COLUMN revision" ) ;
db . execSQL ( "ALTER TABLE groups ADD COLUMN decrypted_group" ) ;
}
2020-03-01 17:41:46 +01:00
2018-02-16 05:33:10 +01:00
db . setTransactionSuccessful ( ) ;
} finally {
db . endTransaction ( ) ;
}
if ( oldVersion < MIGRATE_PREKEYS_VERSION ) {
PreKeyMigrationHelper . cleanUpPreKeys ( context ) ;
2018-02-16 20:10:35 +01:00
}
2019-09-26 16:12:51 +02:00
Log . i ( TAG , "Upgrade complete. Took " + ( System . currentTimeMillis ( ) - startTime ) + " ms." ) ;
2018-01-25 04:17:44 +01:00
}
public SQLiteDatabase getReadableDatabase ( ) {
return getReadableDatabase ( databaseSecret . asString ( ) ) ;
}
public SQLiteDatabase getWritableDatabase ( ) {
return getWritableDatabase ( databaseSecret . asString ( ) ) ;
}
2018-04-02 15:27:50 +02:00
public void markCurrent ( SQLiteDatabase db ) {
db . setVersion ( DATABASE_VERSION ) ;
}
2019-09-05 20:39:18 +02:00
public static boolean databaseFileExists ( @NonNull Context context ) {
return context . getDatabasePath ( DATABASE_NAME ) . exists ( ) ;
}
2020-03-01 16:54:11 +01:00
public static File getDatabaseFile ( @NonNull Context context ) {
return context . getDatabasePath ( DATABASE_NAME ) ;
}
2018-01-25 04:17:44 +01:00
private void executeStatements ( SQLiteDatabase db , String [ ] statements ) {
for ( String statement : statements )
db . execSQL ( statement ) ;
}
}