Merge "[Perm Sync] Add an API for the companion app to get the perm sync consent" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 5b339fa..e11f8ce 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9676,6 +9676,7 @@
     method @Deprecated @NonNull public java.util.List<java.lang.String> getAssociations();
     method @NonNull public java.util.List<android.companion.AssociationInfo> getMyAssociations();
     method @Deprecated public boolean hasNotificationAccess(android.content.ComponentName);
+    method @FlaggedApi("android.companion.perm_sync_user_consent") public boolean isPermissionTransferUserConsented(int);
     method public void requestNotificationAccess(android.content.ComponentName);
     method @FlaggedApi("android.companion.association_tag") public void setAssociationTag(int, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE) public void startObservingDevicePresence(@NonNull String) throws android.companion.DeviceNotAssociatedException;
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index e0ce917..e4a03c5 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -1358,6 +1358,36 @@
     }
 
     /**
+     * Return the current state of consent for permission transfer for the association.
+     * True if the user has allowed permission transfer for the association, false otherwise.
+     *
+     * <p>
+     * Note: The initial user consent is collected via
+     * {@link #buildPermissionTransferUserConsentIntent(int) a permission transfer user consent dialog}.
+     * After the user has made their initial selection, they can toggle the permission transfer
+     * feature in the settings.
+     * This method always returns the state of the toggle setting.
+     * </p>
+     *
+     * @param associationId The unique {@link AssociationInfo#getId ID} assigned to the association
+     *                      of the companion device recorded by CompanionDeviceManager
+     * @return True if the user has consented to the permission transfer, or false otherwise.
+     * @throws DeviceNotAssociatedException Exception if the companion device is not associated with
+     *                                      the user or the calling app.
+     */
+    @UserHandleAware
+    @FlaggedApi(Flags.FLAG_PERM_SYNC_USER_CONSENT)
+    public boolean isPermissionTransferUserConsented(int associationId) {
+        try {
+            return mService.isPermissionTransferUserConsented(mContext.getOpPackageName(),
+                    mContext.getUserId(), associationId);
+        } catch (RemoteException e) {
+            ExceptionUtils.propagateIfInstanceOf(e.getCause(), DeviceNotAssociatedException.class);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Start system data transfer which has been previously approved by the user.
      *
      * <p>Before calling this method, the app needs to make sure there's a communication channel
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index c5a1988..22689f3 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -97,6 +97,8 @@
     PendingIntent buildPermissionTransferUserConsentIntent(String callingPackage, int userId,
         int associationId);
 
+    boolean isPermissionTransferUserConsented(String callingPackage, int userId, int associationId);
+
     void startSystemDataTransfer(String packageName, int userId, int associationId,
         in ISystemDataTransferCallback callback);
 
diff --git a/core/java/android/companion/flags.aconfig b/core/java/android/companion/flags.aconfig
index 6e33dff..9e410b8 100644
--- a/core/java/android/companion/flags.aconfig
+++ b/core/java/android/companion/flags.aconfig
@@ -27,3 +27,10 @@
     description: "Enable device presence APIs"
     bug: "283000075"
 }
+
+flag {
+    name: "perm_sync_user_consent"
+    namespace: "companion"
+    description: "Expose perm sync user consent API"
+    bug: "309528663"
+}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 71a1f01..cce596b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -833,6 +833,13 @@
         }
 
         @Override
+        public boolean isPermissionTransferUserConsented(String packageName, int userId,
+                int associationId) {
+            return mSystemDataTransferProcessor.isPermissionTransferUserConsented(packageName,
+                    userId, associationId);
+        }
+
+        @Override
         public void startSystemDataTransfer(String packageName, int userId, int associationId,
                 ISystemDataTransferCallback callback) {
             mSystemDataTransferProcessor.startSystemDataTransfer(packageName, userId,
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index e5c847a..bd646fa 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -120,7 +120,8 @@
      * Resolve the requested association, throwing if the caller doesn't have
      * adequate permissions.
      */
-    private @NonNull AssociationInfo resolveAssociation(String packageName, int userId,
+    @NonNull
+    private AssociationInfo resolveAssociation(String packageName, int userId,
             int associationId) {
         AssociationInfo association = mAssociationStore.getAssociationById(associationId);
         association = PermissionsUtils.sanitizeWithCallerChecks(mContext, association);
@@ -133,6 +134,20 @@
     }
 
     /**
+     * Return whether the user has consented to the permission transfer for the association.
+     */
+    public boolean isPermissionTransferUserConsented(String packageName, @UserIdInt int userId,
+            int associationId) {
+        resolveAssociation(packageName, userId, associationId);
+
+        PermissionSyncRequest request = getPermissionSyncRequest(associationId);
+        if (request == null) {
+            return false;
+        }
+        return request.isUserConsented();
+    }
+
+    /**
      * Build a PendingIntent of permission sync user consent dialog
      */
     public PendingIntent buildPermissionTransferUserConsentIntent(String packageName,