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