Merge "Adopted Storage: Prepare user storage before move" into main
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 059bd84..22e8251 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.content.pm.UserInfo;
 import android.os.IVold;
 
 import java.util.List;
@@ -169,4 +170,19 @@
      */
     public abstract void registerCloudProviderChangeListener(
             @NonNull CloudProviderChangeListener listener);
+
+    /**
+     * Prepares user data directories before moving storage or apps. This is required as adoptable
+     * storage unlock is tied to the prepare user data and storage needs to be unlocked before
+     * performing any operations on it. This will also create user data directories before
+     * initiating the move operations, which essential for ensuring the directories to have correct
+     * SELinux labels and permissions.
+     *
+     * @param fromVolumeUuid the source volume UUID from which content needs to be transferred
+     * @param toVolumeUuid the destination volume UUID to which contents are to be transferred
+     * @param users a list of users for whom to prepare storage
+     */
+    public abstract void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid,
+            List<UserInfo> users);
+
 }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 08d8be8..08c71c3 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1377,6 +1377,16 @@
         return mVold.supportsBlockCheckpoint();
     }
 
+    private void prepareUserStorageForMoveInternal(String fromVolumeUuid, String toVolumeUuid,
+            List<UserInfo> users) throws Exception {
+
+        final int flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
+        for (UserInfo user : users) {
+            prepareUserStorageInternal(fromVolumeUuid, user.id, user.serialNumber, flags);
+            prepareUserStorageInternal(toVolumeUuid, user.id, user.serialNumber, flags);
+        }
+    }
+
     @Override
     public void onAwakeStateChanged(boolean isAwake) {
         // Ignored
@@ -2986,6 +2996,7 @@
 
         final VolumeInfo from;
         final VolumeInfo to;
+        final List<UserInfo> users;
 
         synchronized (mLock) {
             if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) {
@@ -2999,7 +3010,7 @@
             mMoveTargetUuid = volumeUuid;
 
             // We need all the users unlocked to move their primary storage
-            final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+            users = mContext.getSystemService(UserManager.class).getUsers();
             for (UserInfo user : users) {
                 if (StorageManager.isFileEncrypted() && !isUserKeyUnlocked(user.id)) {
                     Slog.w(TAG, "Failing move due to locked user " + user.id);
@@ -3035,6 +3046,19 @@
             }
         }
 
+        // Prepare the storage before move, this is required to unlock adoptable storage (as the
+        // keys are tied to prepare user data step) & also is required for the destination files to
+        // end up with the correct SELinux labels and encryption policies for directories
+        try {
+            prepareUserStorageForMoveInternal(mPrimaryStorageUuid, volumeUuid, users);
+        } catch (Exception e) {
+            Slog.w(TAG, "Failing move due to failure on prepare user data", e);
+            synchronized (mLock) {
+                onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR);
+            }
+            return;
+        }
+
         try {
             mVold.moveStorage(from.id, to.id, new IVoldTaskListener.Stub() {
                 @Override
@@ -5024,5 +5048,16 @@
             mCloudProviderChangeListeners.add(listener);
             mHandler.obtainMessage(H_CLOUD_MEDIA_PROVIDER_CHANGED, listener);
         }
+
+        @Override
+        public void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid,
+                List<UserInfo> users) {
+            try {
+                prepareUserStorageForMoveInternal(fromVolumeUuid, toVolumeUuid, users);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
     }
 }
diff --git a/services/core/java/com/android/server/pm/MovePackageHelper.java b/services/core/java/com/android/server/pm/MovePackageHelper.java
index 1f96205..139d9cf 100644
--- a/services/core/java/com/android/server/pm/MovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/MovePackageHelper.java
@@ -48,8 +48,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
+import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
-import android.text.TextUtils;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.SparseIntArray;
@@ -63,6 +63,7 @@
 import com.android.server.pm.pkg.PackageStateUtils;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -223,9 +224,7 @@
         }
 
         try {
-            for (int index = 0; index < installedUserIds.length; index++) {
-                prepareUserDataForVolumeIfRequired(volumeUuid, installedUserIds[index], storage);
-            }
+            prepareUserStorageForMove(currentVolumeUuid, volumeUuid, installedUserIds);
         } catch (RuntimeException e) {
             freezer.close();
             throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR,
@@ -380,27 +379,20 @@
         return true;
     }
 
-    private void prepareUserDataForVolumeIfRequired(String volumeUuid, int userId,
-            StorageManager storageManager) {
-        if (TextUtils.isEmpty(volumeUuid)
-                || doesDataDirectoryExistForUser(volumeUuid, userId)) {
-            return;
-        }
+    private void prepareUserStorageForMove(String fromVolumeUuid, String toVolumeUuid,
+            int[] userIds) {
         if (DEBUG_INSTALL) {
-            Slog.d(TAG, "Preparing user directories for user u" + userId + " for UUID "
-                    + volumeUuid);
+            Slog.d(TAG, "Preparing user directories before moving app, from UUID " + fromVolumeUuid
+                    + " to UUID " + toVolumeUuid);
         }
-        final UserInfo user = mPm.mUserManager.getUserInfo(userId);
-        if (user == null) return;
-        // This call is same as StorageEventHelper#loadPrivatePackagesInner which prepares
-        // the storage before reconciling apps
-        storageManager.prepareUserStorage(volumeUuid, user.id, user.serialNumber,
-                StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
-    }
-
-    private boolean doesDataDirectoryExistForUser(String uuid, int userId) {
-        final File userDirectoryFile = Environment.getDataUserCeDirectory(uuid, userId);
-        return userDirectoryFile != null && userDirectoryFile.exists();
+        final StorageManagerInternal smInternal =
+                mPm.mInjector.getLocalService(StorageManagerInternal.class);
+        final ArrayList<UserInfo> users = new ArrayList<>();
+        for (int userId : userIds) {
+            final UserInfo user = mPm.mUserManager.getUserInfo(userId);
+            users.add(user);
+        }
+        smInternal.prepareUserStorageForMove(fromVolumeUuid, toVolumeUuid, users);
     }
 
     public static class MoveCallbacks extends Handler {