Add delayed writing for RoleUserState.

This change adds delayed writing for RoleUserState.

Bug: 110557011
Test: manual
Change-Id: If537e9523b96d9e4c6157e9b6e95b956e5d3c08f
diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java
index 3e3e156..ce05974 100644
--- a/services/core/java/com/android/server/role/RoleUserState.java
+++ b/services/core/java/com/android/server/role/RoleUserState.java
@@ -60,6 +60,9 @@
 
     private static final String ROLES_FILE_NAME = "roles.xml";
 
+    private static final long WRITE_DELAY_MILLIS = 200;
+    private static final long MAX_WRITE_DELAY_MILLIS = 2000;
+
     private static final String TAG_ROLES = "roles";
     private static final String TAG_ROLE = "role";
     private static final String TAG_HOLDER = "holder";
@@ -85,6 +88,9 @@
     private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
 
     @GuardedBy("RoleManagerService.mLock")
+    private long mWritePendingSinceMillis;
+
+    @GuardedBy("RoleManagerService.mLock")
     private boolean mDestroyed;
 
     @NonNull
@@ -282,14 +288,31 @@
         for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
             String roleName = mRoles.keyAt(i);
             ArraySet<String> roleHolders = mRoles.valueAt(i);
+
             roleHolders = new ArraySet<>(roleHolders);
             roles.put(roleName, roleHolders);
         }
 
-        mWriteHandler.removeCallbacksAndMessages(null);
-        // TODO: Throttle writes.
-        mWriteHandler.sendMessage(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
-                mVersion, mLastGrantPackagesHash, roles));
+        long currentTimeMillis = System.currentTimeMillis();
+        long writeDelayMillis;
+        if (!mWriteHandler.hasMessagesOrCallbacks()) {
+            mWritePendingSinceMillis = currentTimeMillis;
+            writeDelayMillis = WRITE_DELAY_MILLIS;
+        } else {
+            mWriteHandler.removeCallbacksAndMessages(null);
+            long writePendingDurationMillis = currentTimeMillis - mWritePendingSinceMillis;
+            if (writePendingDurationMillis >= MAX_WRITE_DELAY_MILLIS) {
+                writeDelayMillis = 0;
+            } else {
+                long maxWriteDelayMillis = Math.max(MAX_WRITE_DELAY_MILLIS
+                        - writePendingDurationMillis, 0);
+                writeDelayMillis = Math.min(WRITE_DELAY_MILLIS, maxWriteDelayMillis);
+            }
+        }
+
+        mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
+                mVersion, mLastGrantPackagesHash, roles), writeDelayMillis);
+        Slog.i(LOG_TAG, "Scheduled writing roles.xml");
     }
 
     @WorkerThread
@@ -310,6 +333,7 @@
 
             serializer.endDocument();
             atomicFile.finishWrite(out);
+            Slog.i(LOG_TAG, "Wrote roles.xml successfully");
         } catch (IllegalArgumentException | IllegalStateException | IOException e) {
             Slog.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup", e);
             if (out != null) {
@@ -367,6 +391,7 @@
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(in, null);
             parseXmlLocked(parser);
+            Slog.i(LOG_TAG, "Read roles.xml successfully");
         } catch (FileNotFoundException e) {
             Slog.i(LOG_TAG, "roles.xml not found");
         } catch (XmlPullParserException | IOException e) {