• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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