Command to change force adoptable state.

Since user builds can't setprop, add an explicit "sm" verb to change
the force adoptable state.

Bug: 21191915
Change-Id: I719d9b18c1a98c97442a5ddb1cc5512e8e4d3d3f
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 4a8cf08..0dad4dc 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -71,6 +71,8 @@
             runHasAdoptable();
         } else if ("get-primary-storage-uuid".equals(op)) {
             runGetPrimaryStorageUuid();
+        } else if ("set-force-adoptable".equals(op)) {
+            runSetForceAdoptable();
         } else if ("partition".equals(op)) {
             runPartition();
         } else if ("mount".equals(op)) {
@@ -116,14 +118,19 @@
     }
 
     public void runHasAdoptable() {
-        System.out.println(SystemProperties.getBoolean(StorageManager.PROP_HAS_ADOPTABLE, false)
-                || SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false));
+        System.out.println(SystemProperties.getBoolean(StorageManager.PROP_HAS_ADOPTABLE, false));
     }
 
     public void runGetPrimaryStorageUuid() throws RemoteException {
         System.out.println(mSm.getPrimaryStorageUuid());
     }
 
+    public void runSetForceAdoptable() throws RemoteException {
+        final boolean forceAdoptable = Boolean.parseBoolean(nextArg());
+        mSm.setDebugFlags(forceAdoptable ? StorageManager.DEBUG_FORCE_ADOPTABLE : 0,
+                StorageManager.DEBUG_FORCE_ADOPTABLE);
+    }
+
     public void runPartition() throws RemoteException {
         final String diskId = nextArg();
         final String type = nextArg();
@@ -177,6 +184,7 @@
         System.err.println("       sm list-volumes [public|private|emulated|all]");
         System.err.println("       sm has-adoptable");
         System.err.println("       sm get-primary-storage-uuid");
+        System.err.println("       sm set-force-adoptable [true|false]");
         System.err.println("");
         System.err.println("       sm partition DISK [public|private|mixed] [ratio]");
         System.err.println("       sm mount VOLUME");
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 9c576d4..e55ae99 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1129,6 +1129,22 @@
             }
 
             @Override
+            public void setDebugFlags(int _flags, int _mask) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(_flags);
+                    _data.writeInt(_mask);
+                    mRemote.transact(Stub.TRANSACTION_setDebugFlags, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
             public String getPrimaryStorageUuid() throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -1274,6 +1290,7 @@
         static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 58;
 
         static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
+        static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
 
         /**
          * Cast an IBinder object into an IMountService interface, generating a
@@ -1804,6 +1821,14 @@
                     reply.writeNoException();
                     return true;
                 }
+                case TRANSACTION_setDebugFlags: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int _flags = data.readInt();
+                    int _mask = data.readInt();
+                    setDebugFlags(_flags, _mask);
+                    reply.writeNoException();
+                    return true;
+                }
                 case TRANSACTION_getPrimaryStorageUuid: {
                     data.enforceInterface(DESCRIPTOR);
                     String volumeUuid = getPrimaryStorageUuid();
@@ -2124,6 +2149,7 @@
     public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException;
     public void forgetVolume(String fsUuid) throws RemoteException;
     public void forgetAllVolumes() throws RemoteException;
+    public void setDebugFlags(int flags, int mask) throws RemoteException;
 
     public String getPrimaryStorageUuid() throws RemoteException;
     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 3a6ff28..8ff56f8 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -82,6 +82,9 @@
     /** {@hide} */
     public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
 
+    /** {@hide} */
+    public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
+
     private final Context mContext;
     private final ContentResolver mResolver;
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index dda9358..fd0ba73 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -95,6 +95,8 @@
     <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/>
     <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
     <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
+    <uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />
 
     <application android:label="@string/app_label">
         <provider
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 7cd5bff..d430b50 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -16,8 +16,10 @@
 
 package com.android.server;
 
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -37,9 +39,9 @@
 import android.mtp.MtpStorage;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.DropBoxManager;
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
-import android.os.DropBoxManager;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -250,6 +252,7 @@
     private static final String TAG_VOLUMES = "volumes";
     private static final String ATTR_VERSION = "version";
     private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
+    private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable";
     private static final String TAG_VOLUME = "volume";
     private static final String ATTR_TYPE = "type";
     private static final String ATTR_FS_UUID = "fsUuid";
@@ -279,6 +282,8 @@
     private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>();
     @GuardedBy("mLock")
     private String mPrimaryStorageUuid;
+    @GuardedBy("mLock")
+    private boolean mForceAdoptable;
 
     /** Map from disk ID to latches */
     @GuardedBy("mLock")
@@ -813,7 +818,8 @@
                 if (cooked.length != 3) break;
                 final String id = cooked[1];
                 int flags = Integer.parseInt(cooked[2]);
-                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)) {
+                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
+                        || mForceAdoptable) {
                     flags |= DiskInfo.FLAG_ADOPTABLE;
                 }
                 mDisks.put(id, new DiskInfo(id, flags));
@@ -1208,6 +1214,7 @@
     private void readSettingsLocked() {
         mRecords.clear();
         mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
+        mForceAdoptable = false;
 
         FileInputStream fis = null;
         try {
@@ -1229,6 +1236,7 @@
                             mPrimaryStorageUuid = readStringAttribute(in,
                                     ATTR_PRIMARY_STORAGE_UUID);
                         }
+                        mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false);
 
                     } else if (TAG_VOLUME.equals(tag)) {
                         final VolumeRecord rec = readVolumeRecord(in);
@@ -1258,6 +1266,7 @@
             out.startTag(null, TAG_VOLUMES);
             writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
             writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
+            writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable);
             final int size = mRecords.size();
             for (int i = 0; i < size; i++) {
                 final VolumeRecord rec = mRecords.valueAt(i);
@@ -1542,6 +1551,21 @@
     }
 
     @Override
+    public void setDebugFlags(int flags, int mask) {
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        waitForReady();
+
+        synchronized (mLock) {
+            if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) {
+                mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0;
+            }
+
+            writeSettingsLocked();
+            resetIfReadyAndConnected();
+        }
+    }
+
+    @Override
     public String getPrimaryStorageUuid() {
         enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
@@ -3036,6 +3060,7 @@
 
             pw.println();
             pw.println("Primary storage UUID: " + mPrimaryStorageUuid);
+            pw.println("Force adoptable: " + mForceAdoptable);
         }
 
         synchronized (mObbMounts) {