Do cleanup when Stopping users

Mark user 0 as initialized, otherwise it will show up as
uninitialized when viewed from secondary user if never switched to user 0.

Bug: 7301595

Also clean up any users that were in the process of being removed, if device
crashes at a bad time.

Change-Id: Ic16a6c9ccb6a64b7463725f6cc279335a821fcd5
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index c18fe0e..b44340f 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -98,21 +98,19 @@
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
+        userFilter.addAction(Intent.ACTION_USER_STOPPING);
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+                if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+                    onUserRemoved(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                            UserHandle.USER_NULL));
+                } else if (Intent.ACTION_USER_STOPPING.equals(intent.getAction())) {
+                    onUserStopping(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                            UserHandle.USER_NULL));
+                }
             }
         }, userFilter);
-
-        IntentFilter userStopFilter = new IntentFilter();
-        userStopFilter.addAction(Intent.ACTION_USER_STOPPED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                onUserStopped(getSendingUserId());
-            }
-        }, UserHandle.ALL, userFilter, null, null);
     }
 
     /**
@@ -203,7 +201,7 @@
         synchronized (mAppWidgetServices) {
             AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
             mAppWidgetServices.remove(userId);
-    
+
             if (impl == null) {
                 AppWidgetServiceImpl.getSettingsFile(userId).delete();
             } else {
@@ -212,7 +210,15 @@
         }
     }
 
-    public void onUserStopped(int userId) {
+    public void onUserStopping(int userId) {
+        if (userId < 1) return;
+        synchronized (mAppWidgetServices) {
+            AppWidgetServiceImpl impl = mAppWidgetServices.get(userId);
+            if (impl != null) {
+                mAppWidgetServices.remove(userId);
+                impl.onUserStopping();
+            }
+        }
     }
 
     private AppWidgetServiceImpl getImplForUser(int userId) {
@@ -322,11 +328,11 @@
             String action = intent.getAction();
             // Slog.d(TAG, "received " + action);
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
-                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                 if (userId >= 0) {
                     getImplForUser(userId).sendInitialBroadcasts();
                 } else {
-                    Slog.w(TAG, "Not user handle supplied in " + intent);
+                    Slog.w(TAG, "Incorrect user handle supplied in " + intent);
                 }
             } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
                 for (int i = 0; i < mAppWidgetServices.size(); i++) {
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 9fea6f3..6a313a0 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -1778,13 +1778,16 @@
         return new AtomicFile(settingsFile);
     }
 
-    void onUserRemoved() {
+    void onUserStopping() {
         // prune the ones we don't want to keep
         int N = mInstalledProviders.size();
         for (int i = N - 1; i >= 0; i--) {
             Provider p = mInstalledProviders.get(i);
             cancelBroadcasts(p);
         }
+    }
+
+    void onUserRemoved() {
         getSettingsFile(mUserId).delete();
     }
 
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index e0f3814..a02fc8d 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -458,15 +458,21 @@
 
         IntentFilter userFilter = new IntentFilter();
         userFilter.addAction(Intent.ACTION_USER_REMOVED);
+        userFilter.addAction(Intent.ACTION_USER_STOPPING);
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
                 String action = intent.getAction();
                 if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                    onRemoveUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                            UserHandle.USER_NULL));
+                } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+                    onStoppingUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                            UserHandle.USER_NULL));
                 }
             }
         }, userFilter);
+
         try {
             ActivityManagerNative.getDefault().registerUserSwitchObserver(
                     new IUserSwitchObserver.Stub() {
@@ -491,13 +497,24 @@
         }
     }
 
-    void removeUser(int userId) {
+    void onStoppingUser(int userId) {
+        if (userId < 1) return;
         synchronized (mLock) {
             WallpaperData wallpaper = mWallpaperMap.get(userId);
             if (wallpaper != null) {
-                wallpaper.wallpaperObserver.stopWatching();
+                if (wallpaper.wallpaperObserver != null) {
+                    wallpaper.wallpaperObserver.stopWatching();
+                    wallpaper.wallpaperObserver = null;
+                }
                 mWallpaperMap.remove(userId);
             }
+        }
+    }
+
+    void onRemoveUser(int userId) {
+        if (userId < 1) return;
+        synchronized (mLock) {
+            onStoppingUser(userId);
             File wallpaperFile = new File(getWallpaperDir(userId), WALLPAPER);
             wallpaperFile.delete();
             File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 4f9375a..77e6c03 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -149,7 +149,7 @@
                         -1, -1);
                 mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
                 readUserListLocked();
-                // Prune out any partially created users.
+                // Prune out any partially created/partially removed users.
                 ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
                 for (int i = 0; i < mUsers.size(); i++) {
                     UserInfo ui = mUsers.valueAt(i);
@@ -459,7 +459,7 @@
     private void fallbackToSingleUserLocked() {
         // Create the primary user
         UserInfo primary = new UserInfo(0, "Primary", null,
-                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
+                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
         mUsers.put(0, primary);
         mNextSerialNumber = MIN_USER_ID;
         updateUserIdsLocked();
@@ -703,6 +703,11 @@
                 return false;
             }
             mRemovingUserIds.add(userHandle);
+            // Set this to a partially created user, so that the user will be purged
+            // on next startup, in case the runtime stops now before stopping and
+            // removing the user completely.
+            user.partial = true;
+            writeUserLocked(user);
         }
 
         int res;