Introduced UserDataPreparer class

Extracted the logic for preparing and destroying user data into a new
class. Unit tests will be added in a follow up cl.

Test: manual + UserManagerTest
Bug: 34736064
Change-Id: I4df7189c4ad8703cb34f54da21873b9ec83589c5
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index abf3526..745b91f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -831,6 +831,7 @@
     private List<String> mKeepUninstalledPackages;
 
     private UserManagerInternal mUserManagerInternal;
+    private final UserDataPreparer mUserDataPreparer;
 
     private File mCacheDir;
 
@@ -2262,8 +2263,8 @@
             mEphemeralInstallDir = new File(dataDir, "app-ephemeral");
             mAsecInternalPath = new File(dataDir, "app-asec").getPath();
             mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
-
-            sUserManager = new UserManagerService(context, this, mPackages);
+            mUserDataPreparer = new UserDataPreparer(mInstaller, mInstallLock, mContext, mOnlyCore);
+            sUserManager = new UserManagerService(context, this, mUserDataPreparer, mPackages);
 
             // Propagate permission configuration in to package manager.
             ArrayMap<String, SystemConfig.PermissionEntry> permConfig
@@ -21256,101 +21257,6 @@
     }
 
     /**
-     * Prepare storage areas for given user on all mounted devices.
-     */
-    void prepareUserData(int userId, int userSerial, int flags) {
-        synchronized (mInstallLock) {
-            final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-                final String volumeUuid = vol.getFsUuid();
-                prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
-            }
-        }
-    }
-
-    private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
-            boolean allowRecover) {
-        // Prepare storage and verify that serial numbers are consistent; if
-        // there's a mismatch we need to destroy to avoid leaking data
-        final StorageManager storage = mContext.getSystemService(StorageManager.class);
-        try {
-            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
-
-            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && !mOnlyCore) {
-                UserManagerService.enforceSerialNumber(
-                        Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
-                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
-                    UserManagerService.enforceSerialNumber(
-                            Environment.getDataSystemDeDirectory(userId), userSerial);
-                }
-            }
-            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
-                UserManagerService.enforceSerialNumber(
-                        Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
-                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
-                    UserManagerService.enforceSerialNumber(
-                            Environment.getDataSystemCeDirectory(userId), userSerial);
-                }
-            }
-
-            synchronized (mInstallLock) {
-                mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
-            }
-        } catch (Exception e) {
-            logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
-                    + " because we failed to prepare: " + e);
-            destroyUserDataLI(volumeUuid, userId,
-                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
-
-            if (allowRecover) {
-                // Try one last time; if we fail again we're really in trouble
-                prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
-            }
-        }
-    }
-
-    /**
-     * Destroy storage areas for given user on all mounted devices.
-     */
-    void destroyUserData(int userId, int flags) {
-        synchronized (mInstallLock) {
-            final StorageManager storage = mContext.getSystemService(StorageManager.class);
-            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
-                final String volumeUuid = vol.getFsUuid();
-                destroyUserDataLI(volumeUuid, userId, flags);
-            }
-        }
-    }
-
-    private void destroyUserDataLI(String volumeUuid, int userId, int flags) {
-        final StorageManager storage = mContext.getSystemService(StorageManager.class);
-        try {
-            // Clean up app data, profile data, and media data
-            mInstaller.destroyUserData(volumeUuid, userId, flags);
-
-            // Clean up system data
-            if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
-                if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
-                    FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
-                    FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
-                    FileUtils.deleteContentsAndDir(Environment.getDataMiscDeDirectory(userId));
-                }
-                if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
-                    FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
-                    FileUtils.deleteContentsAndDir(Environment.getDataMiscCeDirectory(userId));
-                }
-            }
-
-            // Data with special labels is now gone, so finish the job
-            storage.destroyUserStorage(volumeUuid, userId, flags);
-
-        } catch (Exception e) {
-            logCriticalInfo(Log.WARN,
-                    "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
-        }
-    }
-
-    /**
      * Examine all users present on given mounted volume, and destroy data
      * belonging to users that are no longer valid, or whose user ID has been
      * recycled.
@@ -21397,7 +21303,7 @@
 
             if (destroyUser) {
                 synchronized (mInstallLock) {
-                    destroyUserDataLI(volumeUuid, userId,
+                    mUserDataPreparer.destroyUserDataLI(volumeUuid, userId,
                             StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                 }
             }
diff --git a/services/core/java/com/android/server/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java
new file mode 100644
index 0000000..52599fd
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserDataPreparer.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.util.Log;
+
+import java.util.Objects;
+
+import static com.android.server.pm.PackageManagerService.logCriticalInfo;
+
+/**
+ * Helper class for preparing and destroying user storage
+ */
+class UserDataPreparer {
+    private final Object mInstallLock;
+    private final Context mContext;
+    private final boolean mOnlyCore;
+    private final Installer mInstaller;
+
+    UserDataPreparer(Installer installer, Object installLock, Context context, boolean onlyCore) {
+        mInstallLock = installLock;
+        mContext = context;
+        mOnlyCore = onlyCore;
+        mInstaller = installer;
+    }
+
+    /**
+     * Prepare storage areas for given user on all mounted devices.
+     */
+    void prepareUserData(int userId, int userSerial, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, true);
+            }
+        }
+    }
+
+    private void prepareUserDataLI(String volumeUuid, int userId, int userSerial, int flags,
+            boolean allowRecover) {
+        // Prepare storage and verify that serial numbers are consistent; if
+        // there's a mismatch we need to destroy to avoid leaking data
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            storage.prepareUserStorage(volumeUuid, userId, userSerial, flags);
+
+            if ((flags & StorageManager.FLAG_STORAGE_DE) != 0 && !mOnlyCore) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserDeDirectory(volumeUuid, userId), userSerial);
+                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                    UserManagerService.enforceSerialNumber(
+                            Environment.getDataSystemDeDirectory(userId), userSerial);
+                }
+            }
+            if ((flags & StorageManager.FLAG_STORAGE_CE) != 0 && !mOnlyCore) {
+                UserManagerService.enforceSerialNumber(
+                        Environment.getDataUserCeDirectory(volumeUuid, userId), userSerial);
+                if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                    UserManagerService.enforceSerialNumber(
+                            Environment.getDataSystemCeDirectory(userId), userSerial);
+                }
+            }
+
+            mInstaller.createUserData(volumeUuid, userId, userSerial, flags);
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN, "Destroying user " + userId + " on volume " + volumeUuid
+                    + " because we failed to prepare: " + e);
+            destroyUserDataLI(volumeUuid, userId,
+                    StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+
+            if (allowRecover) {
+                // Try one last time; if we fail again we're really in trouble
+                prepareUserDataLI(volumeUuid, userId, userSerial, flags, false);
+            }
+        }
+    }
+
+    /**
+     * Destroy storage areas for given user on all mounted devices.
+     */
+    void destroyUserData(int userId, int flags) {
+        synchronized (mInstallLock) {
+            final StorageManager storage = mContext.getSystemService(StorageManager.class);
+            for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
+                final String volumeUuid = vol.getFsUuid();
+                destroyUserDataLI(volumeUuid, userId, flags);
+            }
+        }
+    }
+
+    void destroyUserDataLI(String volumeUuid, int userId, int flags) {
+        final StorageManager storage = mContext.getSystemService(StorageManager.class);
+        try {
+            // Clean up app data, profile data, and media data
+            mInstaller.destroyUserData(volumeUuid, userId, flags);
+
+            // Clean up system data
+            if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) {
+                if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getUserSystemDirectory(userId));
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemDeDirectory(userId));
+                    FileUtils.deleteContentsAndDir(Environment.getDataMiscDeDirectory(userId));
+                }
+                if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) {
+                    FileUtils.deleteContentsAndDir(Environment.getDataSystemCeDirectory(userId));
+                    FileUtils.deleteContentsAndDir(Environment.getDataMiscCeDirectory(userId));
+                }
+            }
+
+            // Data with special labels is now gone, so finish the job
+            storage.destroyUserStorage(volumeUuid, userId, flags);
+
+        } catch (Exception e) {
+            logCriticalInfo(Log.WARN,
+                    "Failed to destroy user " + userId + " on volume " + volumeUuid + ": " + e);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 9d2d9e5..455d3e4 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -227,6 +227,7 @@
     private final Context mContext;
     private final PackageManagerService mPm;
     private final Object mPackagesLock;
+    private final UserDataPreparer mUserDataPreparer;
     // Short-term lock for internal state, when interaction/sync with PM is not required
     private final Object mUsersLock = new Object();
     private final Object mRestrictionsLock = new Object();
@@ -433,7 +434,7 @@
     // TODO b/28848102 Add support for test dependencies injection
     @VisibleForTesting
     UserManagerService(Context context) {
-        this(context, null, new Object(), context.getCacheDir());
+        this(context, null, null, new Object(), context.getCacheDir());
     }
 
     /**
@@ -441,16 +442,18 @@
      * associated with the package manager, and the given lock is the
      * package manager's own lock.
      */
-    UserManagerService(Context context, PackageManagerService pm, Object packagesLock) {
-        this(context, pm, packagesLock, Environment.getDataDirectory());
+    UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer,
+            Object packagesLock) {
+        this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory());
     }
 
     private UserManagerService(Context context, PackageManagerService pm,
-            Object packagesLock, File dataDir) {
+            UserDataPreparer userDataPreparer, Object packagesLock, File dataDir) {
         mContext = context;
         mPm = pm;
         mPackagesLock = packagesLock;
         mHandler = new MainHandler();
+        mUserDataPreparer = userDataPreparer;
         synchronized (mPackagesLock) {
             mUsersDir = new File(dataDir, USER_INFO_DIR);
             mUsersDir.mkdirs();
@@ -2494,7 +2497,7 @@
             }
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
             storage.createUserKey(userId, userInfo.serialNumber, userInfo.isEphemeral());
-            mPm.prepareUserData(userId, userInfo.serialNumber,
+            mUserDataPreparer.prepareUserData(userId, userInfo.serialNumber,
                     StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
             mPm.createNewUser(userId, disallowedPackages);
             userInfo.partial = false;
@@ -2788,7 +2791,7 @@
         mPm.cleanUpUser(this, userHandle);
 
         // Clean up all data before removing metadata
-        mPm.destroyUserData(userHandle,
+        mUserDataPreparer.destroyUserData(userHandle,
                 StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
 
         // Remove this user from the list
@@ -3129,7 +3132,7 @@
         final int userSerial = userInfo.serialNumber;
         // Migrate only if build fingerprints mismatch
         boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
-        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
+        mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE, migrateAppsData);
 
         if (userId != UserHandle.USER_SYSTEM) {
@@ -3151,7 +3154,7 @@
         final int userSerial = userInfo.serialNumber;
         // Migrate only if build fingerprints mismatch
         boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
-        mPm.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
+        mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
         mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
     }