11package org .thoughtcrime .securesms .database ;
22
3+ import android .app .Application ;
34import android .content .ContentValues ;
4- import android .content .Context ;
55import android .database .Cursor ;
66
77import androidx .annotation .NonNull ;
88
9+ import net .sqlcipher .database .SQLiteDatabase ;
10+ import net .sqlcipher .database .SQLiteOpenHelper ;
11+
912import org .signal .core .util .logging .Log ;
10- import org .thoughtcrime .securesms .database .helpers .SQLCipherOpenHelper ;
13+ import org .thoughtcrime .securesms .crypto .DatabaseSecret ;
14+ import org .thoughtcrime .securesms .crypto .DatabaseSecretProvider ;
1115import org .thoughtcrime .securesms .database .model .MegaphoneRecord ;
1216import org .thoughtcrime .securesms .megaphone .Megaphones .Event ;
17+ import org .thoughtcrime .securesms .util .CursorUtil ;
1318
1419import java .util .ArrayList ;
1520import java .util .Collection ;
2025/**
2126 * IMPORTANT: Writes should only be made through {@link org.thoughtcrime.securesms.megaphone.MegaphoneRepository}.
2227 */
23- public class MegaphoneDatabase extends Database {
28+ public class MegaphoneDatabase extends SQLiteOpenHelper implements SignalDatabase {
2429
2530 private static final String TAG = Log .tag (MegaphoneDatabase .class );
2631
27- private static final String TABLE_NAME = "megaphone" ;
32+ private static final int DATABASE_VERSION = 1 ;
33+ private static final String DATABASE_NAME = "signal-megaphone.db" ;
2834
35+ private static final String TABLE_NAME = "megaphone" ;
2936 private static final String ID = "_id" ;
3037 private static final String EVENT = "event" ;
3138 private static final String SEEN_COUNT = "seen_count" ;
3239 private static final String LAST_SEEN = "last_seen" ;
3340 private static final String FIRST_VISIBLE = "first_visible" ;
3441 private static final String FINISHED = "finished" ;
3542
36- public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
37- EVENT + " TEXT UNIQUE, " +
38- SEEN_COUNT + " INTEGER, " +
39- LAST_SEEN + " INTEGER, " +
43+ public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
44+ EVENT + " TEXT UNIQUE, " +
45+ SEEN_COUNT + " INTEGER, " +
46+ LAST_SEEN + " INTEGER, " +
4047 FIRST_VISIBLE + " INTEGER, " +
41- FINISHED + " INTEGER)" ;
48+ FINISHED + " INTEGER)" ;
49+
50+ private static volatile MegaphoneDatabase instance ;
51+
52+ private final Application application ;
53+ private final DatabaseSecret databaseSecret ;
54+
55+ public static @ NonNull MegaphoneDatabase getInstance (@ NonNull Application context ) {
56+ if (instance == null ) {
57+ synchronized (MegaphoneDatabase .class ) {
58+ if (instance == null ) {
59+ instance = new MegaphoneDatabase (context , DatabaseSecretProvider .getOrCreateDatabaseSecret (context ));
60+ }
61+ }
62+ }
63+ return instance ;
64+ }
65+
66+ public MegaphoneDatabase (@ NonNull Application application , @ NonNull DatabaseSecret databaseSecret ) {
67+ super (application , DATABASE_NAME , null , DATABASE_VERSION , new SqlCipherDatabaseHook ());
68+
69+ this .application = application ;
70+ this .databaseSecret = databaseSecret ;
71+ }
72+
73+ @ Override
74+ public void onCreate (SQLiteDatabase db ) {
75+ Log .i (TAG , "onCreate()" );
76+
77+ db .execSQL (CREATE_TABLE );
78+
79+ if (DatabaseFactory .getInstance (application ).hasTable ("megaphone" )) {
80+ Log .i (TAG , "Found old megaphone table. Migrating data." );
81+ migrateDataFromPreviousDatabase (DatabaseFactory .getInstance (application ).getRawDatabase (), db );
82+ }
83+ }
4284
43- MegaphoneDatabase (Context context , SQLCipherOpenHelper databaseHelper ) {
44- super (context , databaseHelper );
85+ @ Override
86+ public void onUpgrade (SQLiteDatabase db , int oldVersion , int newVersion ) {
87+ Log .i (TAG , "onUpgrade(" + oldVersion + ", " + newVersion + ")" );
88+ }
89+
90+ @ Override
91+ public void onOpen (SQLiteDatabase db ) {
92+ Log .i (TAG , "onOpen()" );
93+
94+ if (DatabaseFactory .getInstance (application ).hasTable ("megaphone" )) {
95+ Log .i (TAG , "Dropping original megaphone table from the main database." );
96+ DatabaseFactory .getInstance (application ).getRawDatabase ().rawExecSQL ("DROP TABLE megaphone" );
97+ }
4598 }
4699
47100 public void insert (@ NonNull Collection <Event > events ) {
48- SQLiteDatabase db = databaseHelper . getWritableDatabase ();
101+ SQLiteDatabase db = getWritableDatabase ();
49102
50103 db .beginTransaction ();
51104 try {
@@ -63,14 +116,14 @@ public void insert(@NonNull Collection<Event> events) {
63116 }
64117
65118 public @ NonNull List <MegaphoneRecord > getAllAndDeleteMissing () {
66- SQLiteDatabase db = databaseHelper . getWritableDatabase ();
119+ SQLiteDatabase db = getWritableDatabase ();
67120 List <MegaphoneRecord > records = new ArrayList <>();
68121
69122 db .beginTransaction ();
70123 try {
71124 Set <String > missingKeys = new HashSet <>();
72125
73- try (Cursor cursor = databaseHelper . getReadableDatabase () .query (TABLE_NAME , null , null , null , null , null , null )) {
126+ try (Cursor cursor = db .query (TABLE_NAME , null , null , null , null , null , null )) {
74127 while (cursor != null && cursor .moveToNext ()) {
75128 String event = cursor .getString (cursor .getColumnIndexOrThrow (EVENT ));
76129 int seenCount = cursor .getInt (cursor .getColumnIndexOrThrow (SEEN_COUNT ));
@@ -91,7 +144,7 @@ public void insert(@NonNull Collection<Event> events) {
91144 String query = EVENT + " = ?" ;
92145 String [] args = new String []{missing };
93146
94- databaseHelper . getWritableDatabase () .delete (TABLE_NAME , query , args );
147+ db .delete (TABLE_NAME , query , args );
95148 }
96149
97150 db .setTransactionSuccessful ();
@@ -109,7 +162,7 @@ public void markFirstVisible(@NonNull Event event, long time) {
109162 ContentValues values = new ContentValues ();
110163 values .put (FIRST_VISIBLE , time );
111164
112- databaseHelper . getWritableDatabase ().update (TABLE_NAME , values , query , args );
165+ getWritableDatabase ().update (TABLE_NAME , values , query , args );
113166 }
114167
115168 public void markSeen (@ NonNull Event event , int seenCount , long lastSeen ) {
@@ -120,7 +173,7 @@ public void markSeen(@NonNull Event event, int seenCount, long lastSeen) {
120173 values .put (SEEN_COUNT , seenCount );
121174 values .put (LAST_SEEN , lastSeen );
122175
123- databaseHelper . getWritableDatabase ().update (TABLE_NAME , values , query , args );
176+ getWritableDatabase ().update (TABLE_NAME , values , query , args );
124177 }
125178
126179 public void markFinished (@ NonNull Event event ) {
@@ -130,13 +183,38 @@ public void markFinished(@NonNull Event event) {
130183 ContentValues values = new ContentValues ();
131184 values .put (FINISHED , 1 );
132185
133- databaseHelper . getWritableDatabase ().update (TABLE_NAME , values , query , args );
186+ getWritableDatabase ().update (TABLE_NAME , values , query , args );
134187 }
135188
136189 public void delete (@ NonNull Event event ) {
137190 String query = EVENT + " = ?" ;
138191 String [] args = new String []{event .getKey ()};
139192
140- databaseHelper .getWritableDatabase ().delete (TABLE_NAME , query , args );
193+ getWritableDatabase ().delete (TABLE_NAME , query , args );
194+ }
195+
196+ private @ NonNull SQLiteDatabase getWritableDatabase () {
197+ return getWritableDatabase (databaseSecret .asString ());
198+ }
199+
200+ @ Override
201+ public @ NonNull SQLiteDatabase getSqlCipherDatabase () {
202+ return getWritableDatabase ();
203+ }
204+
205+ private static void migrateDataFromPreviousDatabase (@ NonNull SQLiteDatabase oldDb , @ NonNull SQLiteDatabase newDb ) {
206+ try (Cursor cursor = oldDb .rawQuery ("SELECT * FROM megaphone" , null )) {
207+ while (cursor .moveToNext ()) {
208+ ContentValues values = new ContentValues ();
209+
210+ values .put (EVENT , CursorUtil .requireString (cursor , "event" ));
211+ values .put (SEEN_COUNT , CursorUtil .requireInt (cursor , "seen_count" ));
212+ values .put (LAST_SEEN , CursorUtil .requireLong (cursor , "last_seen" ));
213+ values .put (FIRST_VISIBLE , CursorUtil .requireLong (cursor , "first_visible" ));
214+ values .put (FINISHED , CursorUtil .requireInt (cursor , "finished" ));
215+
216+ newDb .insert (TABLE_NAME , null , values );
217+ }
218+ }
141219 }
142220}
0 commit comments