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);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index deed895..e6c95b5 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.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.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 @@
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<MoveInfo> mMoves = new SparseArray<>();
+
private final StorageEventListener mListener = new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
@@ -64,20 +76,20 @@
}
@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 @@
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 @@
.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 @@
.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 @@
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 @@
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 @@
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 @@
.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 @@
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 @@
.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 @@
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 @@
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 @@
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 74adeb7..0925fa5 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -229,6 +229,7 @@
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 @@
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 @@
}
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 @@
// TODO: estimate remaining time
try {
- mMoveCallback.onStatusChanged(-1, null, status, -1);
+ mMoveCallback.onStatusChanged(-1, status, -1);
} catch (RemoteException ignored) {
}
@@ -1433,10 +1445,11 @@
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 @@
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,13 +1473,39 @@
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();
+ }
+ }
+
@Override
public String getPrimaryStorageUuid() {
enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
@@ -2818,8 +2858,9 @@
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<IMountServiceListener>
mCallbacks = new RemoteCallbackList<>();
@@ -2863,8 +2904,12 @@
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 @@
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 @@
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 8a19056..453b817 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.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 @@
}
}
}
+
+ @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 @@
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 @@
final String packageAbiOverride;
final int appId;
final String seinfo;
- final String moveTitle;
+ final String label;
// reader
synchronized (mPackages) {
@@ -14251,9 +14257,14 @@
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 @@
}
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 @@
}
}
- mMoveCallbacks.notifyStatusChanged(moveId, moveTitle, 50);
+ mMoveCallbacks.notifyStatusChanged(moveId, 50);
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@Override
@@ -14326,15 +14337,15 @@
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 @@
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 @@
}
private static class MoveCallbacks extends Handler {
+ private static final int MSG_CREATED = 1;
private static final int MSG_STATUS_CHANGED = 2;
private final RemoteCallbackList<IPackageMoveObserver>
@@ -14754,26 +14773,37 @@
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, String moveTitle, int status, long estMillis) {
+ private void notifyStatusChanged(int moveId, int status) {
+ notifyStatusChanged(moveId, status, -1);
+ }
+
+ 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) {