Add role initialization to RoleControllerService.

This change adds RoleManager.setRoleNamesFromController() to allow
RoleControllerService to initialize the roles for a user. This change
also fixes the persistence of roles by calling writeAsyncLocked().

Bug: 110557011
Test: build
Change-Id: I921b6aa691478ca4c0dd1a75fc929a96ce1e7df5
diff --git a/api/system-current.txt b/api/system-current.txt
index ee767af0..f8bb3c4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -867,6 +867,7 @@
     method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
     method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
     method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
+    method public void setRoleNamesFromController(java.util.List<java.lang.String>);
     field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME";
   }
 
diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl
index 2cf13ec2..3ca8ec0 100644
--- a/core/java/android/app/role/IRoleManager.aidl
+++ b/core/java/android/app/role/IRoleManager.aidl
@@ -37,6 +37,8 @@
 
     void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
 
+    void setRoleNamesFromController(in List<String> roleNames);
+
     boolean addRoleHolderFromController(in String roleName, in String packageName);
 
     boolean removeRoleHolderFromController(in String roleName, in String packageName);
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index ef86b01..20e7f27 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -92,8 +92,8 @@
      *
      * @hide
      */
-    public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER =
-            "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER";
+    public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
+            "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
 
     @NonNull
     private final Context mContext;
@@ -342,12 +342,36 @@
     }
 
     /**
+     * Set the names of all the available roles. Should only be called from
+     * {@link android.rolecontrollerservice.RoleControllerService}.
+     * <p>
+     * <strong>Note:</strong> Using this API requires holding
+     * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
+     *
+     * @param roleNames the names of all the available roles
+     *
+     * @throws IllegalArgumentException if the list of role names is {@code null}.
+     *
+     * @hide
+     */
+    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
+    @SystemApi
+    public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+        Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+        try {
+            mService.setRoleNamesFromController(roleNames);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Add a specific application to the holders of a role, only modifying records inside
      * {@link RoleManager}. Should only be called from
      * {@link android.rolecontrollerservice.RoleControllerService}.
      * <p>
      * <strong>Note:</strong> Using this API requires holding
-     * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+     * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
      *
      * @param roleName the name of the role to add the role holder for
      * @param packageName the package name of the application to add to the role holders
@@ -362,7 +386,7 @@
      *
      * @hide
      */
-    @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
     public boolean addRoleHolderFromController(@NonNull String roleName,
             @NonNull String packageName) {
@@ -381,7 +405,7 @@
      * {@link android.rolecontrollerservice.RoleControllerService}.
      * <p>
      * <strong>Note:</strong> Using this API requires holding
-     * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}.
+     * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
      *
      * @param roleName the name of the role to remove the role holder for
      * @param packageName the package name of the application to remove from the role holders
@@ -396,7 +420,7 @@
      *
      * @hide
      */
-    @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER)
+    @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
     @SystemApi
     public boolean removeRoleHolderFromController(@NonNull String roleName,
             @NonNull String packageName) {
diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
index 538f4cf..cb89780 100644
--- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java
+++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java
@@ -48,6 +48,7 @@
     static final boolean DEBUG = false;
     private static final String LOG_TAG = RemoteRoleControllerService.class.getSimpleName();
 
+    // TODO: STOPSHIP: This isn't the right thread, as we are also using it to write to disk.
     @NonNull
     private static final Handler sCallbackHandler = BackgroundThread.getHandler();
 
diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java
index d01e762..4124210 100644
--- a/services/core/java/com/android/server/role/RoleManagerService.java
+++ b/services/core/java/com/android/server/role/RoleManagerService.java
@@ -284,12 +284,26 @@
         }
 
         @Override
+        public void setRoleNamesFromController(@NonNull List<String> roleNames) {
+            Preconditions.checkNotNull(roleNames, "roleNames cannot be null");
+            getContext().enforceCallingOrSelfPermission(
+                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
+                    "setRoleNamesFromController");
+
+            int userId = UserHandle.getCallingUserId();
+            synchronized (mLock) {
+                RoleUserState userState = getUserStateLocked(userId);
+                userState.setRoleNamesLocked(roleNames);
+            }
+        }
+
+        @Override
         public boolean addRoleHolderFromController(@NonNull String roleName,
                 @NonNull String packageName) {
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
             Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
             getContext().enforceCallingOrSelfPermission(
-                    RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
                     "addRoleHolderFromController");
 
             int userId = UserHandle.getCallingUserId();
@@ -305,7 +319,7 @@
             Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
             Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
             getContext().enforceCallingOrSelfPermission(
-                    RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER,
+                    RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
                     "removeRoleHolderFromController");
 
             int userId = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 68e737e..9c43f4d 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -45,6 +45,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.util.List;
 
 /**
  * Stores the state of roles for a user.
@@ -101,7 +102,11 @@
     @GuardedBy("RoleManagerService.mLock")
     public void setVersionLocked(int version) {
         throwIfDestroyedLocked();
+        if (mVersion == version) {
+            return;
+        }
         mVersion = version;
+        writeAsyncLocked();
     }
 
     /**
@@ -132,6 +137,41 @@
     }
 
     /**
+     * Set the names of all available roles.
+     *
+     * @param roleNames the names of all the available roles
+     */
+    @GuardedBy("RoleManagerService.mLock")
+    public void setRoleNamesLocked(@NonNull List<String> roleNames) {
+        throwIfDestroyedLocked();
+        boolean changed = false;
+        for (int i = mRoles.size() - 1; i >= 0; i--) {
+            String roleName = mRoles.keyAt(i);
+            if (!roleNames.contains(roleName)) {
+                ArraySet<String> packageNames = mRoles.valueAt(i);
+                if (!packageNames.isEmpty()) {
+                    Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up, role: "
+                            + roleName + ", holders: " + packageNames);
+                }
+                mRoles.removeAt(i);
+                changed = true;
+            }
+        }
+        int roleNamesSize = roleNames.size();
+        for (int i = 0; i < roleNamesSize; i++) {
+            String roleName = roleNames.get(i);
+            if (!mRoles.containsKey(roleName)) {
+                mRoles.put(roleName, new ArraySet<>());
+                Slog.i(LOG_TAG, "Added new role: " + roleName);
+                changed = true;
+            }
+        }
+        if (changed) {
+            writeAsyncLocked();
+        }
+    }
+
+    /**
      * Add a holder to a role.
      *
      * @param roleName the name of the role to add the holder to
@@ -150,7 +190,10 @@
                     + ", package: " + packageName);
             return false;
         }
-        roleHolders.add(packageName);
+        boolean changed = roleHolders.add(packageName);
+        if (changed) {
+            writeAsyncLocked();
+        }
         return true;
     }
 
@@ -173,7 +216,10 @@
                     + ", package: " + packageName);
             return false;
         }
-        roleHolders.remove(packageName);
+        boolean changed = roleHolders.remove(packageName);
+        if (changed) {
+            writeAsyncLocked();
+        }
         return true;
     }
 
@@ -181,7 +227,7 @@
      * Schedule writing the state to file.
      */
     @GuardedBy("RoleManagerService.mLock")
-    public void writeAsyncLocked() {
+    private void writeAsyncLocked() {
         throwIfDestroyedLocked();
         int version = mVersion;
         ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
@@ -192,6 +238,7 @@
             roles.put(roleName, roleHolders);
         }
         mWriteHandler.removeCallbacksAndMessages(null);
+        // TODO: Throttle writes.
         mWriteHandler.sendMessage(PooledLambda.obtainMessage(
                 RoleUserState::writeSync, this, version, roles));
     }