Merge "[CDM] Support Perm Sync toggle setting" into udc-qpr-dev
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4dea4a7..b17dfa6 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -36,6 +36,7 @@
 import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.companion.datatransfer.PermissionSyncRequest;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -177,7 +178,7 @@
     public @interface DataSyncTypes {}
 
     /**
-     * Used by {@link #enableSystemDataSync(int, int)}}.
+     * Used by {@link #enableSystemDataSyncForTypes(int, int)}}.
      * Sync call metadata like muting, ending and silencing a call.
      *
      */
@@ -529,6 +530,39 @@
     }
 
     /**
+     * @hide
+     */
+    public void enablePermissionsSync(int associationId) {
+        try {
+            mService.enablePermissionsSync(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void disablePermissionsSync(int associationId) {
+        try {
+            mService.disablePermissionsSync(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
+        try {
+            return mService.getPermissionSyncRequest(associationId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * <p>Calling this API requires a uses-feature
      * {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
      *
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index b5e2670..4d05a28 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -24,6 +24,7 @@
 import android.companion.ISystemDataTransferCallback;
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
+import android.companion.datatransfer.PermissionSyncRequest;
 import android.content.ComponentName;
 
 /**
@@ -101,5 +102,11 @@
 
     void disableSystemDataSync(int associationId, int flags);
 
+    void enablePermissionsSync(int associationId);
+
+    void disablePermissionsSync(int associationId);
+
+    PermissionSyncRequest getPermissionSyncRequest(int associationId);
+
     void enableSecureTransport(boolean enabled);
 }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 9d6283b8..2511b50 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -64,6 +64,7 @@
 import android.companion.IOnMessageReceivedListener;
 import android.companion.IOnTransportsChangedListener;
 import android.companion.ISystemDataTransferCallback;
+import android.companion.datatransfer.PermissionSyncRequest;
 import android.companion.utils.FeatureUtils;
 import android.content.ComponentName;
 import android.content.Context;
@@ -794,6 +795,24 @@
         }
 
         @Override
+        public void enablePermissionsSync(int associationId) {
+            getAssociationWithCallerChecks(associationId);
+            mSystemDataTransferProcessor.enablePermissionsSync(associationId);
+        }
+
+        @Override
+        public void disablePermissionsSync(int associationId) {
+            getAssociationWithCallerChecks(associationId);
+            mSystemDataTransferProcessor.disablePermissionsSync(associationId);
+        }
+
+        @Override
+        public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
+            getAssociationWithCallerChecks(associationId);
+            return mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+        }
+
+        @Override
         public void enableSecureTransport(boolean enabled) {
             mTransportManager.enableSecureTransport(enabled);
         }
@@ -934,7 +953,7 @@
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver)
                 throws RemoteException {
             new CompanionDeviceShellCommand(CompanionDeviceManagerService.this, mAssociationStore,
-                    mDevicePresenceMonitor, mTransportManager, mSystemDataTransferRequestStore,
+                    mDevicePresenceMonitor, mTransportManager, mSystemDataTransferProcessor,
                     mAssociationRequestsProcessor)
                     .exec(this, in, out, err, args, callback, resultReceiver);
         }
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 04fbab4..d56436b1 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -25,7 +25,7 @@
 import android.os.ShellCommand;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.server.companion.datatransfer.SystemDataTransferRequestStore;
+import com.android.server.companion.datatransfer.SystemDataTransferProcessor;
 import com.android.server.companion.datatransfer.contextsync.BitmapUtils;
 import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
 import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
@@ -43,20 +43,20 @@
     private final CompanionDevicePresenceMonitor mDevicePresenceMonitor;
     private final CompanionTransportManager mTransportManager;
 
-    private final SystemDataTransferRequestStore mSystemDataTransferRequestStore;
+    private final SystemDataTransferProcessor mSystemDataTransferProcessor;
     private final AssociationRequestsProcessor mAssociationRequestsProcessor;
 
     CompanionDeviceShellCommand(CompanionDeviceManagerService service,
             AssociationStoreImpl associationStore,
             CompanionDevicePresenceMonitor devicePresenceMonitor,
             CompanionTransportManager transportManager,
-            SystemDataTransferRequestStore systemDataTransferRequestStore,
+            SystemDataTransferProcessor systemDataTransferProcessor,
             AssociationRequestsProcessor associationRequestsProcessor) {
         mService = service;
         mAssociationStore = associationStore;
         mDevicePresenceMonitor = devicePresenceMonitor;
         mTransportManager = transportManager;
-        mSystemDataTransferRequestStore = systemDataTransferRequestStore;
+        mSystemDataTransferProcessor = systemDataTransferProcessor;
         mAssociationRequestsProcessor = associationRequestsProcessor;
     }
 
@@ -265,16 +265,47 @@
                     break;
                 }
 
-                case "allow-permission-sync": {
-                    int userId = getNextIntArgRequired();
+                case "get-perm-sync-state": {
                     associationId = getNextIntArgRequired();
-                    boolean enabled = getNextBooleanArgRequired();
-                    PermissionSyncRequest request = new PermissionSyncRequest(associationId);
-                    request.setUserId(userId);
-                    request.setUserConsented(enabled);
-                    mSystemDataTransferRequestStore.writeRequest(userId, request);
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.println((request == null ? "null" : request.isUserConsented()));
+                    break;
                 }
-                break;
+
+                case "remove-perm-sync-state": {
+                    associationId = getNextIntArgRequired();
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.print((request == null ? "null" : request.isUserConsented()));
+                    mSystemDataTransferProcessor.removePermissionSyncRequest(associationId);
+                    request = mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    // should print " -> null"
+                    out.println(" -> " + (request == null ? "null" : request.isUserConsented()));
+                    break;
+                }
+
+                case "enable-perm-sync": {
+                    associationId = getNextIntArgRequired();
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.print((request == null ? "null" : request.isUserConsented()));
+                    mSystemDataTransferProcessor.enablePermissionsSync(associationId);
+                    request = mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.println(" -> " + request.isUserConsented()); // should print " -> true"
+                    break;
+                }
+
+                case "disable-perm-sync": {
+                    associationId = getNextIntArgRequired();
+                    PermissionSyncRequest request =
+                            mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.print((request == null ? "null" : request.isUserConsented()));
+                    mSystemDataTransferProcessor.disablePermissionsSync(associationId);
+                    request = mSystemDataTransferProcessor.getPermissionSyncRequest(associationId);
+                    out.println(" -> " + request.isUserConsented()); // should print " -> false"
+                    break;
+                }
 
                 default:
                     return handleDefaultCommands(cmd);
@@ -346,5 +377,14 @@
 
         pw.println("  create-emulated-transport <ASSOCIATION_ID>");
         pw.println("      Create an EmulatedTransport for testing purposes only");
+
+        pw.println("  enable-perm-sync <ASSOCIATION_ID>");
+        pw.println("      Enable perm sync for the association.");
+        pw.println("  disable-perm-sync <ASSOCIATION_ID>");
+        pw.println("      Disable perm sync for the association.");
+        pw.println("  get-perm-sync-state <ASSOCIATION_ID>");
+        pw.println("      Get perm sync state for the association.");
+        pw.println("  remove-perm-sync-state <ASSOCIATION_ID>");
+        pw.println("      Remove perm sync state for the association.");
     }
 }
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 800a3d9..aeb273b 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -138,6 +138,13 @@
             @UserIdInt int userId, int associationId) {
         if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) {
             Slog.i(LOG_TAG, "User consent Intent should be skipped. Returning null.");
+            // Auto enable perm sync for the allowlisted packages, but don't override user decision
+            PermissionSyncRequest request = getPermissionSyncRequest(associationId);
+            if (request == null) {
+                PermissionSyncRequest newRequest = new PermissionSyncRequest(associationId);
+                newRequest.setUserConsented(true);
+                mSystemDataTransferRequestStore.writeRequest(userId, newRequest);
+            }
             return null;
         }
 
@@ -184,29 +191,17 @@
         final AssociationInfo association = resolveAssociation(packageName, userId, associationId);
 
         // Check if the request has been consented by the user.
-        if (PackageUtils.isPackageAllowlisted(mContext, mPackageManager, packageName)) {
-            Slog.i(LOG_TAG, "Skip user consent check due to the same OEM package.");
-        } else {
-            List<SystemDataTransferRequest> storedRequests =
-                    mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
-                            associationId);
-            boolean hasConsented = false;
-            for (SystemDataTransferRequest storedRequest : storedRequests) {
-                if (storedRequest instanceof PermissionSyncRequest
-                        && storedRequest.isUserConsented()) {
-                    hasConsented = true;
-                    break;
-                }
+        PermissionSyncRequest request = getPermissionSyncRequest(associationId);
+        if (request == null || !request.isUserConsented()) {
+            String message =
+                    "User " + userId + " hasn't consented permission sync for associationId ["
+                            + associationId + ".";
+            Slog.e(LOG_TAG, message);
+            try {
+                callback.onError(message);
+            } catch (RemoteException ignored) {
             }
-            if (!hasConsented) {
-                String message = "User " + userId + " hasn't consented permission sync.";
-                Slog.e(LOG_TAG, message);
-                try {
-                    callback.onError(message);
-                } catch (RemoteException ignored) {
-                }
-                return;
-            }
+            return;
         }
 
         // Start permission sync
@@ -224,6 +219,66 @@
         }
     }
 
+    /**
+     * Enable perm sync for the association
+     */
+    public void enablePermissionsSync(int associationId) {
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+            request.setUserConsented(true);
+            mSystemDataTransferRequestStore.writeRequest(userId, request);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    /**
+     * Disable perm sync for the association
+     */
+    public void disablePermissionsSync(int associationId) {
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            PermissionSyncRequest request = new PermissionSyncRequest(associationId);
+            request.setUserConsented(false);
+            mSystemDataTransferRequestStore.writeRequest(userId, request);
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    /**
+     * Get perm sync request for the association.
+     */
+    @Nullable
+    public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
+        final long callingIdentityToken = Binder.clearCallingIdentity();
+        try {
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            List<SystemDataTransferRequest> requests =
+                    mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
+                            associationId);
+            for (SystemDataTransferRequest request : requests) {
+                if (request instanceof PermissionSyncRequest) {
+                    return (PermissionSyncRequest) request;
+                }
+            }
+            return null;
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentityToken);
+        }
+    }
+
+    /**
+     * Remove perm sync request for the association.
+     */
+    public void removePermissionSyncRequest(int associationId) {
+        int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+        mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
+    }
+
     private void onReceivePermissionRestore(byte[] message) {
         Slog.i(LOG_TAG, "Applying permissions.");
         // Start applying permissions
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index 720cefa..9f489e8 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -104,7 +104,7 @@
     }
 
     @NonNull
-    List<SystemDataTransferRequest> readRequestsByAssociationId(@UserIdInt int userId,
+    public List<SystemDataTransferRequest> readRequestsByAssociationId(@UserIdInt int userId,
             int associationId) {
         List<SystemDataTransferRequest> cachedRequests;
         synchronized (mLock) {