1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.contactkeys;
18 
19 import android.annotation.FlaggedApi;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.database.sqlite.SQLiteDatabase;
23 import android.database.sqlite.SQLiteOpenHelper;
24 import android.database.sqlite.SQLiteStatement;
25 import android.provider.E2eeContactKeysManager;
26 import android.provider.E2eeContactKeysManager.E2eeContactKeys;
27 import android.provider.Flags;
28 import android.util.Log;
29 
30 import java.util.ArrayList;
31 import java.util.List;
32 
33 /**
34  * Database helper for end-to-end encryption contact keys.
35  */
36 @FlaggedApi(Flags.FLAG_USER_KEYS)
37 public class E2eeContactKeysDatabaseHelper extends SQLiteOpenHelper {
38 
39     private static final String TAG = "E2eeContactKeysDatabaseHelper";
40 
41     private static final String CONTACT_KEYS_TABLE_NAME = "contactkeys";
42     private static final String SELF_KEYS_TABLE_NAME = "selfkeys";
43     private static final String DATABASE_NAME = "contact_keys_provider.db";
44     private static final int DATABASE_VERSION = 1;
45 
E2eeContactKeysDatabaseHelper(Context context)46     E2eeContactKeysDatabaseHelper(Context context) {
47         super(context, DATABASE_NAME, null, DATABASE_VERSION);
48     }
49 
50     @Override
onCreate(SQLiteDatabase db)51     public void onCreate(SQLiteDatabase db) {
52         Log.i(TAG, "Bootstrapping database " + DATABASE_NAME + " version: " + DATABASE_VERSION);
53         db.execSQL("CREATE TABLE " + CONTACT_KEYS_TABLE_NAME + " ("
54                 + E2eeContactKeys.LOOKUP_KEY + " TEXT NOT NULL, "
55                 + E2eeContactKeys.DEVICE_ID + " TEXT NOT NULL, "
56                 + E2eeContactKeys.ACCOUNT_ID + " TEXT NOT NULL, "
57                 + E2eeContactKeys.OWNER_PACKAGE_NAME + " TEXT NOT NULL, "
58                 + E2eeContactKeys.TIME_UPDATED + " INTEGER NOT NULL default 0, "
59                 + E2eeContactKeys.KEY_VALUE + " BLOB NOT NULL, "
60                 + E2eeContactKeys.DISPLAY_NAME + " TEXT, "
61                 + E2eeContactKeys.PHONE_NUMBER + " TEXT, "
62                 + E2eeContactKeys.EMAIL_ADDRESS + " TEXT, "
63                 + E2eeContactKeys.LOCAL_VERIFICATION_STATE + " INTEGER NOT NULL default 0, "
64                 + E2eeContactKeys.REMOTE_VERIFICATION_STATE + " INTEGER NOT NULL default 0, "
65                 + " PRIMARY KEY ("
66                 + E2eeContactKeys.LOOKUP_KEY + ", "
67                 + E2eeContactKeys.DEVICE_ID + ", "
68                 + E2eeContactKeys.ACCOUNT_ID + ", "
69                 + E2eeContactKeys.OWNER_PACKAGE_NAME
70                 + "));");
71         db.execSQL("CREATE TABLE " + SELF_KEYS_TABLE_NAME + " ("
72                 + E2eeContactKeys.DEVICE_ID + " TEXT NOT NULL, "
73                 + E2eeContactKeys.ACCOUNT_ID + " TEXT NOT NULL, "
74                 + E2eeContactKeys.OWNER_PACKAGE_NAME + " TEXT NOT NULL, "
75                 + E2eeContactKeys.TIME_UPDATED + " INTEGER NOT NULL default 0, "
76                 + E2eeContactKeys.KEY_VALUE + " BLOB NOT NULL, "
77                 + E2eeContactKeys.REMOTE_VERIFICATION_STATE + " INTEGER NOT NULL default 0,"
78                 + " PRIMARY KEY ("
79                 + E2eeContactKeys.DEVICE_ID + ", "
80                 + E2eeContactKeys.ACCOUNT_ID + ", "
81                 + E2eeContactKeys.OWNER_PACKAGE_NAME
82                 + "));");
83     }
84 
85     @Override
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)86     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
87         // Nothing to do here, still on version 1
88     }
89 
90     /**
91      * Gets all end-to-end encryption contact keys in the contact keys table for a given lookupKey.
92      */
getAllContactKeys(String lookupKey)93     public List<E2eeContactKeysManager.E2eeContactKey> getAllContactKeys(String lookupKey) {
94         try (Cursor c = getReadableDatabase().rawQuery(
95                 "SELECT DISTINCT " + E2eeContactKeys.DEVICE_ID + ","
96                         + E2eeContactKeys.ACCOUNT_ID + ","
97                         + E2eeContactKeys.OWNER_PACKAGE_NAME + ","
98                         + E2eeContactKeys.TIME_UPDATED + ","
99                         + E2eeContactKeys.KEY_VALUE + ","
100                         + E2eeContactKeys.DISPLAY_NAME + ","
101                         + E2eeContactKeys.PHONE_NUMBER + ","
102                         + E2eeContactKeys.EMAIL_ADDRESS + ","
103                         + E2eeContactKeys.LOCAL_VERIFICATION_STATE + ","
104                         + E2eeContactKeys.REMOTE_VERIFICATION_STATE
105                         + " FROM " + CONTACT_KEYS_TABLE_NAME
106                         + " WHERE " + E2eeContactKeys.LOOKUP_KEY + " = ?",
107                 new String[] {lookupKey})) {
108             final List<E2eeContactKeysManager.E2eeContactKey> result =
109                     new ArrayList<>(c.getCount());
110             while (c.moveToNext()) {
111                 String deviceId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.DEVICE_ID));
112                 String accountId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.ACCOUNT_ID));
113                 String ownerPackageName = c.getString(
114                         c.getColumnIndexOrThrow(E2eeContactKeys.OWNER_PACKAGE_NAME));
115                 long timeUpdated = c.getLong(c.getColumnIndexOrThrow(E2eeContactKeys.TIME_UPDATED));
116                 byte[] keyValue = c.getBlob(c.getColumnIndexOrThrow(E2eeContactKeys.KEY_VALUE));
117                 String displayName = c.getString(c.getColumnIndexOrThrow(
118                         E2eeContactKeys.DISPLAY_NAME));
119                 String number = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.PHONE_NUMBER));
120                 String emailAddress = c.getString(c.getColumnIndexOrThrow(
121                         E2eeContactKeys.EMAIL_ADDRESS));
122                 int localVerificationState = c.getInt(c.getColumnIndexOrThrow(
123                         E2eeContactKeys.LOCAL_VERIFICATION_STATE));
124                 int remoteVerificationState = c.getInt(c.getColumnIndexOrThrow(
125                         E2eeContactKeys.REMOTE_VERIFICATION_STATE));
126                 result.add(new E2eeContactKeysManager.E2eeContactKey(deviceId, accountId,
127                         ownerPackageName, timeUpdated, keyValue, localVerificationState,
128                         remoteVerificationState, displayName, number, emailAddress));
129             }
130             return result;
131         }
132     }
133 
134     /**
135      * Gets all end-to-end encryption contact keys in the contact keys table for a given lookupKey
136      * and ownerPackageName.
137      */
getContactKeysForOwnerPackageName( String lookupKey, String ownerPackageName)138     public List<E2eeContactKeysManager.E2eeContactKey> getContactKeysForOwnerPackageName(
139             String lookupKey, String ownerPackageName) {
140         try (Cursor c = getReadableDatabase().rawQuery(
141                 "SELECT DISTINCT " + E2eeContactKeys.DEVICE_ID + ","
142                         + E2eeContactKeys.ACCOUNT_ID + ","
143                         + E2eeContactKeys.TIME_UPDATED + ","
144                         + E2eeContactKeys.KEY_VALUE + ","
145                         + E2eeContactKeys.DISPLAY_NAME + ","
146                         + E2eeContactKeys.PHONE_NUMBER + ","
147                         + E2eeContactKeys.EMAIL_ADDRESS + ","
148                         + E2eeContactKeys.LOCAL_VERIFICATION_STATE + ","
149                         + E2eeContactKeys.REMOTE_VERIFICATION_STATE
150                         + " FROM " + CONTACT_KEYS_TABLE_NAME
151                         + " WHERE " + E2eeContactKeys.LOOKUP_KEY + " = ?"
152                         + " AND " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?",
153                 new String[] {lookupKey, String.valueOf(ownerPackageName)})) {
154             final List<E2eeContactKeysManager.E2eeContactKey> result =
155                     new ArrayList<>(c.getCount());
156             while (c.moveToNext()) {
157                 String deviceId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.DEVICE_ID));
158                 String accountId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.ACCOUNT_ID));
159                 long timeUpdated = c.getLong(c.getColumnIndexOrThrow(E2eeContactKeys.TIME_UPDATED));
160                 byte[] keyValue = c.getBlob(c.getColumnIndexOrThrow(E2eeContactKeys.KEY_VALUE));
161                 String displayName = c.getString(c.getColumnIndexOrThrow(
162                         E2eeContactKeys.DISPLAY_NAME));
163                 String number = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.PHONE_NUMBER));
164                 String emailAddress = c.getString(c.getColumnIndexOrThrow(
165                         E2eeContactKeys.EMAIL_ADDRESS));
166                 int localVerificationState = c.getInt(c.getColumnIndexOrThrow(
167                         E2eeContactKeys.LOCAL_VERIFICATION_STATE));
168                 int remoteVerificationState = c.getInt(c.getColumnIndexOrThrow(
169                         E2eeContactKeys.REMOTE_VERIFICATION_STATE));
170                 result.add(new E2eeContactKeysManager.E2eeContactKey(deviceId, accountId,
171                         ownerPackageName, timeUpdated, keyValue, localVerificationState,
172                         remoteVerificationState, displayName, number, emailAddress));
173             }
174             return result;
175         }
176     }
177 
178     /**
179      * Get a end-to-end encryption contact key for given parameters.
180      */
getContactKey(String lookupKey, String ownerPackageName, String deviceId, String accountId)181     public E2eeContactKeysManager.E2eeContactKey getContactKey(String lookupKey,
182             String ownerPackageName, String deviceId, String accountId) {
183         E2eeContactKeysManager.E2eeContactKey result = null;
184         try (Cursor c = getReadableDatabase().rawQuery(
185                 "SELECT " + E2eeContactKeys.KEY_VALUE + ","
186                         + E2eeContactKeys.TIME_UPDATED + ","
187                         + E2eeContactKeys.DISPLAY_NAME + ","
188                         + E2eeContactKeys.PHONE_NUMBER + ","
189                         + E2eeContactKeys.EMAIL_ADDRESS + ","
190                         + E2eeContactKeys.LOCAL_VERIFICATION_STATE + ","
191                         + E2eeContactKeys.REMOTE_VERIFICATION_STATE + " FROM "
192                         + CONTACT_KEYS_TABLE_NAME
193                         + " WHERE " + E2eeContactKeys.LOOKUP_KEY + " = ?"
194                         + " AND " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?"
195                         + " AND " + E2eeContactKeys.DEVICE_ID + " = ?"
196                         + " AND " + E2eeContactKeys.ACCOUNT_ID + " = ?",
197                 new String[] {lookupKey, String.valueOf(ownerPackageName), deviceId, accountId})) {
198             if (c.moveToNext()) {
199                 byte[] keyValue = c.getBlob(c.getColumnIndexOrThrow(E2eeContactKeys.KEY_VALUE));
200                 long timeUpdated = c.getLong(c.getColumnIndexOrThrow(E2eeContactKeys.TIME_UPDATED));
201                 String displayName = c.getString(c.getColumnIndexOrThrow(
202                         E2eeContactKeys.DISPLAY_NAME));
203                 String number = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.PHONE_NUMBER));
204                 String emailAddress = c.getString(c.getColumnIndexOrThrow(
205                         E2eeContactKeys.EMAIL_ADDRESS));
206                 int localVerificationState = c.getInt(c.getColumnIndexOrThrow(
207                         E2eeContactKeys.LOCAL_VERIFICATION_STATE));
208                 int remoteVerificationState = c.getInt(c.getColumnIndexOrThrow(
209                         E2eeContactKeys.REMOTE_VERIFICATION_STATE));
210                 result = new E2eeContactKeysManager.E2eeContactKey(deviceId, accountId,
211                         ownerPackageName, timeUpdated, keyValue, localVerificationState,
212                         remoteVerificationState, displayName, number, emailAddress);
213             }
214         }
215 
216         return result;
217     }
218 
219     /**
220      * Updates the end-to-end encryption contact key local verification state and returns the
221      * number of keys updated (should always be 0 or 1).
222      */
updateContactKeyLocalVerificationState(String lookupKey, String ownerPackageName, String deviceId, String accountId, int localVerificationState, long timeUpdated)223     public int updateContactKeyLocalVerificationState(String lookupKey, String ownerPackageName,
224             String deviceId, String accountId, int localVerificationState, long timeUpdated) {
225         try (SQLiteStatement updateStatement = getWritableDatabase().compileStatement(
226                 "UPDATE " + CONTACT_KEYS_TABLE_NAME
227                         + " SET " + E2eeContactKeys.LOCAL_VERIFICATION_STATE
228                         + " = ?, "
229                         + E2eeContactKeys.TIME_UPDATED + " = ?"
230                         + " WHERE " + E2eeContactKeys.LOOKUP_KEY + " = ?"
231                         + " AND " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?"
232                         + " AND " + E2eeContactKeys.DEVICE_ID + " = ?"
233                         + " AND " + E2eeContactKeys.ACCOUNT_ID + " = ?")) {
234             updateStatement.bindLong(1, localVerificationState);
235             updateStatement.bindLong(2, timeUpdated);
236             updateStatement.bindString(3, lookupKey);
237             updateStatement.bindString(4, ownerPackageName);
238             updateStatement.bindString(5, deviceId);
239             updateStatement.bindString(6, accountId);
240             return updateStatement.executeUpdateDelete();
241         }
242     }
243 
244     /**
245      * Updates the end-to-end encryption contact key remote verification state and returns the
246      * number of keys updated (should always be 0 or 1).
247      */
updateContactKeyRemoteVerificationState(String lookupKey, String ownerPackageName, String deviceId, String accountId, int remoteVerificationState, long timeUpdated)248     public int updateContactKeyRemoteVerificationState(String lookupKey, String ownerPackageName,
249             String deviceId, String accountId, int remoteVerificationState, long timeUpdated) {
250         try (SQLiteStatement updateStatement = getWritableDatabase().compileStatement(
251                 "UPDATE " + CONTACT_KEYS_TABLE_NAME
252                         + " SET " + E2eeContactKeys.REMOTE_VERIFICATION_STATE
253                         + " = ?, "
254                         + E2eeContactKeys.TIME_UPDATED + " = ?"
255                         + " WHERE " + E2eeContactKeys.LOOKUP_KEY + " = ?"
256                         + " AND " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?"
257                         + " AND " + E2eeContactKeys.DEVICE_ID + " = ?"
258                         + " AND " + E2eeContactKeys.ACCOUNT_ID + " = ?")) {
259             updateStatement.bindLong(1, remoteVerificationState);
260             updateStatement.bindLong(2, timeUpdated);
261             updateStatement.bindString(3, lookupKey);
262             updateStatement.bindString(4, ownerPackageName);
263             updateStatement.bindString(5, deviceId);
264             updateStatement.bindString(6, accountId);
265             return updateStatement.executeUpdateDelete();
266         }
267     }
268 
269     /**
270      * Inserts a new end-to-end encryption contact key or updates an existing one and returns the
271      * number of keys inserted/updated (should always be 0 or 1).
272      */
updateOrInsertContactKey(String lookupKey, byte[] keyValue, String deviceId, String accountId, String ownerPackageName, long timeUpdated, String displayName, String number, String emailAddress)273     public int updateOrInsertContactKey(String lookupKey, byte[] keyValue, String deviceId,
274             String accountId, String ownerPackageName, long timeUpdated,
275             String displayName, String number, String emailAddress) {
276         try (SQLiteStatement updateStatement = getWritableDatabase().compileStatement(
277                 "INSERT INTO " + CONTACT_KEYS_TABLE_NAME + " ("
278                         + E2eeContactKeys.LOOKUP_KEY + ", "
279                         + E2eeContactKeys.DEVICE_ID + ", "
280                         + E2eeContactKeys.ACCOUNT_ID + ", "
281                         + E2eeContactKeys.OWNER_PACKAGE_NAME + ", "
282                         + E2eeContactKeys.KEY_VALUE + ", "
283                         + E2eeContactKeys.TIME_UPDATED + ", "
284                         + E2eeContactKeys.DISPLAY_NAME + ", "
285                         + E2eeContactKeys.PHONE_NUMBER + ", "
286                         + E2eeContactKeys.EMAIL_ADDRESS
287                         + ") VALUES "
288                         + "(?, ?, ?, ?, ?, ?, ?, ?, ?) "
289                         + "ON CONFLICT ("
290                         + E2eeContactKeys.LOOKUP_KEY + ", "
291                         + E2eeContactKeys.DEVICE_ID + ", "
292                         + E2eeContactKeys.ACCOUNT_ID + ", "
293                         + E2eeContactKeys.OWNER_PACKAGE_NAME + ") "
294                         + "DO UPDATE SET " + E2eeContactKeys.KEY_VALUE + " = ?, "
295                         + E2eeContactKeys.TIME_UPDATED + " = ?, "
296                         + E2eeContactKeys.DISPLAY_NAME + " = ?, "
297                         + E2eeContactKeys.PHONE_NUMBER + " = ?, "
298                         + E2eeContactKeys.EMAIL_ADDRESS + " = ?")) {
299             updateStatement.bindString(1, lookupKey);
300             updateStatement.bindString(2, deviceId);
301             updateStatement.bindString(3, accountId);
302             updateStatement.bindString(4, ownerPackageName);
303             updateStatement.bindBlob(5, keyValue);
304             updateStatement.bindLong(6, timeUpdated);
305             if (displayName != null) {
306                 updateStatement.bindString(7, displayName);
307             } else {
308                 updateStatement.bindNull(7);
309             }
310             if (number != null) {
311                 updateStatement.bindString(8, number);
312             } else {
313                 updateStatement.bindNull(8);
314             }
315             if (emailAddress != null) {
316                 updateStatement.bindString(9, emailAddress);
317             } else {
318                 updateStatement.bindNull(9);
319             }
320             updateStatement.bindBlob(10, keyValue);
321             updateStatement.bindLong(11, timeUpdated);
322             if (displayName != null) {
323                 updateStatement.bindString(12, displayName);
324             } else {
325                 updateStatement.bindNull(12);
326             }
327             if (number != null) {
328                 updateStatement.bindString(13, number);
329             } else {
330                 updateStatement.bindNull(13);
331             }
332             if (emailAddress != null) {
333                 updateStatement.bindString(14, emailAddress);
334             } else {
335                 updateStatement.bindNull(14);
336             }
337             return updateStatement.executeUpdateDelete();
338         }
339     }
340 
341     /**
342      * Removes the end-to-end encryption contact key and returns the number of keys removed
343      * (should always be 0 or 1).
344      */
removeContactKey(String lookupKey, String deviceId, String accountId, String ownerPackageName)345     public int removeContactKey(String lookupKey, String deviceId, String accountId,
346             String ownerPackageName) {
347         try (SQLiteStatement updateStatement = getWritableDatabase().compileStatement(
348                 "DELETE FROM " + CONTACT_KEYS_TABLE_NAME
349                         + " WHERE " + E2eeContactKeys.LOOKUP_KEY + " = ?"
350                         + " AND " + E2eeContactKeys.DEVICE_ID + " = ?"
351                         + " AND " + E2eeContactKeys.ACCOUNT_ID + " = ?"
352                         + " AND " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?")) {
353             updateStatement.bindString(1, lookupKey);
354             updateStatement.bindString(2, deviceId);
355             updateStatement.bindString(3, accountId);
356             updateStatement.bindString(4, ownerPackageName);
357             return updateStatement.executeUpdateDelete();
358         }
359     }
360 
361     /**
362      * Gets an end-to-end encryption self key for given parameters.
363      */
getSelfKey(String deviceId, String accountId, String ownerPackageName)364     public E2eeContactKeysManager.E2eeSelfKey getSelfKey(String deviceId, String accountId,
365             String ownerPackageName) {
366         E2eeContactKeysManager.E2eeSelfKey result = null;
367         try (Cursor c = getReadableDatabase().rawQuery(
368                 "SELECT " + E2eeContactKeys.KEY_VALUE + ","
369                         + E2eeContactKeys.TIME_UPDATED + ","
370                         + E2eeContactKeys.REMOTE_VERIFICATION_STATE + " FROM "
371                         + SELF_KEYS_TABLE_NAME
372                         + " WHERE " + E2eeContactKeys.DEVICE_ID + " = ?"
373                         + " AND " + E2eeContactKeys.ACCOUNT_ID + " = ?"
374                         + " AND " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?",
375                 new String[] {deviceId, accountId, ownerPackageName})) {
376             if (c.moveToNext()) {
377                 byte[] keyValue = c.getBlob(c.getColumnIndexOrThrow(E2eeContactKeys.KEY_VALUE));
378                 long timeUpdated = c.getLong(c.getColumnIndexOrThrow(E2eeContactKeys.TIME_UPDATED));
379                 int remoteVerificationState = c.getInt(c.getColumnIndexOrThrow(
380                         E2eeContactKeys.REMOTE_VERIFICATION_STATE));
381                 result = new E2eeContactKeysManager.E2eeSelfKey(deviceId, accountId,
382                         ownerPackageName, timeUpdated, keyValue, remoteVerificationState);
383             }
384         }
385         return result;
386     }
387 
388     /**
389      * Gets all end-to-end encryption self keys in the self keys table.
390      */
getAllSelfKeys()391     public List<E2eeContactKeysManager.E2eeSelfKey> getAllSelfKeys() {
392         try (Cursor c = getReadableDatabase().rawQuery(
393                 "SELECT DISTINCT " + E2eeContactKeys.DEVICE_ID + ","
394                         + E2eeContactKeys.ACCOUNT_ID + ","
395                         + E2eeContactKeys.OWNER_PACKAGE_NAME + ","
396                         + E2eeContactKeys.TIME_UPDATED + ","
397                         + E2eeContactKeys.KEY_VALUE + ","
398                         + E2eeContactKeys.REMOTE_VERIFICATION_STATE
399                         + " FROM " + SELF_KEYS_TABLE_NAME,
400                 new String[] {})) {
401             final List<E2eeContactKeysManager.E2eeSelfKey> result = new ArrayList<>(c.getCount());
402             while (c.moveToNext()) {
403                 String deviceId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.DEVICE_ID));
404                 String accountId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.ACCOUNT_ID));
405                 String ownerPackageName = c.getString(
406                         c.getColumnIndexOrThrow(E2eeContactKeys.OWNER_PACKAGE_NAME));
407                 long timeUpdated = c.getLong(c.getColumnIndexOrThrow(E2eeContactKeys.TIME_UPDATED));
408                 byte[] keyValue = c.getBlob(c.getColumnIndexOrThrow(E2eeContactKeys.KEY_VALUE));
409                 int remoteVerificationState = c.getInt(c.getColumnIndexOrThrow(
410                         E2eeContactKeys.REMOTE_VERIFICATION_STATE));
411                 result.add(new E2eeContactKeysManager.E2eeSelfKey(deviceId, accountId,
412                         ownerPackageName, timeUpdated, keyValue, remoteVerificationState));
413             }
414             return result;
415         }
416     }
417 
418     /**
419      * Gets all end-to-end encryption self keys in the self keys table for a given ownerPackageName.
420      */
getSelfKeysForOwnerPackageName( String ownerPackageName)421     public List<E2eeContactKeysManager.E2eeSelfKey> getSelfKeysForOwnerPackageName(
422             String ownerPackageName) {
423         try (Cursor c = getReadableDatabase().rawQuery(
424                 "SELECT DISTINCT " + E2eeContactKeys.DEVICE_ID + ","
425                         + E2eeContactKeys.ACCOUNT_ID + ","
426                         + E2eeContactKeys.TIME_UPDATED + ","
427                         + E2eeContactKeys.KEY_VALUE + ","
428                         + E2eeContactKeys.REMOTE_VERIFICATION_STATE
429                         + " FROM " + SELF_KEYS_TABLE_NAME
430                         + " WHERE " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?",
431                 new String[] {ownerPackageName})) {
432             final List<E2eeContactKeysManager.E2eeSelfKey> result = new ArrayList<>(c.getCount());
433             while (c.moveToNext()) {
434                 String deviceId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.DEVICE_ID));
435                 String accountId = c.getString(c.getColumnIndexOrThrow(E2eeContactKeys.ACCOUNT_ID));
436                 long timeUpdated = c.getLong(c.getColumnIndexOrThrow(E2eeContactKeys.TIME_UPDATED));
437                 byte[] keyValue = c.getBlob(c.getColumnIndexOrThrow(E2eeContactKeys.KEY_VALUE));
438                 int remoteVerificationState = c.getInt(c.getColumnIndexOrThrow(
439                         E2eeContactKeys.REMOTE_VERIFICATION_STATE));
440                 result.add(new E2eeContactKeysManager.E2eeSelfKey(deviceId, accountId,
441                         ownerPackageName, timeUpdated, keyValue, remoteVerificationState));
442             }
443             return result;
444         }
445     }
446 
447     /**
448      * Inserts a new end-to-end encryption self key or updates an existing one and returns the
449      * number of keys inserted/updated (should always be 0 or 1).
450      */
updateOrInsertSelfKey(byte[] keyValue, String deviceId, String accountId, String ownerPackageName, long timeUpdated)451     public int updateOrInsertSelfKey(byte[] keyValue, String deviceId, String accountId,
452             String ownerPackageName, long timeUpdated) {
453         try (SQLiteStatement updateStatement = getWritableDatabase().compileStatement(
454                 "INSERT INTO " + SELF_KEYS_TABLE_NAME + " ("
455                         + E2eeContactKeys.DEVICE_ID + ", "
456                         + E2eeContactKeys.ACCOUNT_ID + ", "
457                         + E2eeContactKeys.OWNER_PACKAGE_NAME + ", "
458                         + E2eeContactKeys.KEY_VALUE + ", "
459                         + E2eeContactKeys.TIME_UPDATED
460                         + ") VALUES "
461                         + "(?, ?, ?, ?, ?) "
462                         + "ON CONFLICT ("
463                         + E2eeContactKeys.DEVICE_ID + ", "
464                         + E2eeContactKeys.ACCOUNT_ID + ", "
465                         + E2eeContactKeys.OWNER_PACKAGE_NAME + ") "
466                         + "DO UPDATE SET " + E2eeContactKeys.KEY_VALUE + " = ?, "
467                         + E2eeContactKeys.TIME_UPDATED + " = ?")) {
468             updateStatement.bindString(1, deviceId);
469             updateStatement.bindString(2, accountId);
470             updateStatement.bindString(3, ownerPackageName);
471             updateStatement.bindBlob(4, keyValue);
472             updateStatement.bindLong(5, timeUpdated);
473             updateStatement.bindBlob(6, keyValue);
474             updateStatement.bindLong(7, timeUpdated);
475             return updateStatement.executeUpdateDelete();
476         }
477     }
478 
479     /**
480      * Updates the end-to-end encryption self key remote verification state and returns the number
481      * of keys updated (should always be 0 or 1).
482      */
updateSelfKeyRemoteVerificationState(String ownerPackageName, String deviceId, String accountId, int remoteVerificationState, long timeUpdated)483     public int updateSelfKeyRemoteVerificationState(String ownerPackageName, String deviceId,
484             String accountId, int remoteVerificationState, long timeUpdated) {
485         try (SQLiteStatement updateStatement = getWritableDatabase().compileStatement(
486                 "UPDATE " + SELF_KEYS_TABLE_NAME
487                         + " SET " + E2eeContactKeys.REMOTE_VERIFICATION_STATE + " = ?, "
488                         + E2eeContactKeys.TIME_UPDATED + " = ?"
489                         + " WHERE " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?"
490                         + " AND " + E2eeContactKeys.DEVICE_ID + " = ?"
491                         + " AND " + E2eeContactKeys.ACCOUNT_ID + " = ?")) {
492             updateStatement.bindLong(1, remoteVerificationState);
493             updateStatement.bindLong(2, timeUpdated);
494             updateStatement.bindString(3, ownerPackageName);
495             updateStatement.bindString(4, deviceId);
496             updateStatement.bindString(5, accountId);
497             return updateStatement.executeUpdateDelete();
498         }
499     }
500 
501     /**
502      * Removes a end-to-end encryption self key with given parameters.
503      */
removeSelfKey(String deviceId, String accountId, String ownerPackageName)504     public int removeSelfKey(String deviceId, String accountId, String ownerPackageName) {
505         try (SQLiteStatement updateStatement = getWritableDatabase().compileStatement(
506                 "DELETE FROM " + SELF_KEYS_TABLE_NAME
507                         + " WHERE " + E2eeContactKeys.DEVICE_ID + " = ?"
508                         + " AND " + E2eeContactKeys.ACCOUNT_ID + " = ?"
509                         + " AND " + E2eeContactKeys.OWNER_PACKAGE_NAME + " = ?")) {
510             updateStatement.bindString(1, deviceId);
511             updateStatement.bindString(2, accountId);
512             updateStatement.bindString(3, ownerPackageName);
513             return updateStatement.executeUpdateDelete();
514         }
515     }
516 }
517