Returning to wizard, split move events.

Finish wiring up notifications to jump back into in-progress wizard
flow, using moveId as identifier.

Split move events back into separate creation and progress events,
and pass details as bundle to pass extra stuff like UUID.  Null
package still means moving primary storage.

Add explicit "volume forgotten" event for PackageManager to clean
up internal state with.

Plumb through internal path reported by vold, and bring back FUSE
bypass rewriting optimization.

Bug: 19993667
Change-Id: I0f43edbba36c58c5cd33550022c54c4eb9f01a48
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6e511f3..90293a4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,6 +62,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -1561,13 +1562,7 @@
     public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() {
         final StorageManager storage = mContext.getSystemService(StorageManager.class);
         final String volumeUuid = storage.getPrimaryStorageUuid();
-        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
-            return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
-        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
-            return storage.getPrimaryPhysicalVolume();
-        } else {
-            return storage.findVolumeByUuid(volumeUuid);
-        }
+        return storage.findVolumeByQualifiedUuid(volumeUuid);
     }
 
     @Override
@@ -2055,7 +2050,8 @@
     /** {@hide} */
     private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements
             Handler.Callback {
-        private static final int MSG_STATUS_CHANGED = 1;
+        private static final int MSG_CREATED = 1;
+        private static final int MSG_STATUS_CHANGED = 2;
 
         final MoveCallback mCallback;
         final Handler mHandler;
@@ -2068,23 +2064,36 @@
         @Override
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_STATUS_CHANGED:
+                case MSG_CREATED: {
                     final SomeArgs args = (SomeArgs) msg.obj;
-                    mCallback.onStatusChanged(args.argi1, (String) args.arg2, args.argi3,
-                            (long) args.arg4);
+                    mCallback.onCreated(args.argi1, (Bundle) args.arg2);
                     args.recycle();
                     return true;
+                }
+                case MSG_STATUS_CHANGED: {
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    mCallback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3);
+                    args.recycle();
+                    return true;
+                }
             }
             return false;
         }
 
         @Override
-        public void onStatusChanged(int moveId, String moveTitle, int status, long estMillis) {
+        public void onCreated(int moveId, Bundle extras) {
             final SomeArgs args = SomeArgs.obtain();
             args.argi1 = moveId;
-            args.arg2 = moveTitle;
-            args.argi3 = status;
-            args.arg4 = estMillis;
+            args.arg2 = extras;
+            mHandler.obtainMessage(MSG_CREATED, args).sendToTarget();
+        }
+
+        @Override
+        public void onStatusChanged(int moveId, int status, long estMillis) {
+            final SomeArgs args = SomeArgs.obtain();
+            args.argi1 = moveId;
+            args.argi2 = status;
+            args.arg3 = estMillis;
             mHandler.obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget();
         }
     }
diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl
index 155ed0b..86189fc 100644
--- a/core/java/android/content/pm/IPackageMoveObserver.aidl
+++ b/core/java/android/content/pm/IPackageMoveObserver.aidl
@@ -17,10 +17,13 @@
 
 package android.content.pm;
 
+import android.os.Bundle;
+
 /**
  * Callback for moving package resources from the Package Manager.
  * @hide
  */
 oneway interface IPackageMoveObserver {
-    void onStatusChanged(int moveId, String moveTitle, int status, long estMillis);
+    void onCreated(int moveId, in Bundle extras);
+    void onStatusChanged(int moveId, int status, long estMillis);
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a1ee7fc..7ff6ec3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4212,8 +4212,8 @@
 
     /** {@hide} */
     public static abstract class MoveCallback {
-        public abstract void onStatusChanged(int moveId, String moveTitle, int status,
-                long estMillis);
+        public void onCreated(int moveId, Bundle extras) {}
+        public abstract void onStatusChanged(int moveId, int status, long estMillis);
     }
 
     /** {@hide} */
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2eb97f1..e3e16eb 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -758,7 +758,6 @@
      * @hide
      */
     public static File maybeTranslateEmulatedPathToInternal(File path) {
-        // TODO: bring back this optimization
-        return path;
+        return StorageManager.maybeTranslateEmulatedPathToInternal(path);
     }
 }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 931cd3e..021e5e4 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -390,7 +390,7 @@
      * attacks.
      */
     public static boolean contains(File dir, File file) {
-        if (file == null) return false;
+        if (dir == null || file == null) return false;
 
         String dirPath = dir.getAbsolutePath();
         String filePath = file.getAbsolutePath();
diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java
index 2d13e49..c958fb0 100644
--- a/core/java/android/os/storage/IMountServiceListener.java
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -91,10 +91,17 @@
                     reply.writeNoException();
                     return true;
                 }
-                case TRANSACTION_onVolumeMetadataChanged: {
+                case TRANSACTION_onVolumeRecordChanged: {
+                    data.enforceInterface(DESCRIPTOR);
+                    final VolumeRecord rec = (VolumeRecord) data.readParcelable(null);
+                    onVolumeRecordChanged(rec);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_onVolumeForgotten: {
                     data.enforceInterface(DESCRIPTOR);
                     final String fsUuid = data.readString();
-                    onVolumeMetadataChanged(fsUuid);
+                    onVolumeForgotten(fsUuid);
                     reply.writeNoException();
                     return true;
                 }
@@ -192,13 +199,29 @@
             }
 
             @Override
-            public void onVolumeMetadataChanged(String fsUuid) throws RemoteException {
+            public void onVolumeRecordChanged(VolumeRecord rec) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeParcelable(rec, 0);
+                    mRemote.transact(Stub.TRANSACTION_onVolumeRecordChanged, _data, _reply,
+                            android.os.IBinder.FLAG_ONEWAY);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
+            public void onVolumeForgotten(String fsUuid) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
                     _data.writeString(fsUuid);
-                    mRemote.transact(Stub.TRANSACTION_onVolumeMetadataChanged, _data, _reply,
+                    mRemote.transact(Stub.TRANSACTION_onVolumeForgotten, _data, _reply,
                             android.os.IBinder.FLAG_ONEWAY);
                     _reply.readException();
                 } finally {
@@ -228,8 +251,9 @@
         static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0);
         static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1);
         static final int TRANSACTION_onVolumeStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 2);
-        static final int TRANSACTION_onVolumeMetadataChanged = (IBinder.FIRST_CALL_TRANSACTION + 3);
-        static final int TRANSACTION_onDiskScanned = (IBinder.FIRST_CALL_TRANSACTION + 4);
+        static final int TRANSACTION_onVolumeRecordChanged = (IBinder.FIRST_CALL_TRANSACTION + 3);
+        static final int TRANSACTION_onVolumeForgotten = (IBinder.FIRST_CALL_TRANSACTION + 4);
+        static final int TRANSACTION_onDiskScanned = (IBinder.FIRST_CALL_TRANSACTION + 5);
     }
 
     /**
@@ -252,8 +276,8 @@
 
     public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)
             throws RemoteException;
-
-    public void onVolumeMetadataChanged(String fsUuid) throws RemoteException;
+    public void onVolumeRecordChanged(VolumeRecord rec) throws RemoteException;
+    public void onVolumeForgotten(String fsUuid) throws RemoteException;
 
     public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
index 536aca9..214c60d 100644
--- a/core/java/android/os/storage/StorageEventListener.java
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -41,7 +41,10 @@
     public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
     }
 
-    public void onVolumeMetadataChanged(String fsUuid) {
+    public void onVolumeRecordChanged(VolumeRecord rec) {
+    }
+
+    public void onVolumeForgotten(String fsUuid) {
     }
 
     public void onDiskScanned(DiskInfo disk, int volumeCount) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 29da4f1..50e7d1c4 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -93,8 +93,9 @@
             Handler.Callback {
         private static final int MSG_STORAGE_STATE_CHANGED = 1;
         private static final int MSG_VOLUME_STATE_CHANGED = 2;
-        private static final int MSG_VOLUME_METADATA_CHANGED = 3;
-        private static final int MSG_DISK_SCANNED = 4;
+        private static final int MSG_VOLUME_RECORD_CHANGED = 3;
+        private static final int MSG_VOLUME_FORGOTTEN = 4;
+        private static final int MSG_DISK_SCANNED = 5;
 
         final StorageEventListener mCallback;
         final Handler mHandler;
@@ -117,8 +118,12 @@
                     mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
                     args.recycle();
                     return true;
-                case MSG_VOLUME_METADATA_CHANGED:
-                    mCallback.onVolumeMetadataChanged((String) args.arg1);
+                case MSG_VOLUME_RECORD_CHANGED:
+                    mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
+                    args.recycle();
+                    return true;
+                case MSG_VOLUME_FORGOTTEN:
+                    mCallback.onVolumeForgotten((String) args.arg1);
                     args.recycle();
                     return true;
                 case MSG_DISK_SCANNED:
@@ -154,10 +159,17 @@
         }
 
         @Override
-        public void onVolumeMetadataChanged(String fsUuid) {
+        public void onVolumeRecordChanged(VolumeRecord rec) {
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = rec;
+            mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
+        }
+
+        @Override
+        public void onVolumeForgotten(String fsUuid) {
             final SomeArgs args = SomeArgs.obtain();
             args.arg1 = fsUuid;
-            mHandler.obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
+            mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
         }
 
         @Override
@@ -246,8 +258,9 @@
     }
 
     /** {@hide} */
+    @Deprecated
     public static StorageManager from(Context context) {
-        return (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+        return context.getSystemService(StorageManager.class);
     }
 
     /**
@@ -536,6 +549,17 @@
     }
 
     /** {@hide} */
+    public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
+        if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
+            return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
+        } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
+            return getPrimaryPhysicalVolume();
+        } else {
+            return findVolumeByUuid(volumeUuid);
+        }
+    }
+
+    /** {@hide} */
     public @NonNull List<VolumeInfo> getVolumes() {
         try {
             return Arrays.asList(mMountService.getVolumes(0));
@@ -555,6 +579,8 @@
 
     /** {@hide} */
     public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
+        if (vol == null) return null;
+
         // Nickname always takes precedence when defined
         if (!TextUtils.isEmpty(vol.fsUuid)) {
             final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
@@ -861,6 +887,27 @@
                 DEFAULT_FULL_THRESHOLD_BYTES);
     }
 
+    /** {@hide} */
+    public static File maybeTranslateEmulatedPathToInternal(File path) {
+        final IMountService mountService = IMountService.Stub.asInterface(
+                ServiceManager.getService("mount"));
+        try {
+            final VolumeInfo[] vols = mountService.getVolumes(0);
+            for (VolumeInfo vol : vols) {
+                if ((vol.getType() == VolumeInfo.TYPE_EMULATED
+                        || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
+                    final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
+                            vol.getInternalPath(), path);
+                    if (internalPath != null) {
+                        return internalPath;
+                    }
+                }
+            }
+        } catch (RemoteException ignored) {
+        }
+        return path;
+    }
+
     /// Consts to match the password types in cryptfs.h
     /** @hide */
     public static final int CRYPT_TYPE_PASSWORD = 0;
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index fd10cae..2622ee0 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -129,6 +129,7 @@
     public String fsUuid;
     public String fsLabel;
     public String path;
+    public String internalPath;
 
     /** Framework state */
     public final int mtpIndex;
@@ -155,6 +156,7 @@
         fsUuid = parcel.readString();
         fsLabel = parcel.readString();
         path = parcel.readString();
+        internalPath = parcel.readString();
         mtpIndex = parcel.readInt();
     }
 
@@ -248,7 +250,11 @@
     }
 
     public File getPath() {
-        return new File(path);
+        return (path != null) ? new File(path) : null;
+    }
+
+    public File getInternalPath() {
+        return (internalPath != null) ? new File(internalPath) : null;
     }
 
     public File getPathForUser(int userId) {
@@ -335,14 +341,11 @@
         final Uri uri;
         if (type == VolumeInfo.TYPE_PUBLIC) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
-        } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(id)) {
+        } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
                     DOCUMENT_ROOT_PRIMARY_EMULATED);
-        } else if (type == VolumeInfo.TYPE_EMULATED) {
-            // TODO: build intent once supported
-            uri = null;
         } else {
-            throw new IllegalArgumentException();
+            return null;
         }
 
         final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
@@ -372,6 +375,7 @@
         pw.printPair("fsLabel", fsLabel);
         pw.println();
         pw.printPair("path", path);
+        pw.printPair("internalPath", internalPath);
         pw.printPair("mtpIndex", mtpIndex);
         pw.decreaseIndent();
         pw.println();
@@ -437,6 +441,7 @@
         parcel.writeString(fsUuid);
         parcel.writeString(fsLabel);
         parcel.writeString(path);
+        parcel.writeString(internalPath);
         parcel.writeInt(mtpIndex);
     }
 }