1166 lines
46 KiB
Java
1166 lines
46 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2024 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.provider;
|
||
|
|
||
|
import android.annotation.FlaggedApi;
|
||
|
import android.annotation.IntDef;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.RequiresPermission;
|
||
|
import android.annotation.SystemApi;
|
||
|
import android.content.ContentProviderClient;
|
||
|
import android.content.ContentResolver;
|
||
|
import android.content.Context;
|
||
|
import android.net.Uri;
|
||
|
import android.os.Bundle;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
import android.os.RemoteException;
|
||
|
|
||
|
import java.lang.annotation.Retention;
|
||
|
import java.lang.annotation.RetentionPolicy;
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.Arrays;
|
||
|
import java.util.List;
|
||
|
import java.util.Objects;
|
||
|
|
||
|
/**
|
||
|
* E2eeContactKeysManager provides access to the provider of end-to-end encryption contact keys.
|
||
|
* It manages two types of keys - {@link E2eeContactKey} and {@link E2eeSelfKey}.
|
||
|
* <ul>
|
||
|
* <li>
|
||
|
* A {@link E2eeContactKey} is a public key associated with a contact. It's used to end-to-end
|
||
|
* encrypt the communications between a user and the contact. This API allows operations on
|
||
|
* {@link E2eeContactKey}s to insert/update, remove, change the verification state, and retrieving
|
||
|
* keys (either created by or visible to the caller app).
|
||
|
* </li>
|
||
|
* <li>
|
||
|
* A {@link E2eeSelfKey} is a key for this device, so the key represents the owner of the device.
|
||
|
* This API allows operations on {@link E2eeSelfKey}s to insert/update, remove, and retrieving
|
||
|
* self keys (either created by or visible to the caller app).
|
||
|
* </li>
|
||
|
* </ul>
|
||
|
* Keys are uniquely identified by:
|
||
|
* <ul>
|
||
|
* <li>
|
||
|
* ownerPackageName - package name of an app that the key belongs to.
|
||
|
* </li>
|
||
|
* <li>
|
||
|
* deviceId - an app-specified identifier for the device, defined by the app. Within that app,
|
||
|
* the deviceID should be unique among devices belonging to that user.
|
||
|
* </li>
|
||
|
* <li>
|
||
|
* accountId - the app-specified identifier for the account for which the contact key can be used.
|
||
|
* Using different account IDs allows for multiple key entries representing the same user.
|
||
|
* For most apps this would be a phone number.
|
||
|
* </li>
|
||
|
* </ul>
|
||
|
* Contact keys also use lookupKey which is an opaque value used to identify a contact in
|
||
|
* ContactsProvider.
|
||
|
*/
|
||
|
@FlaggedApi(Flags.FLAG_USER_KEYS)
|
||
|
public final class E2eeContactKeysManager {
|
||
|
/**
|
||
|
* The authority for the end-to-end encryption contact keys provider.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public static final String AUTHORITY = "com.android.contactkeys.contactkeysprovider";
|
||
|
|
||
|
/**
|
||
|
* A content:// style uri to the authority for the end-to-end encryption contact keys provider.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
|
||
|
|
||
|
/**
|
||
|
* Maximum size of an end-to-end encryption contact key.
|
||
|
*/
|
||
|
private static final int MAX_KEY_SIZE_BYTES = 5000;
|
||
|
|
||
|
/**
|
||
|
* Special value to distinguish a null array in a parcelable object.
|
||
|
*/
|
||
|
private static final int ARRAY_IS_NULL = -1;
|
||
|
|
||
|
@NonNull
|
||
|
private final ContentResolver mContentResolver;
|
||
|
|
||
|
/** @hide */
|
||
|
public E2eeContactKeysManager(@NonNull Context context) {
|
||
|
Objects.requireNonNull(context);
|
||
|
mContentResolver = context.getContentResolver();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inserts a new entry into the end-to-end encryption contact keys table or updates one if it
|
||
|
* already exists.
|
||
|
* The inserted/updated end-to-end encryption contact key is owned by the caller app.
|
||
|
*
|
||
|
* @param lookupKey value that references the contact
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes)
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
|
||
|
public void updateOrInsertE2eeContactKey(@NonNull String lookupKey,
|
||
|
@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@NonNull byte[] keyValue) {
|
||
|
validateKeyLength(keyValue);
|
||
|
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putByteArray(E2eeContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue));
|
||
|
|
||
|
nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_OR_INSERT_CONTACT_KEY_METHOD, extras);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves an end-to-end encryption contact key entry given the lookup key, device ID,
|
||
|
* accountId and inferred caller package name.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @return a {@link E2eeContactKey} object containing the contact key information,
|
||
|
* or null if no contact key is found.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
|
||
|
@Nullable
|
||
|
public E2eeContactKey getE2eeContactKey(
|
||
|
@NonNull String lookupKey,
|
||
|
@NonNull String deviceId,
|
||
|
@NonNull String accountId) {
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.GET_CONTACT_KEY_METHOD, extras);
|
||
|
|
||
|
if (response == null) {
|
||
|
return null;
|
||
|
}
|
||
|
return response.getParcelable(E2eeContactKeys.KEY_CONTACT_KEY, E2eeContactKey.class);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves all end-to-end encryption contact key entries that belong to apps visible to
|
||
|
* the caller.
|
||
|
* The keys will be stripped of deviceId, timeUpdated and keyValue data.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @return a list of {@link E2eeContactKey} objects containing the contact key
|
||
|
* information, or an empty list if no keys are found.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
|
||
|
@NonNull
|
||
|
public List<E2eeContactKey> getAllE2eeContactKeys(@NonNull String lookupKey) {
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.GET_ALL_CONTACT_KEYS_METHOD, extras);
|
||
|
|
||
|
if (response == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
List<E2eeContactKey> value = response.getParcelableArrayList(
|
||
|
E2eeContactKeys.KEY_CONTACT_KEYS, E2eeContactKey.class);
|
||
|
if (value == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves all end-to-end encryption contact key entries for a given lookupKey that belong to
|
||
|
* the caller app.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @return a list of {@link E2eeContactKey} objects containing the end-to-end encryption
|
||
|
* contact key information, or an empty list if no keys are found.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
|
||
|
@NonNull
|
||
|
public List<E2eeContactKey> getOwnerE2eeContactKeys(@NonNull String lookupKey) {
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.GET_OWNER_CONTACT_KEYS_METHOD, extras);
|
||
|
|
||
|
if (response == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
List<E2eeContactKey> value = response.getParcelableArrayList(
|
||
|
E2eeContactKeys.KEY_CONTACT_KEYS, E2eeContactKey.class);
|
||
|
if (value == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates an end-to-end encryption contact key entry's local verification state that belongs
|
||
|
* to the caller app.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param localVerificationState the new local verification state
|
||
|
* @return true if the entry was updated, false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
|
||
|
public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String lookupKey,
|
||
|
@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@VerificationState int localVerificationState) {
|
||
|
validateVerificationState(localVerificationState);
|
||
|
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putInt(E2eeContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState);
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates an end-to-end encryption contact key entry's local verification state that belongs
|
||
|
* to the app identified by ownerPackageName.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param ownerPackageName the package name of the app that owns the key
|
||
|
* @param localVerificationState the new local verification state
|
||
|
* @return true if the entry was updated, false otherwise.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
@RequiresPermission(allOf = {
|
||
|
android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS,
|
||
|
android.Manifest.permission.WRITE_CONTACTS})
|
||
|
public boolean updateE2eeContactKeyLocalVerificationState(@NonNull String lookupKey,
|
||
|
@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@NonNull String ownerPackageName,
|
||
|
@VerificationState int localVerificationState) {
|
||
|
validateVerificationState(localVerificationState);
|
||
|
|
||
|
final Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME,
|
||
|
Objects.requireNonNull(ownerPackageName));
|
||
|
extras.putInt(E2eeContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState);
|
||
|
|
||
|
final Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates an end-to-end encryption contact key entry's remote verification state that belongs
|
||
|
* to the caller app.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param remoteVerificationState the new remote verification state
|
||
|
* @return true if the entry was updated, false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
|
||
|
public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String lookupKey,
|
||
|
@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@VerificationState int remoteVerificationState) {
|
||
|
validateVerificationState(remoteVerificationState);
|
||
|
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates an end-to-end encryption contact key entry's remote verification state that belongs
|
||
|
* to the app identified by ownerPackageName.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param ownerPackageName the package name of the app that owns the key
|
||
|
* @param remoteVerificationState the new remote verification state
|
||
|
* @return true if the entry was updated, false otherwise.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
@RequiresPermission(allOf = {
|
||
|
android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS,
|
||
|
android.Manifest.permission.WRITE_CONTACTS})
|
||
|
public boolean updateE2eeContactKeyRemoteVerificationState(@NonNull String lookupKey,
|
||
|
@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@NonNull String ownerPackageName,
|
||
|
@VerificationState int remoteVerificationState) {
|
||
|
validateVerificationState(remoteVerificationState);
|
||
|
|
||
|
final Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME,
|
||
|
Objects.requireNonNull(ownerPackageName));
|
||
|
extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
|
||
|
|
||
|
final Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
private static void validateVerificationState(int verificationState) {
|
||
|
if (verificationState != VERIFICATION_STATE_UNVERIFIED
|
||
|
&& verificationState != VERIFICATION_STATE_VERIFICATION_FAILED
|
||
|
&& verificationState != VERIFICATION_STATE_VERIFIED) {
|
||
|
throw new IllegalArgumentException("Verification state value "
|
||
|
+ verificationState + " is not supported");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes an end-to-end encryption contact key entry that belongs to the caller app.
|
||
|
*
|
||
|
* @param lookupKey the value that references the contact
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @return true if the entry was removed, false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
|
||
|
public boolean removeE2eeContactKey(@NonNull String lookupKey,
|
||
|
@NonNull String deviceId,
|
||
|
@NonNull String accountId) {
|
||
|
final Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
|
||
|
final Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.REMOVE_CONTACT_KEY_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inserts a new entry into the end-to-end encryption self keys table or updates one if it
|
||
|
* already exists.
|
||
|
*
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param keyValue the raw bytes for the key (max size is {@link #getMaxKeySizeBytes} bytes)
|
||
|
* @return true if the entry was added or updated, false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
|
||
|
public boolean updateOrInsertE2eeSelfKey(@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@NonNull byte[] keyValue) {
|
||
|
validateKeyLength(keyValue);
|
||
|
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putByteArray(E2eeContactKeys.KEY_VALUE, Objects.requireNonNull(keyValue));
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_OR_INSERT_SELF_KEY_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
private static void validateKeyLength(byte[] keyValue) {
|
||
|
Objects.requireNonNull(keyValue);
|
||
|
if (keyValue.length == 0 || keyValue.length > getMaxKeySizeBytes()) {
|
||
|
throw new IllegalArgumentException("Key value length is " + keyValue.length + "."
|
||
|
+ " Should be more than 0 and less than " + getMaxKeySizeBytes());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates an end-to-end encryption self key entry's remote verification state.
|
||
|
*
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param remoteVerificationState the new remote verification state
|
||
|
* @return true if the entry was updated, false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
|
||
|
public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@VerificationState int remoteVerificationState) {
|
||
|
validateVerificationState(remoteVerificationState);
|
||
|
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Updates an end-to-end encryption self key entry's remote verification state that belongs to
|
||
|
* the app identified by ownerPackageName.
|
||
|
*
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @param ownerPackageName the package name of the app that owns the key
|
||
|
* @param remoteVerificationState the new remote verification state
|
||
|
* @return true if the entry was updated, false otherwise.
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
@RequiresPermission(allOf = {
|
||
|
android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS,
|
||
|
android.Manifest.permission.WRITE_CONTACTS})
|
||
|
public boolean updateE2eeSelfKeyRemoteVerificationState(@NonNull String deviceId,
|
||
|
@NonNull String accountId,
|
||
|
@NonNull String ownerPackageName,
|
||
|
@VerificationState int remoteVerificationState) {
|
||
|
validateVerificationState(remoteVerificationState);
|
||
|
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
extras.putString(E2eeContactKeys.OWNER_PACKAGE_NAME,
|
||
|
Objects.requireNonNull(ownerPackageName));
|
||
|
extras.putInt(E2eeContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Maximum size of an end-to-end encryption contact key.
|
||
|
*/
|
||
|
public static int getMaxKeySizeBytes() {
|
||
|
return MAX_KEY_SIZE_BYTES;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns an end-to-end encryption self key entry given the deviceId and the inferred package
|
||
|
* name of the caller.
|
||
|
*
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @return a {@link E2eeSelfKey} object containing the end-to-end encryption self key
|
||
|
* information, or null if no self key is found.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
|
||
|
@Nullable
|
||
|
public E2eeSelfKey getE2eeSelfKey(@NonNull String deviceId,
|
||
|
@NonNull String accountId) {
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.GET_SELF_KEY_METHOD, extras);
|
||
|
|
||
|
if (response == null) {
|
||
|
return null;
|
||
|
}
|
||
|
return response.getParcelable(E2eeContactKeys.KEY_CONTACT_KEY, E2eeSelfKey.class);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns all end-to-end encryption self key entries that belong to apps visible to the caller.
|
||
|
* The keys will be stripped of deviceId, timeUpdated and keyValue data.
|
||
|
*
|
||
|
* @return a list of {@link E2eeSelfKey} objects containing the end-to-end encryption self key
|
||
|
* information, or an empty list if no self keys are found.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
|
||
|
@NonNull
|
||
|
public List<E2eeSelfKey> getAllE2eeSelfKeys() {
|
||
|
Bundle extras = new Bundle();
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver, E2eeContactKeys.GET_ALL_SELF_KEYS_METHOD,
|
||
|
extras);
|
||
|
|
||
|
if (response == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
List<E2eeSelfKey> value = response.getParcelableArrayList(E2eeContactKeys.KEY_CONTACT_KEYS,
|
||
|
E2eeSelfKey.class);
|
||
|
if (value == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns all end-to-end encryption self key entries that are owned by the caller app.
|
||
|
*
|
||
|
* @return a list of {@link E2eeSelfKey} objects containing the end-to-end encryption self key
|
||
|
* information, or an empty list if no self keys are found.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.READ_CONTACTS)
|
||
|
@NonNull
|
||
|
public List<E2eeSelfKey> getOwnerE2eeSelfKeys() {
|
||
|
Bundle extras = new Bundle();
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver, E2eeContactKeys.GET_OWNER_SELF_KEYS_METHOD,
|
||
|
extras);
|
||
|
|
||
|
if (response == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
List<E2eeSelfKey> value = response.getParcelableArrayList(E2eeContactKeys.KEY_CONTACT_KEYS,
|
||
|
E2eeSelfKey.class);
|
||
|
if (value == null) {
|
||
|
return new ArrayList<>();
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Removes an end-to-end encryption self key entry given the deviceId and the inferred
|
||
|
* package name of the caller.
|
||
|
*
|
||
|
* @param deviceId an app-specified identifier for the device
|
||
|
* @param accountId an app-specified identifier for the account
|
||
|
* @return true if the entry was removed, false otherwise.
|
||
|
*/
|
||
|
@RequiresPermission(android.Manifest.permission.WRITE_CONTACTS)
|
||
|
public boolean removeE2eeSelfKey(@NonNull String deviceId,
|
||
|
@NonNull String accountId) {
|
||
|
Bundle extras = new Bundle();
|
||
|
extras.putString(E2eeContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
|
||
|
extras.putString(E2eeContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
|
||
|
|
||
|
Bundle response = nullSafeCall(mContentResolver,
|
||
|
E2eeContactKeys.REMOVE_SELF_KEY_METHOD, extras);
|
||
|
|
||
|
return response != null && response.getBoolean(E2eeContactKeys.KEY_UPDATED_ROWS);
|
||
|
}
|
||
|
|
||
|
private Bundle nullSafeCall(@NonNull ContentResolver resolver, @NonNull String method,
|
||
|
@Nullable Bundle extras) {
|
||
|
try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY_URI)) {
|
||
|
return client.call(method, null, extras);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowAsRuntimeException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Possible values of verification state.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@IntDef(prefix = {"VERIFICATION_STATE_"}, value = {
|
||
|
VERIFICATION_STATE_UNVERIFIED,
|
||
|
VERIFICATION_STATE_VERIFICATION_FAILED,
|
||
|
VERIFICATION_STATE_VERIFIED
|
||
|
})
|
||
|
@Retention(RetentionPolicy.SOURCE)
|
||
|
public @interface VerificationState {
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Unverified state of a contact end to end encrypted key.
|
||
|
*/
|
||
|
public static final int VERIFICATION_STATE_UNVERIFIED = 0;
|
||
|
/**
|
||
|
* Failed verification state of a contact end to end encrypted key.
|
||
|
*/
|
||
|
public static final int VERIFICATION_STATE_VERIFICATION_FAILED = 1;
|
||
|
/**
|
||
|
* Verified state of a contact end to end encrypted key.
|
||
|
*/
|
||
|
public static final int VERIFICATION_STATE_VERIFIED = 2;
|
||
|
|
||
|
/** @hide */
|
||
|
public static final class E2eeContactKeys {
|
||
|
|
||
|
private E2eeContactKeys() {
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* An opaque value that contains hints on how to find the contact if
|
||
|
* its row id changed as a result of a sync or aggregation.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String LOOKUP_KEY = "lookup";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* An app-specified identifier for the device for which the end-to-end encryption
|
||
|
* contact key can be used.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String DEVICE_ID = "device_id";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* An app-specified identifier for the account for which the end-to-end encryption
|
||
|
* contact key can be used.
|
||
|
* Usually a phone number.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String ACCOUNT_ID = "account_id";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* The display name for the contact.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String DISPLAY_NAME = "display_name";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* The phone number as the user entered it.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String PHONE_NUMBER = "number";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* The email address.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String EMAIL_ADDRESS = "address";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* Timestamp at which the key was updated.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String TIME_UPDATED = "time_updated";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* The raw bytes for the key.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String KEY_VALUE = "key_value";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* The package name of the package that created the key.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String OWNER_PACKAGE_NAME = "owner_package_name";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* Describes the local verification state for the key, for instance QR-code based
|
||
|
* verification.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String LOCAL_VERIFICATION_STATE = "local_verification_state";
|
||
|
|
||
|
/**
|
||
|
* <p>
|
||
|
* Describes the remote verification state for the key, for instance through a key
|
||
|
* transparency server.
|
||
|
* </p>
|
||
|
*/
|
||
|
public static final String REMOTE_VERIFICATION_STATE = "remote_verification_state";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to add a new key for a contact.
|
||
|
*/
|
||
|
public static final String UPDATE_OR_INSERT_CONTACT_KEY_METHOD = "updateOrInsertContactKey";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to retrieve key for a single contact.
|
||
|
*/
|
||
|
public static final String GET_CONTACT_KEY_METHOD = "getContactKey";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to retrieve all end-to-end encryption contact keys.
|
||
|
*/
|
||
|
public static final String GET_ALL_CONTACT_KEYS_METHOD = "getAllContactKeys";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to retrieve end-to-end encryption contact keys that belong
|
||
|
* to the caller.
|
||
|
*/
|
||
|
public static final String GET_OWNER_CONTACT_KEYS_METHOD = "getOwnerContactKeys";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to update an end-to-end encryption contact key local
|
||
|
* verification state.
|
||
|
*/
|
||
|
public static final String UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD =
|
||
|
"updateContactKeyLocalVerificationState";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to update an end-to-end encryption contact key remote
|
||
|
* verification state.
|
||
|
*/
|
||
|
public static final String UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD =
|
||
|
"updateContactKeyRemoteVerificationState";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to remove a end-to-end encryption contact key.
|
||
|
*/
|
||
|
public static final String REMOVE_CONTACT_KEY_METHOD = "removeContactKey";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to add a new self key.
|
||
|
*/
|
||
|
public static final String UPDATE_OR_INSERT_SELF_KEY_METHOD = "updateOrInsertSelfKey";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to update a self key remote verification state.
|
||
|
*/
|
||
|
public static final String UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD =
|
||
|
"updateSelfKeyRemoteVerificationState";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to retrieve a self key.
|
||
|
*/
|
||
|
public static final String GET_SELF_KEY_METHOD = "getSelfKey";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to retrieve all self keys.
|
||
|
*/
|
||
|
public static final String GET_ALL_SELF_KEYS_METHOD = "getAllSelfKeys";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to retrieve self keys that belong to the caller.
|
||
|
*/
|
||
|
public static final String GET_OWNER_SELF_KEYS_METHOD = "getOwnerSelfKeys";
|
||
|
|
||
|
/**
|
||
|
* The method to invoke in order to remove a new self key.
|
||
|
*/
|
||
|
public static final String REMOVE_SELF_KEY_METHOD = "removeSelfKey";
|
||
|
|
||
|
/**
|
||
|
* Key in the incoming Bundle for all the end-to-end encryption contact keys.
|
||
|
*/
|
||
|
public static final String KEY_CONTACT_KEYS = "key_contact_keys";
|
||
|
|
||
|
/**
|
||
|
* Key in the incoming Bundle for a single end-to-end encryption contact key.
|
||
|
*/
|
||
|
public static final String KEY_CONTACT_KEY = "key_contact_key";
|
||
|
|
||
|
/**
|
||
|
* Key in the incoming Bundle for a number of modified rows.
|
||
|
*/
|
||
|
public static final String KEY_UPDATED_ROWS = "key_updated_rows";
|
||
|
}
|
||
|
/**
|
||
|
* A parcelable class encapsulating other users' end to end encrypted contact key.
|
||
|
*/
|
||
|
public static final class E2eeContactKey extends E2eeBaseKey implements Parcelable {
|
||
|
|
||
|
/**
|
||
|
* Describes the local verification state for the key, for instance QR-code based
|
||
|
* verification.
|
||
|
*/
|
||
|
private final int mLocalVerificationState;
|
||
|
|
||
|
/**
|
||
|
* The display name for the contact.
|
||
|
*/
|
||
|
private final String mDisplayName;
|
||
|
|
||
|
/**
|
||
|
* The phone number as the user entered it.
|
||
|
*/
|
||
|
private final String mPhoneNumber;
|
||
|
|
||
|
/**
|
||
|
* The email address.
|
||
|
*/
|
||
|
private final String mEmailAddress;
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public E2eeContactKey(@Nullable 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) {
|
||
|
super(deviceId, accountId, ownerPackageName, timeUpdated, keyValue,
|
||
|
remoteVerificationState);
|
||
|
this.mLocalVerificationState = localVerificationState;
|
||
|
this.mDisplayName = displayName;
|
||
|
this.mPhoneNumber = phoneNumber;
|
||
|
this.mEmailAddress = emailAddress;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the local verification state for the key, for instance QR-code based verification.
|
||
|
*
|
||
|
* @return The local verification state for the key.
|
||
|
*/
|
||
|
public @VerificationState int getLocalVerificationState() {
|
||
|
return mLocalVerificationState;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the display name for the contact.
|
||
|
*
|
||
|
* @return The display name for the contact.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getDisplayName() {
|
||
|
return mDisplayName;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the phone number as the user entered it.
|
||
|
*
|
||
|
* @return The phone number as the user entered it.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getPhoneNumber() {
|
||
|
return mPhoneNumber;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the email address.
|
||
|
*
|
||
|
* @return The email address.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getEmailAddress() {
|
||
|
return mEmailAddress;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated,
|
||
|
Arrays.hashCode(mKeyValue), mLocalVerificationState, mRemoteVerificationState,
|
||
|
mDisplayName, mPhoneNumber, mEmailAddress);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(Object obj) {
|
||
|
if (obj == null) return false;
|
||
|
if (obj == this) return true;
|
||
|
|
||
|
if (!(obj instanceof E2eeContactKey toCompare)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return Objects.equals(mDeviceId, toCompare.mDeviceId)
|
||
|
&& Objects.equals(mAccountId, toCompare.mAccountId)
|
||
|
&& Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName)
|
||
|
&& mTimeUpdated == toCompare.mTimeUpdated
|
||
|
&& Arrays.equals(mKeyValue, toCompare.mKeyValue)
|
||
|
&& mLocalVerificationState == toCompare.mLocalVerificationState
|
||
|
&& mRemoteVerificationState == toCompare.mRemoteVerificationState
|
||
|
&& Objects.equals(mDisplayName, toCompare.mDisplayName)
|
||
|
&& Objects.equals(mPhoneNumber, toCompare.mPhoneNumber)
|
||
|
&& Objects.equals(mEmailAddress, toCompare.mEmailAddress);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||
|
dest.writeString8(mDeviceId);
|
||
|
dest.writeString8(mAccountId);
|
||
|
dest.writeString8(mOwnerPackageName);
|
||
|
dest.writeLong(mTimeUpdated);
|
||
|
dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL);
|
||
|
if (mKeyValue != null) {
|
||
|
dest.writeByteArray(mKeyValue);
|
||
|
}
|
||
|
dest.writeInt(mLocalVerificationState);
|
||
|
dest.writeInt(mRemoteVerificationState);
|
||
|
dest.writeString8(mDisplayName);
|
||
|
dest.writeString8(mPhoneNumber);
|
||
|
dest.writeString8(mEmailAddress);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static final Creator<E2eeContactKey> CREATOR =
|
||
|
new Creator<>() {
|
||
|
@Override
|
||
|
public E2eeContactKey createFromParcel(Parcel source) {
|
||
|
String deviceId = source.readString8();
|
||
|
String accountId = source.readString8();
|
||
|
String ownerPackageName = source.readString8();
|
||
|
long timeUpdated = source.readLong();
|
||
|
int keyValueLength = source.readInt();
|
||
|
byte[] keyValue;
|
||
|
if (keyValueLength > 0) {
|
||
|
keyValue = new byte[keyValueLength];
|
||
|
source.readByteArray(keyValue);
|
||
|
} else {
|
||
|
keyValue = null;
|
||
|
}
|
||
|
int localVerificationState = source.readInt();
|
||
|
int remoteVerificationState = source.readInt();
|
||
|
String displayName = source.readString8();
|
||
|
String number = source.readString8();
|
||
|
String address = source.readString8();
|
||
|
return new E2eeContactKey(deviceId, accountId, ownerPackageName,
|
||
|
timeUpdated, keyValue, localVerificationState,
|
||
|
remoteVerificationState, displayName, number, address);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public E2eeContactKey[] newArray(int size) {
|
||
|
return new E2eeContactKey[size];
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A parcelable class encapsulating self end to end encrypted contact key.
|
||
|
*/
|
||
|
public static final class E2eeSelfKey extends E2eeBaseKey implements Parcelable {
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public E2eeSelfKey(@Nullable String deviceId, @NonNull String accountId,
|
||
|
@NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue,
|
||
|
@VerificationState int remoteVerificationState) {
|
||
|
super(deviceId, accountId, ownerPackageName, timeUpdated, keyValue,
|
||
|
remoteVerificationState);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return Objects.hash(mDeviceId, mAccountId, mOwnerPackageName, mTimeUpdated,
|
||
|
Arrays.hashCode(mKeyValue), mRemoteVerificationState);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(Object obj) {
|
||
|
if (obj == null) return false;
|
||
|
if (obj == this) return true;
|
||
|
|
||
|
if (!(obj instanceof E2eeSelfKey toCompare)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return Objects.equals(mDeviceId, toCompare.mDeviceId)
|
||
|
&& Objects.equals(mAccountId, toCompare.mAccountId)
|
||
|
&& Objects.equals(mOwnerPackageName, toCompare.mOwnerPackageName)
|
||
|
&& mTimeUpdated == toCompare.mTimeUpdated
|
||
|
&& Arrays.equals(mKeyValue, toCompare.mKeyValue)
|
||
|
&& mRemoteVerificationState == toCompare.mRemoteVerificationState;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||
|
dest.writeString8(mDeviceId);
|
||
|
dest.writeString8(mAccountId);
|
||
|
dest.writeString8(mOwnerPackageName);
|
||
|
dest.writeLong(mTimeUpdated);
|
||
|
dest.writeInt(mKeyValue != null ? mKeyValue.length : ARRAY_IS_NULL);
|
||
|
if (mKeyValue != null) {
|
||
|
dest.writeByteArray(mKeyValue);
|
||
|
}
|
||
|
dest.writeInt(mRemoteVerificationState);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@NonNull
|
||
|
public static final Creator<E2eeSelfKey> CREATOR =
|
||
|
new Creator<>() {
|
||
|
@Override
|
||
|
public E2eeSelfKey createFromParcel(Parcel source) {
|
||
|
String deviceId = source.readString8();
|
||
|
String accountId = source.readString8();
|
||
|
String ownerPackageName = source.readString8();
|
||
|
long timeUpdated = source.readLong();
|
||
|
int keyValueLength = source.readInt();
|
||
|
byte[] keyValue;
|
||
|
if (keyValueLength > 0) {
|
||
|
keyValue = new byte[keyValueLength];
|
||
|
source.readByteArray(keyValue);
|
||
|
} else {
|
||
|
keyValue = null;
|
||
|
}
|
||
|
int remoteVerificationState = source.readInt();
|
||
|
return new E2eeSelfKey(deviceId, accountId, ownerPackageName,
|
||
|
timeUpdated, keyValue, remoteVerificationState);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public E2eeSelfKey[] newArray(int size) {
|
||
|
return new E2eeSelfKey[size];
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* An abstract class that's extended by self and contact key classes.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
abstract static class E2eeBaseKey {
|
||
|
/**
|
||
|
* An app-specified identifier for the device for which the key can be used.
|
||
|
*/
|
||
|
protected final String mDeviceId;
|
||
|
|
||
|
/**
|
||
|
* An app-specified identifier for the account for which the key can be used.
|
||
|
* Usually a phone number.
|
||
|
*/
|
||
|
protected final String mAccountId;
|
||
|
|
||
|
/**
|
||
|
* Owner application package name.
|
||
|
*/
|
||
|
protected final String mOwnerPackageName;
|
||
|
|
||
|
/**
|
||
|
* Timestamp at which the key was updated.
|
||
|
*/
|
||
|
protected final long mTimeUpdated;
|
||
|
|
||
|
/**
|
||
|
* The raw bytes for the key.
|
||
|
*/
|
||
|
protected final byte[] mKeyValue;
|
||
|
|
||
|
/**
|
||
|
* Describes the remote verification state for the end-to-end encryption key, for instance
|
||
|
* through a key transparency server.
|
||
|
*/
|
||
|
protected final int mRemoteVerificationState;
|
||
|
|
||
|
protected E2eeBaseKey(@Nullable String deviceId, @NonNull String accountId,
|
||
|
@NonNull String ownerPackageName, long timeUpdated, @Nullable byte[] keyValue,
|
||
|
@VerificationState int remoteVerificationState) {
|
||
|
this.mDeviceId = deviceId;
|
||
|
this.mAccountId = accountId;
|
||
|
this.mOwnerPackageName = ownerPackageName;
|
||
|
this.mTimeUpdated = timeUpdated;
|
||
|
this.mKeyValue = keyValue == null ? null : Arrays.copyOf(keyValue, keyValue.length);
|
||
|
this.mRemoteVerificationState = remoteVerificationState;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the app-specified identifier for the device for which the end-to-end encryption
|
||
|
* key can be used.
|
||
|
* Returns null if the app doesn't have the required visibility into
|
||
|
* the end-to-end encryption key.
|
||
|
*
|
||
|
* @return An app-specified identifier for the device.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public String getDeviceId() {
|
||
|
return mDeviceId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the app-specified identifier for the account for which the end-to-end encryption
|
||
|
* key can be used.
|
||
|
* Usually a phone number.
|
||
|
*
|
||
|
* @return An app-specified identifier for the account.
|
||
|
*/
|
||
|
@NonNull
|
||
|
public String getAccountId() {
|
||
|
return mAccountId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the owner application package name.
|
||
|
*
|
||
|
* @return The owner application package name.
|
||
|
*/
|
||
|
@NonNull
|
||
|
public String getOwnerPackageName() {
|
||
|
return mOwnerPackageName;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the timestamp at which the end-to-end encryption key was updated. Returns -1 if
|
||
|
* the app doesn't have the required visibility into the key.
|
||
|
*
|
||
|
* @return The timestamp at which the key was updated in the System.currentTimeMillis()
|
||
|
* base.
|
||
|
*/
|
||
|
public long getTimeUpdated() {
|
||
|
return mTimeUpdated;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the raw bytes for the end-to-end encryption key.
|
||
|
* Returns null if the app doesn't have the required visibility into
|
||
|
* the end-to-end encryption key.
|
||
|
*
|
||
|
* @return A copy of the raw bytes for the end-to-end encryption key.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public byte[] getKeyValue() {
|
||
|
return mKeyValue == null ? null : Arrays.copyOf(mKeyValue, mKeyValue.length);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the remote verification state for the end-to-end encryption key, for instance
|
||
|
* through a key transparency server.
|
||
|
*
|
||
|
* @return The remote verification state for the end-to-end encryption key.
|
||
|
*/
|
||
|
public @VerificationState int getRemoteVerificationState() {
|
||
|
return mRemoteVerificationState;
|
||
|
}
|
||
|
}
|
||
|
}
|