[Multi-User] Unregister stopped users in BMS

Adds a handler for system service callbacks when a user is stopped. This
currently only unregisters the user in bookkeeping but will be used in
the future for cleanup work such as cancelling scheduled jobs, etc.

Bug: 120212806
Test: 1) atest RunBackupFrameworksServicesRoboTests
2) atest TrampolineTest
3) Enable multi user + register user -> service started for user; stop
user -> user is unregistered

Change-Id: Ic712da646f961b9bb02c7d77d964a13345eca7ec
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f702d0f..7049744 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -124,6 +124,10 @@
         }
     }
 
+    // ---------------------------------------------
+    // USER LIFECYCLE CALLBACKS
+    // ---------------------------------------------
+
     /**
      * Starts the backup service for user {@code userId} by creating a new instance of {@link
      * UserBackupManagerService} and registering it with this service.
@@ -153,6 +157,12 @@
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
 
+    /** Stops the backup service for user {@code userId} when the user is stopped. */
+    @VisibleForTesting
+    protected void stopServiceForUser(int userId) {
+        mServiceUsers.remove(userId);
+    }
+
     SparseArray<UserBackupManagerService> getServiceUsers() {
         return mServiceUsers;
     }
@@ -820,5 +830,10 @@
             }
             sInstance.unlockUser(userId);
         }
+
+        @Override
+        public void onStopUser(int userId) {
+            sInstance.stopUser(userId);
+        }
     }
 }
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index e295c53a..8eb5207 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -198,6 +198,26 @@
     }
 
     /**
+     * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
+     * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
+     */
+    void stopUser(int userId) {
+        if (userId != UserHandle.USER_SYSTEM && !isMultiUserEnabled()) {
+            Slog.i(TAG, "Multi-user disabled, cannot stop service for user: " + userId);
+            return;
+        }
+
+        postToHandler(
+                () -> {
+                    BackupManagerService service = mService;
+                    if (service != null) {
+                        Slog.i(TAG, "Stopping service for user: " + userId);
+                        service.stopServiceForUser(userId);
+                    }
+                });
+    }
+
+    /**
      * Only privileged callers should be changing the backup state. This method only acts on {@link
      * UserHandle#USER_SYSTEM} and is a no-op if passed non-system users. Deactivating backup in the
      * system user also deactivates backup in all users.
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
index b253e0a..b8723c5 100644
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -175,6 +175,32 @@
         assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
     }
 
+    /** Test that the service unregisters users when stopped. */
+    @Test
+    public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception {
+        BackupManagerService backupManagerService =
+                createServiceAndRegisterUser(mUserOneId, mUserOneService);
+        backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+
+        backupManagerService.stopServiceForUser(mUserOneId);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(1);
+        assertThat(serviceUsers.get(mUserOneId)).isNull();
+        assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService);
+    }
+
+    /** Test that the service unregisters users when stopped. */
+    @Test
+    public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
+        BackupManagerService backupManagerService = createService();
+
+        backupManagerService.stopServiceForUser(mUserOneId);
+
+        SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getServiceUsers();
+        assertThat(serviceUsers.size()).isEqualTo(0);
+    }
+
     /**
      * Test that the backup services throws a {@link SecurityException} if the caller does not have
      * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
index 1f37722..e02a30d 100644
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
@@ -163,6 +163,36 @@
     }
 
     @Test
+    public void stopUser_whenMultiUserSettingDisabled_callsBackupManagerServiceForSystemUser() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+        mTrampoline.initializeService();
+
+        mTrampoline.stopUser(UserHandle.USER_SYSTEM);
+
+        verify(mBackupManagerServiceMock).stopServiceForUser(UserHandle.USER_SYSTEM);
+    }
+
+    @Test
+    public void stopUser_whenMultiUserSettingDisabled_isIgnoredForNonSystemUser() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 0);
+        mTrampoline.initializeService();
+
+        mTrampoline.stopUser(10);
+
+        verify(mBackupManagerServiceMock, never()).stopServiceForUser(10);
+    }
+
+    @Test
+    public void stopUser_whenMultiUserSettingEnabled_callsBackupManagerServiceForNonSystemUser() {
+        Settings.Global.putInt(mContentResolver, Settings.Global.BACKUP_MULTI_USER_ENABLED, 1);
+        mTrampoline.initializeService();
+
+        mTrampoline.stopUser(10);
+
+        verify(mBackupManagerServiceMock).stopServiceForUser(10);
+    }
+
+    @Test
     public void initializeService_successfullyInitializesBackupService() {
         mTrampoline.initializeService();