From 50a05454795c93ac483f5cb6819e74cb17be1b5b Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 29 Apr 2015 11:24:52 -0700 Subject: 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 --- .../android/app/ApplicationPackageManager.java | 39 +++-- .../android/content/pm/IPackageMoveObserver.aidl | 5 +- core/java/android/content/pm/PackageManager.java | 4 +- core/java/android/os/Environment.java | 3 +- core/java/android/os/FileUtils.java | 2 +- .../android/os/storage/IMountServiceListener.java | 40 ++++-- .../android/os/storage/StorageEventListener.java | 5 +- core/java/android/os/storage/StorageManager.java | 61 +++++++- core/java/android/os/storage/VolumeInfo.java | 17 ++- .../android/systemui/usb/StorageNotification.java | 157 ++++++++++++++++----- .../core/java/com/android/server/MountService.java | 78 ++++++++-- .../android/server/pm/PackageManagerService.java | 68 ++++++--- 12 files changed, 370 insertions(+), 109 deletions(-) diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 6e511f38f7e7..90293a48949f 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -62,6 +62,7 @@ import android.graphics.Rect; 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 @@ final class ApplicationPackageManager extends PackageManager { 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 @@ final class ApplicationPackageManager extends PackageManager { /** {@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 @@ final class ApplicationPackageManager extends PackageManager { @Override public boolean handleMessage(Message msg) { switch (msg.what) { - case MSG_STATUS_CHANGED: + case MSG_CREATED: { + final SomeArgs args = (SomeArgs) msg.obj; + 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, (String) args.arg2, args.argi3, - (long) args.arg4); + 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 = 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.arg2 = moveTitle; - args.argi3 = status; - args.arg4 = estMillis; + 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 155ed0bf57d8..86189fce1fd6 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 a1ee7fcf83a9..7ff6ec38699f 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4212,8 +4212,8 @@ public abstract class PackageManager { /** {@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 2eb97f14fc81..e3e16eb1747b 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -758,7 +758,6 @@ public class Environment { * @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 931cd3e6a1d5..021e5e4cdd6d 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -390,7 +390,7 @@ public class FileUtils { * 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 2d13e497d010..c958fb0bcd0d 100644 --- a/core/java/android/os/storage/IMountServiceListener.java +++ b/core/java/android/os/storage/IMountServiceListener.java @@ -91,10 +91,17 @@ public interface IMountServiceListener extends IInterface { 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 @@ public interface IMountServiceListener extends IInterface { } @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 @@ public interface IMountServiceListener extends IInterface { 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 interface IMountServiceListener extends IInterface { 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 536aca9b12c9..214c60d3bfb8 100644 --- a/core/java/android/os/storage/StorageEventListener.java +++ b/core/java/android/os/storage/StorageEventListener.java @@ -41,7 +41,10 @@ public class StorageEventListener { 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 29da4f1de64a..50e7d1c44718 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -93,8 +93,9 @@ public class StorageManager { 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 @@ public class StorageManager { 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 @@ public class StorageManager { } @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 @@ public class StorageManager { } /** {@hide} */ + @Deprecated public static StorageManager from(Context context) { - return (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + return context.getSystemService(StorageManager.class); } /** @@ -535,6 +548,17 @@ public class StorageManager { return findVolumeById(privateVol.getId().replace("private", "emulated")); } + /** {@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 getVolumes() { try { @@ -555,6 +579,8 @@ public class StorageManager { /** {@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 @@ public class StorageManager { 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 fd10cae780e3..2622ee04eaab 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -129,6 +129,7 @@ public class VolumeInfo implements Parcelable { public String fsUuid; public String fsLabel; public String path; + public String internalPath; /** Framework state */ public final int mtpIndex; @@ -155,6 +156,7 @@ public class VolumeInfo implements Parcelable { fsUuid = parcel.readString(); fsLabel = parcel.readString(); path = parcel.readString(); + internalPath = parcel.readString(); mtpIndex = parcel.readInt(); } @@ -248,7 +250,11 @@ public class VolumeInfo implements Parcelable { } 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 @@ public class VolumeInfo implements Parcelable { 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 @@ public class VolumeInfo implements Parcelable { 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 @@ public class VolumeInfo implements Parcelable { parcel.writeString(fsUuid); parcel.writeString(fsLabel); parcel.writeString(path); + parcel.writeString(internalPath); parcel.writeInt(mtpIndex); } } diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index deed8957173d..e6c95b5dd1ea 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.pm.PackageManager.MoveCallback; +import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; import android.os.storage.DiskInfo; @@ -36,6 +37,7 @@ import android.os.storage.VolumeRecord; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.Log; +import android.util.SparseArray; import com.android.internal.R; import com.android.systemui.SystemUI; @@ -57,6 +59,16 @@ public class StorageNotification extends SystemUI { private NotificationManager mNotificationManager; private StorageManager mStorageManager; + private static class MoveInfo { + public int moveId; + public Bundle extras; + public String packageName; + public String label; + public String volumeUuid; + } + + private final SparseArray mMoves = new SparseArray<>(); + private final StorageEventListener mListener = new StorageEventListener() { @Override public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { @@ -64,20 +76,20 @@ public class StorageNotification extends SystemUI { } @Override - public void onVolumeMetadataChanged(String fsUuid) { + public void onVolumeRecordChanged(VolumeRecord rec) { // Avoid kicking notifications when getting early metadata before // mounted. If already mounted, we're being kicked because of a // nickname or init'ed change. - final VolumeInfo vol = mStorageManager.findVolumeByUuid(fsUuid); + final VolumeInfo vol = mStorageManager.findVolumeByUuid(rec.getFsUuid()); if (vol != null && vol.isMountedReadable()) { onVolumeStateChangedInternal(vol); } + } - final VolumeRecord rec = mStorageManager.findRecordByUuid(fsUuid); - if (rec == null) { - // Private volume was probably just forgotten - mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL); - } + @Override + public void onVolumeForgotten(String fsUuid) { + // Stop annoying the user + mNotificationManager.cancelAsUser(fsUuid, PRIVATE_ID, UserHandle.ALL); } @Override @@ -97,11 +109,30 @@ public class StorageNotification extends SystemUI { private final MoveCallback mMoveCallback = new MoveCallback() { @Override - public void onStatusChanged(int moveId, String moveTitle, int status, long estMillis) { + public void onCreated(int moveId, Bundle extras) { + final MoveInfo move = new MoveInfo(); + move.moveId = moveId; + move.extras = extras; + if (extras != null) { + move.packageName = extras.getString(Intent.EXTRA_PACKAGE_NAME); + move.label = extras.getString(Intent.EXTRA_TITLE); + move.volumeUuid = extras.getString(VolumeRecord.EXTRA_FS_UUID); + } + mMoves.put(moveId, move); + } + + @Override + public void onStatusChanged(int moveId, int status, long estMillis) { + final MoveInfo move = mMoves.get(moveId); + if (move == null) { + Log.w(TAG, "Ignoring unknown move " + moveId); + return; + } + if (PackageManager.isMoveStatusFinished(status)) { - onMoveFinished(moveId, moveTitle, status); + onMoveFinished(move, status); } else { - onMoveProgress(moveId, moveTitle, status, estMillis); + onMoveProgress(move, status, estMillis); } } }; @@ -149,10 +180,10 @@ public class StorageNotification extends SystemUI { .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setContentTitle(title) .setContentText(text) + .setContentIntent(buildForgetPendingIntent(rec)) .setStyle(new Notification.BigTextStyle().bigText(text)) .setVisibility(Notification.VISIBILITY_PUBLIC) .setLocalOnly(true) - .setContentIntent(buildForgetPendingIntent(rec)) .setCategory(Notification.CATEGORY_SYSTEM) .setOngoing(true) .build(); @@ -175,10 +206,10 @@ public class StorageNotification extends SystemUI { .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setContentTitle(title) .setContentText(text) + .setContentIntent(buildInitPendingIntent(disk)) .setStyle(new Notification.BigTextStyle().bigText(text)) .setVisibility(Notification.VISIBILITY_PUBLIC) .setLocalOnly(true) - .setContentIntent(buildInitPendingIntent(disk)) .setCategory(Notification.CATEGORY_ERROR) .build(); @@ -280,13 +311,13 @@ public class StorageNotification extends SystemUI { final CharSequence text = mContext.getString( R.string.ext_media_new_notification_message, disk.getDescription()); - final PendingIntent initAction = buildInitPendingIntent(vol); + final PendingIntent initIntent = buildInitPendingIntent(vol); return buildNotificationBuilder(vol, title, text) .addAction(new Action(0, mContext.getString(R.string.ext_media_init_action), - initAction)) + initIntent)) .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), buildUnmountPendingIntent(vol))) - .setContentIntent(initAction) + .setContentIntent(initIntent) .setDeleteIntent(buildSnoozeIntent(vol)) .setCategory(Notification.CATEGORY_SYSTEM) .build(); @@ -296,13 +327,13 @@ public class StorageNotification extends SystemUI { final CharSequence text = mContext.getString( R.string.ext_media_ready_notification_message, disk.getDescription()); - final PendingIntent browseAction = buildBrowsePendingIntent(vol); + final PendingIntent browseIntent = buildBrowsePendingIntent(vol); return buildNotificationBuilder(vol, title, text) .addAction(new Action(0, mContext.getString(R.string.ext_media_browse_action), - browseAction)) + browseIntent)) .addAction(new Action(0, mContext.getString(R.string.ext_media_unmount_action), buildUnmountPendingIntent(vol))) - .setContentIntent(browseAction) + .setContentIntent(browseIntent) .setDeleteIntent(buildSnoozeIntent(vol)) .setCategory(Notification.CATEGORY_SYSTEM) .setPriority(Notification.PRIORITY_LOW) @@ -337,7 +368,7 @@ public class StorageNotification extends SystemUI { R.string.ext_media_unmountable_notification_message, disk.getDescription()); return buildNotificationBuilder(vol, title, text) - .setContentIntent(buildDetailsPendingIntent(vol)) + .setContentIntent(buildVolumeSettingsPendingIntent(vol)) .setCategory(Notification.CATEGORY_ERROR) .build(); } @@ -376,10 +407,10 @@ public class StorageNotification extends SystemUI { .build(); } - private void onMoveProgress(int moveId, String moveTitle, int status, long estMillis) { + private void onMoveProgress(MoveInfo move, int status, long estMillis) { final CharSequence title; - if (!TextUtils.isEmpty(moveTitle)) { - title = mContext.getString(R.string.ext_media_move_specific_title, moveTitle); + if (!TextUtils.isEmpty(move.label)) { + title = mContext.getString(R.string.ext_media_move_specific_title, move.label); } else { title = mContext.getString(R.string.ext_media_move_title); } @@ -391,11 +422,19 @@ public class StorageNotification extends SystemUI { text = DateUtils.formatDuration(estMillis); } + final PendingIntent intent; + if (move.packageName != null) { + intent = buildWizardMovePendingIntent(move); + } else { + intent = buildWizardMigratePendingIntent(move); + } + final Notification notif = new Notification.Builder(mContext) .setSmallIcon(R.drawable.stat_notify_sdcard) .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setContentTitle(title) .setContentText(text) + .setContentIntent(intent) .setStyle(new Notification.BigTextStyle().bigText(text)) .setVisibility(Notification.VISIBILITY_PUBLIC) .setLocalOnly(true) @@ -405,19 +444,19 @@ public class StorageNotification extends SystemUI { .setOngoing(true) .build(); - mNotificationManager.notifyAsUser(moveTitle, MOVE_ID, notif, UserHandle.ALL); + mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, notif, UserHandle.ALL); } - private void onMoveFinished(int moveId, String moveTitle, int status) { - if (!TextUtils.isEmpty(moveTitle)) { + private void onMoveFinished(MoveInfo move, int status) { + if (move.packageName != null) { // We currently ignore finished app moves; just clear the last // published progress - mNotificationManager.cancelAsUser(moveTitle, MOVE_ID, UserHandle.ALL); + mNotificationManager.cancelAsUser(move.packageName, MOVE_ID, UserHandle.ALL); return; } - final VolumeInfo vol = mContext.getPackageManager().getPrimaryStorageCurrentVolume(); - final String descrip = mStorageManager.getBestVolumeDescription(vol); + final VolumeInfo privateVol = mContext.getPackageManager().getPrimaryStorageCurrentVolume(); + final String descrip = mStorageManager.getBestVolumeDescription(privateVol); final CharSequence title; final CharSequence text; @@ -429,19 +468,29 @@ public class StorageNotification extends SystemUI { text = mContext.getString(R.string.ext_media_move_failure_message); } + // Jump back into the wizard flow if we moved to a real disk + final PendingIntent intent; + if (privateVol != null && privateVol.getDisk() != null) { + intent = buildWizardReadyPendingIntent(privateVol.getDisk()); + } else { + intent = buildVolumeSettingsPendingIntent(privateVol); + } + final Notification notif = new Notification.Builder(mContext) .setSmallIcon(R.drawable.stat_notify_sdcard) .setColor(mContext.getColor(R.color.system_notification_accent_color)) .setContentTitle(title) .setContentText(text) + .setContentIntent(intent) .setStyle(new Notification.BigTextStyle().bigText(text)) .setVisibility(Notification.VISIBILITY_PUBLIC) .setLocalOnly(true) .setCategory(Notification.CATEGORY_SYSTEM) .setPriority(Notification.PRIORITY_LOW) + .setAutoCancel(true) .build(); - mNotificationManager.notifyAsUser(moveTitle, MOVE_ID, notif, UserHandle.ALL); + mNotificationManager.notifyAsUser(move.packageName, MOVE_ID, notif, UserHandle.ALL); } private int getSmallIcon(DiskInfo disk, int state) { @@ -513,10 +562,20 @@ public class StorageNotification extends SystemUI { PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); } - private PendingIntent buildDetailsPendingIntent(VolumeInfo vol) { + private PendingIntent buildVolumeSettingsPendingIntent(VolumeInfo vol) { final Intent intent = new Intent(); - intent.setClassName("com.android.settings", - "com.android.settings.Settings$PublicVolumeSettingsActivity"); + switch (vol.getType()) { + case VolumeInfo.TYPE_PRIVATE: + intent.setClassName("com.android.settings", + "com.android.settings.Settings$PrivateVolumeSettingsActivity"); + break; + case VolumeInfo.TYPE_PUBLIC: + intent.setClassName("com.android.settings", + "com.android.settings.Settings$PublicVolumeSettingsActivity"); + break; + default: + return null; + } intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); final int requestKey = vol.getId().hashCode(); @@ -543,4 +602,38 @@ public class StorageNotification extends SystemUI { return PendingIntent.getActivityAsUser(mContext, requestKey, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); } + + private PendingIntent buildWizardMigratePendingIntent(MoveInfo move) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardMigrateProgress"); + intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId); + + final VolumeInfo vol = mStorageManager.findVolumeByQualifiedUuid(move.volumeUuid); + intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.getId()); + + return PendingIntent.getActivityAsUser(mContext, move.moveId, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } + + private PendingIntent buildWizardMovePendingIntent(MoveInfo move) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardMoveProgress"); + intent.putExtra(PackageManager.EXTRA_MOVE_ID, move.moveId); + + return PendingIntent.getActivityAsUser(mContext, move.moveId, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } + + private PendingIntent buildWizardReadyPendingIntent(DiskInfo disk) { + final Intent intent = new Intent(); + intent.setClassName("com.android.settings", + "com.android.settings.deviceinfo.StorageWizardReady"); + intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.getId()); + + final int requestKey = disk.getId().hashCode(); + return PendingIntent.getActivityAsUser(mContext, requestKey, intent, + PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); + } } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 74adeb7f5b96..0925fa52e524 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -229,6 +229,7 @@ class MountService extends IMountService.Stub public static final int VOLUME_FS_UUID_CHANGED = 653; public static final int VOLUME_FS_LABEL_CHANGED = 654; public static final int VOLUME_PATH_CHANGED = 655; + public static final int VOLUME_INTERNAL_PATH_CHANGED = 656; public static final int VOLUME_DESTROYED = 659; public static final int MOVE_STATUS = 660; @@ -661,6 +662,9 @@ class MountService extends IMountService.Stub try { mConnector.execute("volume", "reset"); + for (int userId : mStartedUsers) { + mConnector.execute("volume", "start_user", userId); + } } catch (NativeDaemonConnectorException e) { Slog.w(TAG, "Failed to reset vold", e); } @@ -902,6 +906,14 @@ class MountService extends IMountService.Stub } break; } + case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: { + if (cooked.length != 3) break; + final VolumeInfo vol = mVolumes.get(cooked[1]); + if (vol != null) { + vol.internalPath = cooked[2]; + } + break; + } case VoldResponseCode.VOLUME_DESTROYED: { if (cooked.length != 2) break; mVolumes.remove(cooked[1]); @@ -1076,7 +1088,7 @@ class MountService extends IMountService.Stub // TODO: estimate remaining time try { - mMoveCallback.onStatusChanged(-1, null, status, -1); + mMoveCallback.onStatusChanged(-1, status, -1); } catch (RemoteException ignored) { } @@ -1433,10 +1445,11 @@ class MountService extends IMountService.Stub enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); + Preconditions.checkNotNull(fsUuid); synchronized (mLock) { final VolumeRecord rec = mRecords.get(fsUuid); rec.nickname = nickname; - mCallbacks.notifyVolumeMetadataChanged(fsUuid); + mCallbacks.notifyVolumeRecordChanged(rec); writeSettingsLocked(); } } @@ -1446,10 +1459,11 @@ class MountService extends IMountService.Stub enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); + Preconditions.checkNotNull(fsUuid); synchronized (mLock) { final VolumeRecord rec = mRecords.get(fsUuid); rec.userFlags = (rec.userFlags & ~mask) | (flags & mask); - mCallbacks.notifyVolumeMetadataChanged(fsUuid); + mCallbacks.notifyVolumeRecordChanged(rec); writeSettingsLocked(); } } @@ -1459,10 +1473,36 @@ class MountService extends IMountService.Stub enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); + Preconditions.checkNotNull(fsUuid); synchronized (mLock) { mRecords.remove(fsUuid); - mCallbacks.notifyVolumeMetadataChanged(fsUuid); + + // TODO: tell vold to forget keys + + // If this had been primary storage, revert back to internal and + // reset vold so we bind into new volume into place. + if (Objects.equals(mPrimaryStorageUuid, fsUuid)) { + mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL; + resetIfReadyAndConnected(); + } + + mCallbacks.notifyVolumeForgotten(fsUuid); + writeSettingsLocked(); + } + } + + private void forgetAll() { + synchronized (mLock) { + for (int i = 0; i < mRecords.size(); i++) { + final String fsUuid = mRecords.keyAt(i); + mCallbacks.notifyVolumeForgotten(fsUuid); + } + + mRecords.clear(); writeSettingsLocked(); + + mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL; + resetIfReadyAndConnected(); } } @@ -2818,8 +2858,9 @@ class MountService extends IMountService.Stub private static class Callbacks extends Handler { 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; private final RemoteCallbackList mCallbacks = new RemoteCallbackList<>(); @@ -2863,8 +2904,12 @@ class MountService extends IMountService.Stub callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); break; } - case MSG_VOLUME_METADATA_CHANGED: { - callback.onVolumeMetadataChanged((String) args.arg1); + case MSG_VOLUME_RECORD_CHANGED: { + callback.onVolumeRecordChanged((VolumeRecord) args.arg1); + break; + } + case MSG_VOLUME_FORGOTTEN: { + callback.onVolumeForgotten((String) args.arg1); break; } case MSG_DISK_SCANNED: { @@ -2890,10 +2935,16 @@ class MountService extends IMountService.Stub obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); } - private void notifyVolumeMetadataChanged(String fsUuid) { + private void notifyVolumeRecordChanged(VolumeRecord rec) { + final SomeArgs args = SomeArgs.obtain(); + args.arg1 = rec.clone(); + obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); + } + + private void notifyVolumeForgotten(String fsUuid) { final SomeArgs args = SomeArgs.obtain(); args.arg1 = fsUuid; - obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget(); + obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); } private void notifyDiskScanned(DiskInfo disk, int volumeCount) { @@ -2909,11 +2960,8 @@ class MountService extends IMountService.Stub mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); for (String arg : args) { - if ("--clear".equals(arg)) { - synchronized (mLock) { - mRecords.clear(); - writeSettingsLocked(); - } + if ("--forget-all".equals(arg)) { + forgetAll(); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8a190568509f..453b8177ee20 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -155,6 +155,7 @@ import android.os.storage.IMountService; import android.os.storage.StorageEventListener; import android.os.storage.StorageManager; import android.os.storage.VolumeInfo; +import android.os.storage.VolumeRecord; import android.security.KeyStore; import android.security.SystemKeyStore; import android.system.ErrnoException; @@ -1558,6 +1559,11 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + + @Override + public void onVolumeForgotten(String fsUuid) { + // TODO: remove all packages hosted on this uuid + } }; private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int userId) { @@ -14203,7 +14209,7 @@ public class PackageManagerService extends IPackageManager.Stub { movePackageInternal(packageName, volumeUuid, moveId); } catch (PackageManagerException e) { Slog.d(TAG, "Failed to move " + packageName, e); - mMoveCallbacks.notifyStatusChanged(moveId, null, + mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_FAILED_INTERNAL_ERROR); } return moveId; @@ -14221,7 +14227,7 @@ public class PackageManagerService extends IPackageManager.Stub { final String packageAbiOverride; final int appId; final String seinfo; - final String moveTitle; + final String label; // reader synchronized (mPackages) { @@ -14251,9 +14257,14 @@ public class PackageManagerService extends IPackageManager.Stub { packageAbiOverride = ps.cpuAbiOverrideString; appId = UserHandle.getAppId(pkg.applicationInfo.uid); seinfo = pkg.applicationInfo.seinfo; - moveTitle = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo)); + label = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo)); } + final Bundle extras = new Bundle(); + extras.putString(Intent.EXTRA_PACKAGE_NAME, packageName); + extras.putString(Intent.EXTRA_TITLE, label); + mMoveCallbacks.notifyCreated(moveId, extras); + int installFlags; final boolean moveData; @@ -14279,7 +14290,7 @@ public class PackageManagerService extends IPackageManager.Stub { } Slog.d(TAG, "Moving " + packageName + " from " + currentVolumeUuid + " to " + volumeUuid); - mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, 10); + mMoveCallbacks.notifyStatusChanged(moveId, 10); if (moveData) { synchronized (mInstallLock) { @@ -14299,7 +14310,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, 50); + mMoveCallbacks.notifyStatusChanged(moveId, 50); final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() { @Override @@ -14326,15 +14337,15 @@ public class PackageManagerService extends IPackageManager.Stub { final int status = PackageManager.installStatusToPublicStatus(returnCode); switch (status) { case PackageInstaller.STATUS_SUCCESS: - mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, + mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_SUCCEEDED); break; case PackageInstaller.STATUS_FAILURE_STORAGE: - mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, + mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE); break; default: - mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, + mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_FAILED_INTERNAL_ERROR); break; } @@ -14357,12 +14368,19 @@ public class PackageManagerService extends IPackageManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); final int realMoveId = mNextMoveId.getAndIncrement(); - final String realTitle = null; + final Bundle extras = new Bundle(); + extras.putString(VolumeRecord.EXTRA_FS_UUID, volumeUuid); + mMoveCallbacks.notifyCreated(realMoveId, extras); final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() { @Override - public void onStatusChanged(int moveId, String title, int status, long estMillis) { - mMoveCallbacks.notifyStatusChanged(realMoveId, realTitle, status, estMillis); + public void onCreated(int moveId, Bundle extras) { + // Ignored + } + + @Override + public void onStatusChanged(int moveId, int status, long estMillis) { + mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis); } }; @@ -14717,6 +14735,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private static class MoveCallbacks extends Handler { + private static final int MSG_CREATED = 1; private static final int MSG_STATUS_CHANGED = 2; private final RemoteCallbackList @@ -14754,26 +14773,37 @@ public class PackageManagerService extends IPackageManager.Stub { private void invokeCallback(IPackageMoveObserver callback, int what, SomeArgs args) throws RemoteException { switch (what) { + case MSG_CREATED: { + callback.onCreated(args.argi1, (Bundle) args.arg2); + break; + } case MSG_STATUS_CHANGED: { - callback.onStatusChanged(args.argi1, (String) args.arg2, args.argi3, - (long) args.arg4); + callback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3); break; } } } - private void notifyStatusChanged(int moveId, String moveTitle, int status) { - notifyStatusChanged(moveId, moveTitle, status, -1); + private void notifyCreated(int moveId, Bundle extras) { + Slog.v(TAG, "Move " + moveId + " created " + extras.toString()); + + final SomeArgs args = SomeArgs.obtain(); + args.argi1 = moveId; + args.arg2 = extras; + obtainMessage(MSG_CREATED, args).sendToTarget(); + } + + private void notifyStatusChanged(int moveId, int status) { + notifyStatusChanged(moveId, status, -1); } - private void notifyStatusChanged(int moveId, String moveTitle, int status, long estMillis) { + private void notifyStatusChanged(int moveId, int status, long estMillis) { Slog.v(TAG, "Move " + moveId + " status " + status); final SomeArgs args = SomeArgs.obtain(); args.argi1 = moveId; - args.arg2 = moveTitle; - args.argi3 = status; - args.arg4 = estMillis; + args.argi2 = status; + args.arg3 = estMillis; obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget(); synchronized (mLastStatus) { -- cgit v1.2.3-59-g8ed1b