Progress towards FBE and adoptable storage.

Offer to adopt storage devices on FBE devices, but keep it guarded
behind a system property for now, since we still need to work out key
storage details.

Verify that all users are unlocked before moving apps or shared
storage.  We only need them to be unlocked; we don't need them to
be actually running.

Have PackageManager dump the set of volumes that it's finished
scanning and loading, since otherwise CTS can get excited and race
too far ahead of it.  Add a specific error code to communicate
that users are locked.

Test: cts-tradefed run commandAndExit cts-dev --abi armeabi-v7a -m CtsAppSecurityHostTestCases -t android.appsecurity.cts.AdoptableHostTest
Bug: 37436961, 29923055, 25861755, 30230655
Change-Id: I749dc3d8148e1a95d8bc4be56665253ef826d3fe
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a040520f..232d42e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -63,6 +63,7 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.os.storage.IStorageManager;
 import android.os.storage.StorageManager;
 import android.system.ErrnoException;
@@ -370,13 +371,6 @@
         return getSharedPreferences(file, mode);
     }
 
-    private boolean isBuggy() {
-        // STOPSHIP: fix buggy apps
-        if (SystemProperties.getBoolean("fw.ignore_buggy", false)) return false;
-        if ("com.google.android.tts".equals(getApplicationInfo().packageName)) return true;
-        return false;
-    }
-
     @Override
     public SharedPreferences getSharedPreferences(File file, int mode) {
         SharedPreferencesImpl sp;
@@ -387,9 +381,8 @@
                 checkMode(mode);
                 if (getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.O) {
                     if (isCredentialProtectedStorage()
-                            && !getSystemService(StorageManager.class).isUserKeyUnlocked(
-                            UserHandle.myUserId())
-                            && !isBuggy()) {
+                            && !getSystemService(UserManager.class)
+                                    .isUserUnlockingOrUnlocked(UserHandle.myUserId())) {
                         throw new IllegalStateException("SharedPreferences in credential encrypted "
                                 + "storage are not available until after user is unlocked");
                     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6bc7d42..712d37f 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1490,6 +1490,9 @@
      */
     public static final int MOVE_FAILED_3RD_PARTY_NOT_ALLOWED_ON_INTERNAL = -9;
 
+    /** @hide */
+    public static final int MOVE_FAILED_LOCKED_USER = -10;
+
     /**
      * Flag parameter for {@link #movePackage} to indicate that
      * the package should be moved to internal storage if its
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f2aa113..71f5ff7 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -118,6 +118,8 @@
     public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
     /** {@hide} */
     public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk";
+    /** {@hide} */
+    public static final String PROP_ADOPTABLE_FBE = "persist.sys.adoptable_fbe";
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index f718e80..aa2ce1c 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -108,6 +108,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.NativeDaemonConnector.Command;
 import com.android.server.NativeDaemonConnector.SensitiveArg;
+import com.android.server.pm.PackageManagerException;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.storage.AppFuseBridge;
 
@@ -1078,7 +1079,8 @@
                     flags |= DiskInfo.FLAG_ADOPTABLE;
                 }
                 // Adoptable storage isn't currently supported on FBE devices
-                if (StorageManager.isFileEncryptedNativeOnly()) {
+                if (StorageManager.isFileEncryptedNativeOnly()
+                        && !SystemProperties.getBoolean(StorageManager.PROP_ADOPTABLE_FBE, false)) {
                     flags &= ~DiskInfo.FLAG_ADOPTABLE;
                 }
                 mDisks.put(id, new DiskInfo(id, flags));
@@ -2045,7 +2047,8 @@
         }
 
         if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
-            if (StorageManager.isFileEncryptedNativeOnly()) {
+            if (StorageManager.isFileEncryptedNativeOnly()
+                    && !SystemProperties.getBoolean(StorageManager.PROP_ADOPTABLE_FBE, false)) {
                 throw new IllegalStateException(
                         "Adoptable storage not available on device with native FBE");
             }
@@ -2121,6 +2124,17 @@
             mMoveCallback = callback;
             mMoveTargetUuid = volumeUuid;
 
+            // We need all the users unlocked to move their primary storage
+            final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+            for (UserInfo user : users) {
+                if (StorageManager.isFileEncryptedNativeOrEmulated()
+                        && !isUserKeyUnlocked(user.id)) {
+                    Slog.w(TAG, "Failing move due to locked user " + user.id);
+                    onMoveStatusLocked(PackageManager.MOVE_FAILED_LOCKED_USER);
+                    return;
+                }
+            }
+
             // When moving to/from primary physical volume, we probably just nuked
             // the current storage location, so we have nothing to move.
             if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid)
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2ac7e50..eb532ff 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -58,7 +58,6 @@
 import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
 import static android.content.pm.PackageManager.INSTALL_INTERNAL;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
 import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
@@ -78,6 +77,7 @@
 import static android.content.pm.PackageManager.MOVE_FAILED_DEVICE_ADMIN;
 import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST;
 import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR;
+import static android.content.pm.PackageManager.MOVE_FAILED_LOCKED_USER;
 import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
@@ -87,6 +87,7 @@
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDWR;
+
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
@@ -102,6 +103,7 @@
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
 import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+
 import static dalvik.system.DexFile.getNonProfileGuidedCompilerFilter;
 
 import android.Manifest;
@@ -130,6 +132,7 @@
 import android.content.pm.AppsQueryHelper;
 import android.content.pm.AuxiliaryResolveInfo;
 import android.content.pm.ChangedPackages;
+import android.content.pm.ComponentInfo;
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
@@ -724,6 +727,9 @@
 
     final ProtectedPackages mProtectedPackages;
 
+    @GuardedBy("mLoadedVolumes")
+    final ArraySet<String> mLoadedVolumes = new ArraySet<>();
+
     boolean mFirstBoot;
 
     PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
@@ -21675,6 +21681,7 @@
         public static final int DUMP_DEXOPT = 1 << 20;
         public static final int DUMP_COMPILER_STATS = 1 << 21;
         public static final int DUMP_CHANGES = 1 << 22;
+        public static final int DUMP_VOLUMES = 1 << 23;
 
         public static final int OPTION_SHOW_FILTERS = 1 << 0;
 
@@ -21914,6 +21921,8 @@
                 dumpState.setDump(DumpState.DUMP_INSTALLS);
             } else if ("frozen".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_FROZEN);
+            } else if ("volumes".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_VOLUMES);
             } else if ("dexopt".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_DEXOPT);
             } else if ("compiler-stats".equals(cmd)) {
@@ -22298,6 +22307,23 @@
                 ipw.decreaseIndent();
             }
 
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
+                if (dumpState.onTitlePrinted()) pw.println();
+
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+                ipw.println();
+                ipw.println("Loaded volumes:");
+                ipw.increaseIndent();
+                if (mLoadedVolumes.size() == 0) {
+                    ipw.println("(none)");
+                } else {
+                    for (int i = 0; i < mLoadedVolumes.size(); i++) {
+                        ipw.println(mLoadedVolumes.valueAt(i));
+                    }
+                }
+                ipw.decreaseIndent();
+            }
+
             if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
                 if (dumpState.onTitlePrinted()) pw.println();
                 dumpDexoptStateLPr(pw, packageName);
@@ -23014,6 +23040,7 @@
 
         if (DEBUG_INSTALL) Slog.d(TAG, "Loaded packages " + loaded);
         sendResourcesChangedBroadcast(true, false, loaded, null);
+        mLoadedVolumes.add(vol.getId());
     }
 
     private void unloadPrivatePackages(final VolumeInfo vol) {
@@ -23065,6 +23092,7 @@
 
         if (DEBUG_INSTALL) Slog.d(TAG, "Unloaded packages " + unloaded);
         sendResourcesChangedBroadcast(false, false, unloaded, null);
+        mLoadedVolumes.remove(vol.getId());
 
         // Try very hard to release any references to this path so we don't risk
         // the system server being killed due to open FDs
@@ -23608,8 +23636,7 @@
                     movePackageInternal(packageName, volumeUuid, moveId, callingUid, user);
                 } catch (PackageManagerException e) {
                     Slog.w(TAG, "Failed to move " + packageName, e);
-                    mMoveCallbacks.notifyStatusChanged(moveId,
-                            PackageManager.MOVE_FAILED_INTERNAL_ERROR);
+                    mMoveCallbacks.notifyStatusChanged(moveId, e.error);
                 }
             }
         });
@@ -23732,6 +23759,17 @@
             measurePath = Environment.getDataAppDirectory(volumeUuid);
         }
 
+        // If we're moving app data around, we need all the users unlocked
+        if (moveCompleteApp) {
+            for (int userId : installedUserIds) {
+                if (StorageManager.isFileEncryptedNativeOrEmulated()
+                        && !StorageManager.isUserKeyUnlocked(userId)) {
+                    throw new PackageManagerException(MOVE_FAILED_LOCKED_USER,
+                            "User " + userId + " must be unlocked");
+                }
+            }
+        }
+
         final PackageStats stats = new PackageStats(null, -1);
         synchronized (mInstaller) {
             for (int userId : installedUserIds) {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 35e4e58c..c07bd8e 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -345,7 +345,7 @@
                             + "of user " + userInfo.id + "can unlock user profile.");
                 }
 
-                if (!StorageManager.isUserKeyUnlocked(userInfo.id)
+                if (!mUserManager.isUserUnlockingOrUnlocked(userInfo.id)
                         && !directUnlock) {
                     if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
                             + "'s trust agent " + name + ": FBE still locked and "