Merge changes from topic "contactkey_update_by_other_apps" into main

* changes:
  Add WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS to Shell.
  Add APIs for apps to update Contact keys of other apps.
  Add WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS permission.
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cf6f4f5..e6040f8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -414,6 +414,7 @@
     field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
     field public static final String WRITE_SECURITY_LOG = "android.permission.WRITE_SECURITY_LOG";
     field public static final String WRITE_SMS = "android.permission.WRITE_SMS";
+    field @FlaggedApi("android.provider.user_keys") public static final String WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS = "android.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS";
   }
 
   public static final class Manifest.permission_group {
@@ -11304,6 +11305,12 @@
     field public static final int ERROR_UNKNOWN = 0; // 0x0
   }
 
+  @FlaggedApi("android.provider.user_keys") public class ContactKeysManager {
+    method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateContactKeyLocalVerificationState(@NonNull String, @NonNull String, @NonNull String, @NonNull String, int);
+    method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateContactKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, @NonNull String, int);
+    method @RequiresPermission(allOf={android.Manifest.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS, android.Manifest.permission.WRITE_CONTACTS}) public boolean updateSelfKeyRemoteVerificationState(@NonNull String, @NonNull String, @NonNull String, int);
+  }
+
   @Deprecated public static final class ContactsContract.MetadataSync implements android.provider.BaseColumns android.provider.ContactsContract.MetadataSyncColumns {
     field @Deprecated public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
     field @Deprecated public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
diff --git a/core/java/android/provider/ContactKeysManager.java b/core/java/android/provider/ContactKeysManager.java
index ecde699..bef6456 100644
--- a/core/java/android/provider/ContactKeysManager.java
+++ b/core/java/android/provider/ContactKeysManager.java
@@ -21,6 +21,7 @@
 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;
@@ -247,6 +248,44 @@
     }
 
     /**
+     * Updates a 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 updateContactKeyLocalVerificationState(@NonNull String lookupKey,
+            @NonNull String deviceId,
+            @NonNull String accountId,
+            @NonNull String ownerPackageName,
+            @VerificationState int localVerificationState) {
+        validateVerificationState(localVerificationState);
+
+        final Bundle extras = new Bundle();
+        extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+        extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+        extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+        extras.putString(ContactKeys.OWNER_PACKAGE_NAME, Objects.requireNonNull(ownerPackageName));
+        extras.putInt(ContactKeys.LOCAL_VERIFICATION_STATE, localVerificationState);
+
+        final Bundle response = nullSafeCall(mContentResolver,
+                ContactKeys.UPDATE_CONTACT_KEY_LOCAL_VERIFICATION_STATE_METHOD, extras);
+
+        return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+    }
+
+    /**
      * Updates a contact key entry's remote verification state that belongs to the caller app.
      *
      * @param lookupKey the value that references the contact
@@ -275,6 +314,45 @@
         return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
     }
 
+    /**
+     * Updates a 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 updateContactKeyRemoteVerificationState(@NonNull String lookupKey,
+            @NonNull String deviceId,
+            @NonNull String accountId,
+            @NonNull String ownerPackageName,
+            @VerificationState int remoteVerificationState) {
+        validateVerificationState(remoteVerificationState);
+
+        final Bundle extras = new Bundle();
+        extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
+        extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+        extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+        extras.putString(ContactKeys.OWNER_PACKAGE_NAME, Objects.requireNonNull(ownerPackageName));
+        extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
+
+        final Bundle response = nullSafeCall(mContentResolver,
+                ContactKeys.UPDATE_CONTACT_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
+
+        return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+    }
+
+
     private static void validateVerificationState(int verificationState) {
         if (verificationState != UNVERIFIED
                 && verificationState != VERIFICATION_FAILED
@@ -297,12 +375,12 @@
     public boolean removeContactKey(@NonNull String lookupKey,
             @NonNull String deviceId,
             @NonNull String accountId) {
-        Bundle extras = new Bundle();
+        final Bundle extras = new Bundle();
         extras.putString(ContactKeys.LOOKUP_KEY, Objects.requireNonNull(lookupKey));
         extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
         extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
 
-        Bundle response = nullSafeCall(mContentResolver,
+        final Bundle response = nullSafeCall(mContentResolver,
                 ContactKeys.REMOVE_CONTACT_KEY_METHOD, extras);
 
         return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
@@ -369,6 +447,41 @@
     }
 
     /**
+     * Updates a 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 updateSelfKeyRemoteVerificationState(@NonNull String deviceId,
+            @NonNull String accountId,
+            @NonNull String ownerPackageName,
+            @VerificationState int remoteVerificationState) {
+        validateVerificationState(remoteVerificationState);
+
+        Bundle extras = new Bundle();
+        extras.putString(ContactKeys.DEVICE_ID, Objects.requireNonNull(deviceId));
+        extras.putString(ContactKeys.ACCOUNT_ID, Objects.requireNonNull(accountId));
+        extras.putString(ContactKeys.OWNER_PACKAGE_NAME, Objects.requireNonNull(ownerPackageName));
+        extras.putInt(ContactKeys.REMOTE_VERIFICATION_STATE, remoteVerificationState);
+
+        Bundle response = nullSafeCall(mContentResolver,
+                ContactKeys.UPDATE_SELF_KEY_REMOTE_VERIFICATION_STATE_METHOD, extras);
+
+        return response != null && response.getBoolean(ContactKeys.KEY_UPDATED_ROWS);
+    }
+
+    /**
      * Maximum size of a contact key.
      */
     public static int getMaxKeySizeBytes() {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 5fad4c6..21d2bf2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -881,6 +881,19 @@
         android:description="@string/permdesc_writeContacts"
       android:protectionLevel="dangerous" />
 
+    <!-- Allows an app to update the verification status of E2EE contact keys owned by other apps.
+        <p>This permission is only granted to system apps.
+        <p>Protection level: signature|privileged
+        @SystemApi
+        @hide
+        @FlaggedApi("android.provider.user_keys")
+    -->
+    <permission android:name="android.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS"
+                android:permissionGroup="android.permission-group.UNDEFINED"
+                android:label="@string/permlab_writeVerificationStateE2eeContactKeys"
+                android:description="@string/permdesc_writeVerificationStateE2eeContactKeys"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to set default account for new contacts.
         <p> This permission is only granted to system applications fulfilling the Contacts app role.
         <p>Protection level: internal|role
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index be059f1..ec47313 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2227,6 +2227,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
     <string name="permdesc_updatePackagesWithoutUserAction">Allows the holder to update the app it previously installed without user action</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_writeVerificationStateE2eeContactKeys">update the verification states of E2EE contact keys owned by other apps</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
+    <string name="permdesc_writeVerificationStateE2eeContactKeys">Allows the app to update the verification states of E2EE contact keys owned by other apps</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2873428..d4c2c2b 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -571,6 +571,8 @@
         <!-- Permission required for BinaryTransparencyService shell API and host test -->
         <permission name="android.permission.GET_BACKGROUND_INSTALLED_PACKAGES" />
         <permission name="android.permission.READ_SYSTEM_GRAMMATICAL_GENDER"/>
+        <!-- Permissions required for CTS test - CtsContactKeysProviderPrivilegedApp -->
+        <permission name="android.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 95e0e1b..32c7433 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -908,6 +908,9 @@
     <!-- Permissions required for CTS test - CtsPermissionUiTestCases -->
     <uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
 
+    <!-- Permissions required for CTS test - CtsContactKeysProviderPrivilegedApp -->
+    <uses-permission android:name="android.permission.WRITE_VERIFICATION_STATE_E2EE_CONTACT_KEYS"/>
+
     <application
         android:label="@string/app_label"
         android:theme="@android:style/Theme.DeviceDefault.DayNight"