1 /* 2 * Copyright (C) 2024 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 android.provider; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.content.ContentProviderClient; 26 import android.content.ContentResolver; 27 import android.content.Context; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.os.RemoteException; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.Objects; 40 41 /** 42 * E2eeContactKeysManager provides access to the provider of end-to-end encryption contact keys. 43 * It manages two types of keys - {@link E2eeContactKey} and {@link E2eeSelfKey}. 44 * <ul> 45 * <li> 46 * A {@link E2eeContactKey} is a public key associated with a contact. It's used to end-to-end 47 * encrypt the communications between a user and the contact. This API allows operations on 48 * {@link E2eeContactKey}s to insert/update, remove, change the verification state, and retrieving 49 * keys (either created by or visible to the caller app). 50 * </li> 51 * <li> 52 * A {@link E2eeSelfKey} is a key for this device, so the key represents the owner of the device. 53 * This API allows operations on {@link E2eeSelfKey}s to insert/update, remove, and retrieving 54 * self keys (either created by or visible to the caller app). 55 * </li> 56 * </ul> 57 * Keys are uniquely identified by: 58 * <ul> 59 * <li> 60 * ownerPackageName - package name of an app that the key belongs to. 61 * </li> 62 * <li> 63 * deviceId - an app-specified identifier for the device, defined by the app. Within that app, 64 * the deviceID should be unique among devices belonging to that user. 65 * </li> 66 * <li> 67 * accountId - the app-specified identifier for the account for which the contact key can be used. 68 * Using different account IDs allows for multiple key entries representing the same user. 69 * For most apps this would be a phone number. 70 * </li> 71 * </ul> 72 * Contact keys also use lookupKey which is an opaque value used to identify a contact in 73 * ContactsProvider. 74 */ 75 @FlaggedApi(Flags.FLAG_USER_KEYS) 76 public final class E2eeContactKeysManager { 77 /** 78 * The authority for the end-to-end encryption contact keys provider. 79 * 80 * @hide 81 */ 82 public static final String AUTHORITY = "com.android.contactkeys.contactkeysprovider"; 83 84 /** 85 * A content:// style uri to the authority for the end-to-end encryption contact keys provider. 86 * 87 * @hide 88 */ 89 @NonNull 90 public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY); 91 92 /** 93 * Maximum size of an end-to-end encryption contact key. 94 */ 95 private static final int MAX_KEY_SIZE_BYTES = 5000; 96 97 /** 98 * Special value to distinguish a null array in a parcelable object. 99 */ 100 private static final int ARRAY_IS_NULL = -1; 101 102 @NonNull 103 private final ContentResolver mContentResolver; 104 105 /** @hide */ E2eeContactKeysManager(@onNull Context context)106 public E2eeContactKeysManager(@NonNull Context context) { 107 Objects.requireNonNull(context); 108 mContentResolver = context.getContentResolver(); 109 } 110 111 /** 112 * Inserts a new entry into the end-to-end encryption contact keys table or updates one if it 113 * already exists. 114 * The inserted/updated end-to-end encryption contact key is owned by the caller app. 115 * 116 * @param lookupKey value that references the contact 117 * @param deviceId an app-specified identifier for the device 118 * @param accountId an app-specified identifier for the account 119 * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes) 120 */ 121 @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) updateOrInsertE2eeContactKey(@onNull String lookupKey, @NonNull String deviceId, @NonNull String accountId, @NonNull byte[] keyValue)122 public void updateOrInsertE2eeContactKey(@NonNull String lookupKey, 123 @NonNull String deviceId, 124 @NonNull String accountId, 125 @NonNull byte[] keyValue) { 126 validateKeyLength(keyValue); 127 128 Bundle extras = new Bundle(); 129 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 130 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 131 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 132 extras.putByteArray(E2eeContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue)); 133 134 nullSafeCall(mContentResolver, 135 E2eeContactKeys.UPDATE_OR_INSERT_CONTACT_KEY_METHOD, extras); 136 } 137 138 /** 139 * Retrieves an end-to-end encryption contact key entry given the lookup key, device ID, 140 * accountId and inferred caller package name. 141 * 142 * @param lookupKey the value that references the contact 143 * @param deviceId an app-specified identifier for the device 144 * @param accountId an app-specified identifier for the account 145 * @return a {@link E2eeContactKey} object containing the contact key information, 146 * or null if no contact key is found. 147 */ 148 @RequiresPermission(android.Manifest.permission.READ_CONTACTS) 149 @Nullable getE2eeContactKey( @onNull String lookupKey, @NonNull String deviceId, @NonNull String accountId)150 public E2eeContactKey getE2eeContactKey( 151 @NonNull String lookupKey, 152 @NonNull String deviceId, 153 @NonNull String accountId) { 154 Bundle extras = new Bundle(); 155 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 156 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 157 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 158 159 Bundle response = nullSafeCall(mContentResolver, 160 E2eeContactKeys.GET_CONTACT_KEY_METHOD, extras); 161 162 if (response == null) { 163 return null; 164 } 165 return response.getParcelable(E2eeContactKeys.KEY_CONTACT_KEY, E2eeContactKey.class); 166 } 167 168 /** 169 * Retrieves all end-to-end encryption contact key entries that belong to apps visible to 170 * the caller. 171 * The keys will be stripped of deviceId, timeUpdated and keyValue data. 172 * 173 * @param lookupKey the value that references the contact 174 * @return a list of {@link E2eeContactKey} objects containing the contact key 175 * information, or an empty list if no keys are found. 176 */ 177 @RequiresPermission(android.Manifest.permission.READ_CONTACTS) 178 @NonNull getAllE2eeContactKeys(@onNull String lookupKey)179 public List<E2eeContactKey> getAllE2eeContactKeys(@NonNull String lookupKey) { 180 Bundle extras = new Bundle(); 181 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 182 183 Bundle response = nullSafeCall(mContentResolver, 184 E2eeContactKeys.GET_ALL_CONTACT_KEYS_METHOD, extras); 185 186 if (response == null) { 187 return new ArrayList<>(); 188 } 189 List<E2eeContactKey> value = response.getParcelableArrayList( 190 E2eeContactKeys.KEY_CONTACT_KEYS, E2eeContactKey.class); 191 if (value == null) { 192 return new ArrayList<>(); 193 } 194 return value; 195 } 196 197 /** 198 * Retrieves all end-to-end encryption contact key entries for a given lookupKey that belong to 199 * the caller app. 200 * 201 * @param lookupKey the value that references the contact 202 * @return a list of {@link E2eeContactKey} objects containing the end-to-end encryption 203 * contact key information, or an empty list if no keys are found. 204 */ 205 @RequiresPermission(android.Manifest.permission.READ_CONTACTS) 206 @NonNull getOwnerE2eeContactKeys(@onNull String lookupKey)207 public List<E2eeContactKey> getOwnerE2eeContactKeys(@NonNull String lookupKey) { 208 Bundle extras = new Bundle(); 209 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 210 211 Bundle response = nullSafeCall(mContentResolver, 212 E2eeContactKeys.GET_OWNER_CONTACT_KEYS_METHOD, extras); 213 214 if (response == null) { 215 return new ArrayList<>(); 216 } 217 List<E2eeContactKey> value = response.getParcelableArrayList( 218 E2eeContactKeys.KEY_CONTACT_KEYS, E2eeContactKey.class); 219 if (value == null) { 220 return new ArrayList<>(); 221 } 222 return value; 223 } 224 225 /** 226 * Updates an end-to-end encryption contact key entry's local verification state that belongs 227 * to the caller app. 228 * 229 * @param lookupKey the value that references the contact 230 * @param deviceId an app-specified identifier for the device 231 * @param accountId an app-specified identifier for the account 232 * @param localVerificationState the new local verification state 233 * @return true if the entry was updated, false otherwise. 234 */ 235 @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) updateE2eeContactKeyLocalVerificationState(@onNull String lookupKey, @NonNull String deviceId, @NonNull String accountId, @VerificationState int localVerificationState)236 public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String lookupKey, 237 @NonNull String deviceId, 238 @NonNull String accountId, 239 @VerificationState int localVerificationState) { 240 validateVerificationState(localVerificationState); 241 242 Bundle extras = new Bundle(); 243 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 244 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 245 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 246 extras.putInt(E2eeContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState); 247 248 Bundle response = nullSafeCall(mContentResolver, 249 E2eeContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras); 250 251 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 252 } 253 254 /** 255 * Updates an end-to-end encryption contact key entry's local verification state that belongs 256 * to the app identified by ownerPackageName. 257 * 258 * @param lookupKey the value that references the contact 259 * @param deviceId an app-specified identifier for the device 260 * @param accountId an app-specified identifier for the account 261 * @param ownerPackageName the package name of the app that owns the key 262 * @param localVerificationState the new local verification state 263 * @return true if the entry was updated, false otherwise. 264 * @hide 265 */ 266 @SystemApi 267 @RequiresPermission(allOf = { 268 android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, 269 android.Manifest.permission.WRITE_CONTACTS}) updateE2eeContactKeyLocalVerificationState(@onNull String lookupKey, @NonNull String deviceId, @NonNull String accountId, @NonNull String ownerPackageName, @VerificationState int localVerificationState)270 public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String lookupKey, 271 @NonNull String deviceId, 272 @NonNull String accountId, 273 @NonNull String ownerPackageName, 274 @VerificationState int localVerificationState) { 275 validateVerificationState(localVerificationState); 276 277 final Bundle extras = new Bundle(); 278 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 279 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 280 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 281 extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME, 282 Objects.requireNonNull(ownerPackageName)); 283 extras.putInt(E2eeContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState); 284 285 final Bundle response = nullSafeCall(mContentResolver, 286 E2eeContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras); 287 288 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 289 } 290 291 /** 292 * Updates an end-to-end encryption contact key entry's remote verification state that belongs 293 * to the caller app. 294 * 295 * @param lookupKey the value that references the contact 296 * @param deviceId an app-specified identifier for the device 297 * @param accountId an app-specified identifier for the account 298 * @param remoteVerificationState the new remote verification state 299 * @return true if the entry was updated, false otherwise. 300 */ 301 @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) updateE2eeContactKeyRemoteVerificationState(@onNull String lookupKey, @NonNull String deviceId, @NonNull String accountId, @VerificationState int remoteVerificationState)302 public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String lookupKey, 303 @NonNull String deviceId, 304 @NonNull String accountId, 305 @VerificationState int remoteVerificationState) { 306 validateVerificationState(remoteVerificationState); 307 308 Bundle extras = new Bundle(); 309 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 310 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 311 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 312 extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState); 313 314 Bundle response = nullSafeCall(mContentResolver, 315 E2eeContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras); 316 317 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 318 } 319 320 /** 321 * Updates an end-to-end encryption contact key entry's remote verification state that belongs 322 * to the app identified by ownerPackageName. 323 * 324 * @param lookupKey the value that references the contact 325 * @param deviceId an app-specified identifier for the device 326 * @param accountId an app-specified identifier for the account 327 * @param ownerPackageName the package name of the app that owns the key 328 * @param remoteVerificationState the new remote verification state 329 * @return true if the entry was updated, false otherwise. 330 * @hide 331 */ 332 @SystemApi 333 @RequiresPermission(allOf = { 334 android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, 335 android.Manifest.permission.WRITE_CONTACTS}) updateE2eeContactKeyRemoteVerificationState(@onNull String lookupKey, @NonNull String deviceId, @NonNull String accountId, @NonNull String ownerPackageName, @VerificationState int remoteVerificationState)336 public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String lookupKey, 337 @NonNull String deviceId, 338 @NonNull String accountId, 339 @NonNull String ownerPackageName, 340 @VerificationState int remoteVerificationState) { 341 validateVerificationState(remoteVerificationState); 342 343 final Bundle extras = new Bundle(); 344 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 345 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 346 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 347 extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME, 348 Objects.requireNonNull(ownerPackageName)); 349 extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState); 350 351 final Bundle response = nullSafeCall(mContentResolver, 352 E2eeContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras); 353 354 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 355 } 356 validateVerificationState(int verificationState)357 private static void validateVerificationState(int verificationState) { 358 if (verificationState != VERIFICATION_STATE_UNVERIFIED 359 && verificationState != VERIFICATION_STATE_VERIFICATION_FAILED 360 && verificationState != VERIFICATION_STATE_VERIFIED) { 361 throw new IllegalArgumentException("Verification state value " 362 + verificationState + " is not supported"); 363 } 364 } 365 366 /** 367 * Removes an end-to-end encryption contact key entry that belongs to the caller app. 368 * 369 * @param lookupKey the value that references the contact 370 * @param deviceId an app-specified identifier for the device 371 * @param accountId an app-specified identifier for the account 372 * @return true if the entry was removed, false otherwise. 373 */ 374 @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) removeE2eeContactKey(@onNull String lookupKey, @NonNull String deviceId, @NonNull String accountId)375 public boolean removeE2eeContactKey(@NonNull String lookupKey, 376 @NonNull String deviceId, 377 @NonNull String accountId) { 378 final Bundle extras = new Bundle(); 379 extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey)); 380 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 381 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 382 383 final Bundle response = nullSafeCall(mContentResolver, 384 E2eeContactKeys.REMOVE_CONTACT_KEY_METHOD, extras); 385 386 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 387 } 388 389 /** 390 * Inserts a new entry into the end-to-end encryption self keys table or updates one if it 391 * already exists. 392 * 393 * @param deviceId an app-specified identifier for the device 394 * @param accountId an app-specified identifier for the account 395 * @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes) 396 * @return true if the entry was added or updated, false otherwise. 397 */ 398 @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) updateOrInsertE2eeSelfKey(@onNull String deviceId, @NonNull String accountId, @NonNull byte[] keyValue)399 public boolean updateOrInsertE2eeSelfKey(@NonNull String deviceId, 400 @NonNull String accountId, 401 @NonNull byte[] keyValue) { 402 validateKeyLength(keyValue); 403 404 Bundle extras = new Bundle(); 405 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 406 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 407 extras.putByteArray(E2eeContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue)); 408 409 Bundle response = nullSafeCall(mContentResolver, 410 E2eeContactKeys.UPDATE_OR_INSERT_SELF_KEY_METHOD, extras); 411 412 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 413 } 414 validateKeyLength(byte[] keyValue)415 private static void validateKeyLength(byte[] keyValue) { 416 Objects.requireNonNull(keyValue); 417 if (keyValue.length == 0 || keyValue.length > getMaxKeySizeBytes()) { 418 throw new IllegalArgumentException("Key value length is " + keyValue.length + "." 419 + " Should be more than 0 and less than " + getMaxKeySizeBytes()); 420 } 421 } 422 423 /** 424 * Updates an end-to-end encryption self key entry's remote verification state. 425 * 426 * @param deviceId an app-specified identifier for the device 427 * @param accountId an app-specified identifier for the account 428 * @param remoteVerificationState the new remote verification state 429 * @return true if the entry was updated, false otherwise. 430 */ 431 @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) updateE2eeSelfKeyRemoteVerificationState(@onNull String deviceId, @NonNull String accountId, @VerificationState int remoteVerificationState)432 public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String deviceId, 433 @NonNull String accountId, 434 @VerificationState int remoteVerificationState) { 435 validateVerificationState(remoteVerificationState); 436 437 Bundle extras = new Bundle(); 438 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 439 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 440 extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState); 441 442 Bundle response = nullSafeCall(mContentResolver, 443 E2eeContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras); 444 445 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 446 } 447 448 /** 449 * Updates an end-to-end encryption self key entry's remote verification state that belongs to 450 * the app identified by ownerPackageName. 451 * 452 * @param deviceId an app-specified identifier for the device 453 * @param accountId an app-specified identifier for the account 454 * @param ownerPackageName the package name of the app that owns the key 455 * @param remoteVerificationState the new remote verification state 456 * @return true if the entry was updated, false otherwise. 457 * @hide 458 */ 459 @SystemApi 460 @RequiresPermission(allOf = { 461 android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, 462 android.Manifest.permission.WRITE_CONTACTS}) updateE2eeSelfKeyRemoteVerificationState(@onNull String deviceId, @NonNull String accountId, @NonNull String ownerPackageName, @VerificationState int remoteVerificationState)463 public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String deviceId, 464 @NonNull String accountId, 465 @NonNull String ownerPackageName, 466 @VerificationState int remoteVerificationState) { 467 validateVerificationState(remoteVerificationState); 468 469 Bundle extras = new Bundle(); 470 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 471 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 472 extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME, 473 Objects.requireNonNull(ownerPackageName)); 474 extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState); 475 476 Bundle response = nullSafeCall(mContentResolver, 477 E2eeContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras); 478 479 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 480 } 481 482 /** 483 * Maximum size of an end-to-end encryption contact key. 484 */ getMaxKeySizeBytes()485 public static int getMaxKeySizeBytes() { 486 return MAX_KEY_SIZE_BYTES; 487 } 488 489 /** 490 * Returns an end-to-end encryption self key entry given the deviceId and the inferred package 491 * name of the caller. 492 * 493 * @param deviceId an app-specified identifier for the device 494 * @param accountId an app-specified identifier for the account 495 * @return a {@link E2eeSelfKey} object containing the end-to-end encryption self key 496 * information, or null if no self key is found. 497 */ 498 @RequiresPermission(android.Manifest.permission.READ_CONTACTS) 499 @Nullable getE2eeSelfKey(@onNull String deviceId, @NonNull String accountId)500 public E2eeSelfKey getE2eeSelfKey(@NonNull String deviceId, 501 @NonNull String accountId) { 502 Bundle extras = new Bundle(); 503 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 504 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 505 506 Bundle response = nullSafeCall(mContentResolver, 507 E2eeContactKeys.GET_SELF_KEY_METHOD, extras); 508 509 if (response == null) { 510 return null; 511 } 512 return response.getParcelable(E2eeContactKeys.KEY_CONTACT_KEY, E2eeSelfKey.class); 513 } 514 515 /** 516 * Returns all end-to-end encryption self key entries that belong to apps visible to the caller. 517 * The keys will be stripped of deviceId, timeUpdated and keyValue data. 518 * 519 * @return a list of {@link E2eeSelfKey} objects containing the end-to-end encryption self key 520 * information, or an empty list if no self keys are found. 521 */ 522 @RequiresPermission(android.Manifest.permission.READ_CONTACTS) 523 @NonNull getAllE2eeSelfKeys()524 public List<E2eeSelfKey> getAllE2eeSelfKeys() { 525 Bundle extras = new Bundle(); 526 527 Bundle response = nullSafeCall(mContentResolver, E2eeContactKeys.GET_ALL_SELF_KEYS_METHOD, 528 extras); 529 530 if (response == null) { 531 return new ArrayList<>(); 532 } 533 List<E2eeSelfKey> value = response.getParcelableArrayList(E2eeContactKeys.KEY_CONTACT_KEYS, 534 E2eeSelfKey.class); 535 if (value == null) { 536 return new ArrayList<>(); 537 } 538 return value; 539 } 540 541 /** 542 * Returns all end-to-end encryption self key entries that are owned by the caller app. 543 * 544 * @return a list of {@link E2eeSelfKey} objects containing the end-to-end encryption self key 545 * information, or an empty list if no self keys are found. 546 */ 547 @RequiresPermission(android.Manifest.permission.READ_CONTACTS) 548 @NonNull getOwnerE2eeSelfKeys()549 public List<E2eeSelfKey> getOwnerE2eeSelfKeys() { 550 Bundle extras = new Bundle(); 551 552 Bundle response = nullSafeCall(mContentResolver, E2eeContactKeys.GET_OWNER_SELF_KEYS_METHOD, 553 extras); 554 555 if (response == null) { 556 return new ArrayList<>(); 557 } 558 List<E2eeSelfKey> value = response.getParcelableArrayList(E2eeContactKeys.KEY_CONTACT_KEYS, 559 E2eeSelfKey.class); 560 if (value == null) { 561 return new ArrayList<>(); 562 } 563 return value; 564 } 565 566 /** 567 * Removes an end-to-end encryption self key entry given the deviceId and the inferred 568 * package name of the caller. 569 * 570 * @param deviceId an app-specified identifier for the device 571 * @param accountId an app-specified identifier for the account 572 * @return true if the entry was removed, false otherwise. 573 */ 574 @RequiresPermission(android.Manifest.permission.WRITE_CONTACTS) removeE2eeSelfKey(@onNull String deviceId, @NonNull String accountId)575 public boolean removeE2eeSelfKey(@NonNull String deviceId, 576 @NonNull String accountId) { 577 Bundle extras = new Bundle(); 578 extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId)); 579 extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId)); 580 581 Bundle response = nullSafeCall(mContentResolver, 582 E2eeContactKeys.REMOVE_SELF_KEY_METHOD, extras); 583 584 return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS); 585 } 586 nullSafeCall(@onNull ContentResolver resolver, @NonNull String method, @Nullable Bundle extras)587 private Bundle nullSafeCall(@NonNull ContentResolver resolver, @NonNull String method, 588 @Nullable Bundle extras) { 589 try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY_URI)) { 590 return client.call(method, null, extras); 591 } catch (RemoteException e) { 592 throw e.rethrowAsRuntimeException(); 593 } 594 } 595 596 /** 597 * Possible values of verification state. 598 * 599 * @hide 600 */ 601 @IntDef(prefix = {"VERIFICATION_STATE_"}, value = { 602 VERIFICATION_STATE_UNVERIFIED, 603 VERIFICATION_STATE_VERIFICATION_FAILED, 604 VERIFICATION_STATE_VERIFIED 605 }) 606 @Retention(RetentionPolicy.SOURCE) 607 public @interface VerificationState { 608 } 609 610 /** 611 * Unverified state of a contact end to end encrypted key. 612 */ 613 public static final int VERIFICATION_STATE_UNVERIFIED = 0; 614 /** 615 * Failed verification state of a contact end to end encrypted key. 616 */ 617 public static final int VERIFICATION_STATE_VERIFICATION_FAILED = 1; 618 /** 619 * Verified state of a contact end to end encrypted key. 620 */ 621 public static final int VERIFICATION_STATE_VERIFIED = 2; 622 623 /** @hide */ 624 public static final class E2eeContactKeys { 625 E2eeContactKeys()626 private E2eeContactKeys() { 627 } 628 629 /** 630 * <p> 631 * An opaque value that contains hints on how to find the contact if 632 * its row id changed as a result of a sync or aggregation. 633 * </p> 634 */ 635 public static final String LOOKUP_KEY = "lookup"; 636 637 /** 638 * <p> 639 * An app-specified identifier for the device for which the end-to-end encryption 640 * contact key can be used. 641 * </p> 642 */ 643 public static final String DEVICE_ID = "device_id"; 644 645 /** 646 * <p> 647 * An app-specified identifier for the account for which the end-to-end encryption 648 * contact key can be used. 649 * Usually a phone number. 650 * </p> 651 */ 652 public static final String ACCOUNT_ID = "account_id"; 653 654 /** 655 * <p> 656 * The display name for the contact. 657 * </p> 658 */ 659 public static final String DISPLAY_NAME = "display_name"; 660 661 /** 662 * <p> 663 * The phone number as the user entered it. 664 * </p> 665 */ 666 public static final String PHONE_NUMBER = "number"; 667 668 /** 669 * <p> 670 * The email address. 671 * </p> 672 */ 673 public static final String EMAIL_ADDRESS = "address"; 674 675 /** 676 * <p> 677 * Timestamp at which the key was updated. 678 * </p> 679 */ 680 public static final String TIME_UPDATED = "time_updated"; 681 682 /** 683 * <p> 684 * The raw bytes for the key. 685 * </p> 686 */ 687 public static final String KEY_VALUE = "key_value"; 688 689 /** 690 * <p> 691 * The package name of the package that created the key. 692 * </p> 693 */ 694 public static final String OWNER_PACKAGE_NAME = "owner_package_name"; 695 696 /** 697 * <p> 698 * Describes the local verification state for the key, for instance QR-code based 699 * verification. 700 * </p> 701 */ 702 public static final String LOCAL_VERIFICATION_STATE = "local_verification_state"; 703 704 /** 705 * <p> 706 * Describes the remote verification state for the key, for instance through a key 707 * transparency server. 708 * </p> 709 */ 710 public static final String REMOTE_VERIFICATION_STATE = "remote_verification_state"; 711 712 /** 713 * The method to invoke in order to add a new key for a contact. 714 */ 715 public static final String UPDATE_OR_INSERT_CONTACT_KEY_METHOD = "updateOrInsertContactKey"; 716 717 /** 718 * The method to invoke in order to retrieve key for a single contact. 719 */ 720 public static final String GET_CONTACT_KEY_METHOD = "getContactKey"; 721 722 /** 723 * The method to invoke in order to retrieve all end-to-end encryption contact keys. 724 */ 725 public static final String GET_ALL_CONTACT_KEYS_METHOD = "getAllContactKeys"; 726 727 /** 728 * The method to invoke in order to retrieve end-to-end encryption contact keys that belong 729 * to the caller. 730 */ 731 public static final String GET_OWNER_CONTACT_KEYS_METHOD = "getOwnerContactKeys"; 732 733 /** 734 * The method to invoke in order to update an end-to-end encryption contact key local 735 * verification state. 736 */ 737 public static final String UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD = 738 "updateContactKeyLocalVerificationState"; 739 740 /** 741 * The method to invoke in order to update an end-to-end encryption contact key remote 742 * verification state. 743 */ 744 public static final String UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD = 745 "updateContactKeyRemoteVerificationState"; 746 747 /** 748 * The method to invoke in order to remove a end-to-end encryption contact key. 749 */ 750 public static final String REMOVE_CONTACT_KEY_METHOD = "removeContactKey"; 751 752 /** 753 * The method to invoke in order to add a new self key. 754 */ 755 public static final String UPDATE_OR_INSERT_SELF_KEY_METHOD = "updateOrInsertSelfKey"; 756 757 /** 758 * The method to invoke in order to update a self key remote verification state. 759 */ 760 public static final String UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD = 761 "updateSelfKeyRemoteVerificationState"; 762 763 /** 764 * The method to invoke in order to retrieve a self key. 765 */ 766 public static final String GET_SELF_KEY_METHOD = "getSelfKey"; 767 768 /** 769 * The method to invoke in order to retrieve all self keys. 770 */ 771 public static final String GET_ALL_SELF_KEYS_METHOD = "getAllSelfKeys"; 772 773 /** 774 * The method to invoke in order to retrieve self keys that belong to the caller. 775 */ 776 public static final String GET_OWNER_SELF_KEYS_METHOD = "getOwnerSelfKeys"; 777 778 /** 779 * The method to invoke in order to remove a new self key. 780 */ 781 public static final String REMOVE_SELF_KEY_METHOD = "removeSelfKey"; 782 783 /** 784 * Key in the incoming Bundle for all the end-to-end encryption contact keys. 785 */ 786 public static final String KEY_CONTACT_KEYS = "key_contact_keys"; 787 788 /** 789 * Key in the incoming Bundle for a single end-to-end encryption contact key. 790 */ 791 public static final String KEY_CONTACT_KEY = "key_contact_key"; 792 793 /** 794 * Key in the incoming Bundle for a number of modified rows. 795 */ 796 public static final String KEY_UPDATED_ROWS = "key_updated_rows"; 797 } 798 /** 799 * A parcelable class encapsulating other users' end to end encrypted contact key. 800 */ 801 public static final class E2eeContactKey extends E2eeBaseKey implements Parcelable { 802 803 /** 804 * Describes the local verification state for the key, for instance QR-code based 805 * verification. 806 */ 807 private final int mLocalVerificationState; 808 809 /** 810 * The display name for the contact. 811 */ 812 private final String mDisplayName; 813 814 /** 815 * The phone number as the user entered it. 816 */ 817 private final String mPhoneNumber; 818 819 /** 820 * The email address. 821 */ 822 private final String mEmailAddress; 823 824 /** 825 * @hide 826 */ E2eeContactKey(@ullable String deviceId, @NonNull String accountId, @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, @VerificationState int localVerificationState, @VerificationState int remoteVerificationState, @Nullable String displayName, @Nullable String phoneNumber, @Nullable String emailAddress)827 public E2eeContactKey(@Nullable String deviceId, @NonNull String accountId, 828 @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, 829 @VerificationState int localVerificationState, 830 @VerificationState int remoteVerificationState, 831 @Nullable String displayName, 832 @Nullable String phoneNumber, @Nullable String emailAddress) { 833 super(deviceId, accountId, ownerPackageName, timeUpdated, keyValue, 834 remoteVerificationState); 835 this.mLocalVerificationState = localVerificationState; 836 this.mDisplayName = displayName; 837 this.mPhoneNumber = phoneNumber; 838 this.mEmailAddress = emailAddress; 839 } 840 841 /** 842 * Gets the local verification state for the key, for instance QR-code based verification. 843 * 844 * @return The local verification state for the key. 845 */ getLocalVerificationState()846 public @VerificationState int getLocalVerificationState() { 847 return mLocalVerificationState; 848 } 849 850 /** 851 * Gets the display name for the contact. 852 * 853 * @return The display name for the contact. 854 */ 855 @Nullable getDisplayName()856 public String getDisplayName() { 857 return mDisplayName; 858 } 859 860 /** 861 * Gets the phone number as the user entered it. 862 * 863 * @return The phone number as the user entered it. 864 */ 865 @Nullable getPhoneNumber()866 public String getPhoneNumber() { 867 return mPhoneNumber; 868 } 869 870 /** 871 * Gets the email address. 872 * 873 * @return The email address. 874 */ 875 @Nullable getEmailAddress()876 public String getEmailAddress() { 877 return mEmailAddress; 878 } 879 880 @Override hashCode()881 public int hashCode() { 882 return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated, 883 Arrays.hashCode(mKeyValue), mLocalVerificationState, mRemoteVerificationState, 884 mDisplayName, mPhoneNumber, mEmailAddress); 885 } 886 887 @Override equals(Object obj)888 public boolean equals(Object obj) { 889 if (obj == null) return false; 890 if (obj == this) return true; 891 892 if (!(obj instanceof E2eeContactKey toCompare)) { 893 return false; 894 } 895 896 return Objects.equals(mDeviceId, toCompare.mDeviceId) 897 && Objects.equals(mAccountId, toCompare.mAccountId) 898 && Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName) 899 && mTimeUpdated == toCompare.mTimeUpdated 900 && Arrays.equals(mKeyValue, toCompare.mKeyValue) 901 && mLocalVerificationState == toCompare.mLocalVerificationState 902 && mRemoteVerificationState == toCompare.mRemoteVerificationState 903 && Objects.equals(mDisplayName, toCompare.mDisplayName) 904 && Objects.equals(mPhoneNumber, toCompare.mPhoneNumber) 905 && Objects.equals(mEmailAddress, toCompare.mEmailAddress); 906 } 907 908 @Override writeToParcel(@onNull Parcel dest, int flags)909 public void writeToParcel(@NonNull Parcel dest, int flags) { 910 dest.writeString8(mDeviceId); 911 dest.writeString8(mAccountId); 912 dest.writeString8(mOwnerPackageName); 913 dest.writeLong(mTimeUpdated); 914 dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL); 915 if (mKeyValue != null) { 916 dest.writeByteArray(mKeyValue); 917 } 918 dest.writeInt(mLocalVerificationState); 919 dest.writeInt(mRemoteVerificationState); 920 dest.writeString8(mDisplayName); 921 dest.writeString8(mPhoneNumber); 922 dest.writeString8(mEmailAddress); 923 } 924 925 @Override describeContents()926 public int describeContents() { 927 return 0; 928 } 929 930 @NonNull 931 public static final Creator<E2eeContactKey> CREATOR = 932 new Creator<>() { 933 @Override 934 public E2eeContactKey createFromParcel(Parcel source) { 935 String deviceId = source.readString8(); 936 String accountId = source.readString8(); 937 String ownerPackageName = source.readString8(); 938 long timeUpdated = source.readLong(); 939 int keyValueLength = source.readInt(); 940 byte[] keyValue; 941 if (keyValueLength > 0) { 942 keyValue = new byte[keyValueLength]; 943 source.readByteArray(keyValue); 944 } else { 945 keyValue = null; 946 } 947 int localVerificationState = source.readInt(); 948 int remoteVerificationState = source.readInt(); 949 String displayName = source.readString8(); 950 String number = source.readString8(); 951 String address = source.readString8(); 952 return new E2eeContactKey(deviceId, accountId, ownerPackageName, 953 timeUpdated, keyValue, localVerificationState, 954 remoteVerificationState, displayName, number, address); 955 } 956 957 @Override 958 public E2eeContactKey[] newArray(int size) { 959 return new E2eeContactKey[size]; 960 } 961 }; 962 } 963 964 /** 965 * A parcelable class encapsulating self end to end encrypted contact key. 966 */ 967 public static final class E2eeSelfKey extends E2eeBaseKey implements Parcelable { 968 /** 969 * @hide 970 */ E2eeSelfKey(@ullable String deviceId, @NonNull String accountId, @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, @VerificationState int remoteVerificationState)971 public E2eeSelfKey(@Nullable String deviceId, @NonNull String accountId, 972 @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, 973 @VerificationState int remoteVerificationState) { 974 super(deviceId, accountId, ownerPackageName, timeUpdated, keyValue, 975 remoteVerificationState); 976 } 977 978 @Override hashCode()979 public int hashCode() { 980 return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated, 981 Arrays.hashCode(mKeyValue), mRemoteVerificationState); 982 } 983 984 @Override equals(Object obj)985 public boolean equals(Object obj) { 986 if (obj == null) return false; 987 if (obj == this) return true; 988 989 if (!(obj instanceof E2eeSelfKey toCompare)) { 990 return false; 991 } 992 993 return Objects.equals(mDeviceId, toCompare.mDeviceId) 994 && Objects.equals(mAccountId, toCompare.mAccountId) 995 && Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName) 996 && mTimeUpdated == toCompare.mTimeUpdated 997 && Arrays.equals(mKeyValue, toCompare.mKeyValue) 998 && mRemoteVerificationState == toCompare.mRemoteVerificationState; 999 } 1000 1001 @Override writeToParcel(@onNull Parcel dest, int flags)1002 public void writeToParcel(@NonNull Parcel dest, int flags) { 1003 dest.writeString8(mDeviceId); 1004 dest.writeString8(mAccountId); 1005 dest.writeString8(mOwnerPackageName); 1006 dest.writeLong(mTimeUpdated); 1007 dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL); 1008 if (mKeyValue != null) { 1009 dest.writeByteArray(mKeyValue); 1010 } 1011 dest.writeInt(mRemoteVerificationState); 1012 } 1013 1014 @Override describeContents()1015 public int describeContents() { 1016 return 0; 1017 } 1018 1019 @NonNull 1020 public static final Creator<E2eeSelfKey> CREATOR = 1021 new Creator<>() { 1022 @Override 1023 public E2eeSelfKey createFromParcel(Parcel source) { 1024 String deviceId = source.readString8(); 1025 String accountId = source.readString8(); 1026 String ownerPackageName = source.readString8(); 1027 long timeUpdated = source.readLong(); 1028 int keyValueLength = source.readInt(); 1029 byte[] keyValue; 1030 if (keyValueLength > 0) { 1031 keyValue = new byte[keyValueLength]; 1032 source.readByteArray(keyValue); 1033 } else { 1034 keyValue = null; 1035 } 1036 int remoteVerificationState = source.readInt(); 1037 return new E2eeSelfKey(deviceId, accountId, ownerPackageName, 1038 timeUpdated, keyValue, remoteVerificationState); 1039 } 1040 1041 @Override 1042 public E2eeSelfKey[] newArray(int size) { 1043 return new E2eeSelfKey[size]; 1044 } 1045 }; 1046 } 1047 1048 /** 1049 * An abstract class that's extended by self and contact key classes. 1050 * 1051 * @hide 1052 */ 1053 abstract static class E2eeBaseKey { 1054 /** 1055 * An app-specified identifier for the device for which the key can be used. 1056 */ 1057 protected final String mDeviceId; 1058 1059 /** 1060 * An app-specified identifier for the account for which the key can be used. 1061 * Usually a phone number. 1062 */ 1063 protected final String mAccountId; 1064 1065 /** 1066 * Owner application package name. 1067 */ 1068 protected final String mOwnerPackageName; 1069 1070 /** 1071 * Timestamp at which the key was updated. 1072 */ 1073 protected final long mTimeUpdated; 1074 1075 /** 1076 * The raw bytes for the key. 1077 */ 1078 protected final byte[] mKeyValue; 1079 1080 /** 1081 * Describes the remote verification state for the end-to-end encryption key, for instance 1082 * through a key transparency server. 1083 */ 1084 protected final int mRemoteVerificationState; 1085 E2eeBaseKey(@ullable String deviceId, @NonNull String accountId, @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, @VerificationState int remoteVerificationState)1086 protected E2eeBaseKey(@Nullable String deviceId, @NonNull String accountId, 1087 @NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue, 1088 @VerificationState int remoteVerificationState) { 1089 this.mDeviceId = deviceId; 1090 this.mAccountId = accountId; 1091 this.mOwnerPackageName = ownerPackageName; 1092 this.mTimeUpdated = timeUpdated; 1093 this.mKeyValue = keyValue == null ? null : Arrays.copyOf(keyValue, keyValue.length); 1094 this.mRemoteVerificationState = remoteVerificationState; 1095 } 1096 1097 /** 1098 * Gets the app-specified identifier for the device for which the end-to-end encryption 1099 * key can be used. 1100 * Returns null if the app doesn't have the required visibility into 1101 * the end-to-end encryption key. 1102 * 1103 * @return An app-specified identifier for the device. 1104 */ 1105 @Nullable getDeviceId()1106 public String getDeviceId() { 1107 return mDeviceId; 1108 } 1109 1110 /** 1111 * Gets the app-specified identifier for the account for which the end-to-end encryption 1112 * key can be used. 1113 * Usually a phone number. 1114 * 1115 * @return An app-specified identifier for the account. 1116 */ 1117 @NonNull getAccountId()1118 public String getAccountId() { 1119 return mAccountId; 1120 } 1121 1122 /** 1123 * Gets the owner application package name. 1124 * 1125 * @return The owner application package name. 1126 */ 1127 @NonNull getOwnerPackageName()1128 public String getOwnerPackageName() { 1129 return mOwnerPackageName; 1130 } 1131 1132 /** 1133 * Gets the timestamp at which the end-to-end encryption key was updated. Returns -1 if 1134 * the app doesn't have the required visibility into the key. 1135 * 1136 * @return The timestamp at which the key was updated in the System.currentTimeMillis() 1137 * base. 1138 */ getTimeUpdated()1139 public long getTimeUpdated() { 1140 return mTimeUpdated; 1141 } 1142 1143 /** 1144 * Gets the raw bytes for the end-to-end encryption key. 1145 * Returns null if the app doesn't have the required visibility into 1146 * the end-to-end encryption key. 1147 * 1148 * @return A copy of the raw bytes for the end-to-end encryption key. 1149 */ 1150 @Nullable getKeyValue()1151 public byte[] getKeyValue() { 1152 return mKeyValue == null ? null : Arrays.copyOf(mKeyValue, mKeyValue.length); 1153 } 1154 1155 /** 1156 * Gets the remote verification state for the end-to-end encryption key, for instance 1157 * through a key transparency server. 1158 * 1159 * @return The remote verification state for the end-to-end encryption key. 1160 */ getRemoteVerificationState()1161 public @VerificationState int getRemoteVerificationState() { 1162 return mRemoteVerificationState; 1163 } 1164 } 1165 } 1166