Fix crashes when quickly adding and removing users

Make USER_REMOVED an ordered broadcast and send it before the user's
state is completely removed from the system. This gives services the
opportunity to clean up their state, while still having access to the
user's directory and UserInfo object (such as serial number).

Tell SyncManager to skip over dying/partially created users.

Improve UserManager tests, waiting for users to be removed fully.

Bug: 7382252

Change-Id: I93cfb39c9efe6f15087bf83c569a2d154ef27168
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 072dd33..fb93d05 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -19,9 +19,11 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
+import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.IStopUserCallback;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -736,21 +738,38 @@
         return res == ActivityManager.USER_OP_SUCCESS;
     }
 
-    void finishRemoveUser(int userHandle) {
+    void finishRemoveUser(final int userHandle) {
         if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
-        synchronized (mInstallLock) {
-            synchronized (mPackagesLock) {
-                removeUserStateLocked(userHandle);
-            }
-        }
-        if (DBG) Slog.i(LOG_TAG, "Removed user " + userHandle + ", sending broadcast");
-        // Let other services shutdown any activity
+        // Let other services shutdown any activity and clean up their state before completely
+        // wiping the user's system directory and removing from the user list
         long ident = Binder.clearCallingIdentity();
         try {
             Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
             addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
-            mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
-                    android.Manifest.permission.MANAGE_USERS);
+            mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
+                    android.Manifest.permission.MANAGE_USERS,
+
+                    new BroadcastReceiver() {
+                        @Override
+                        public void onReceive(Context context, Intent intent) {
+                            if (DBG) {
+                                Slog.i(LOG_TAG,
+                                        "USER_REMOVED broadcast sent, cleaning up user data "
+                                        + userHandle);
+                            }
+                            new Thread() {
+                                public void run() {
+                                    synchronized (mInstallLock) {
+                                        synchronized (mPackagesLock) {
+                                            removeUserStateLocked(userHandle);
+                                        }
+                                    }
+                                }
+                            }.start();
+                        }
+                    },
+
+                    null, Activity.RESULT_OK, null, null);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }