diff options
6 files changed, 198 insertions, 38 deletions
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index b84b1e2..39de1dc7 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -30,7 +30,6 @@ import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageInstaller; import android.content.pm.IPackageManager; -import android.content.pm.IPackageMoveObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; @@ -237,8 +236,12 @@ public final class Pm { return runForceDexOpt(); } - if ("move".equals(op)) { - return runMove(); + if ("move-package".equals(op)) { + return runMovePackage(); + } + + if ("move-primary-storage".equals(op)) { + return runMovePrimaryStorage(); } try { @@ -1285,7 +1288,7 @@ public final class Pm { } } - public int runMove() { + public int runMovePackage() { final String packageName = nextArg(); String volumeUuid = nextArg(); if ("internal".equals(volumeUuid)) { @@ -1313,6 +1316,33 @@ public final class Pm { } } + public int runMovePrimaryStorage() { + String volumeUuid = nextArg(); + if ("internal".equals(volumeUuid)) { + volumeUuid = null; + } + + try { + final int moveId = mPm.movePrimaryStorage(volumeUuid); + + int status = mPm.getMoveStatus(moveId); + while (!PackageManager.isMoveStatusFinished(status)) { + SystemClock.sleep(DateUtils.SECOND_IN_MILLIS); + status = mPm.getMoveStatus(moveId); + } + + if (status == PackageManager.MOVE_SUCCEEDED) { + System.out.println("Success"); + return 0; + } else { + System.err.println("Failure [" + status + "]"); + return 1; + } + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + private int runUninstall() throws RemoteException { int flags = 0; int userId = UserHandle.USER_ALL; @@ -1860,7 +1890,8 @@ public final class Pm { System.err.println(" pm install-abandon SESSION_ID"); System.err.println(" pm uninstall [-k] [--user USER_ID] PACKAGE"); System.err.println(" pm set-installer PACKAGE INSTALLER"); - System.err.println(" pm move PACKAGE [internal|UUID]"); + System.err.println(" pm move-package PACKAGE [internal|UUID]"); + System.err.println(" pm move-primary-storage [internal|UUID]"); System.err.println(" pm clear [--user USER_ID] PACKAGE"); System.err.println(" pm enable [--user USER_ID] PACKAGE_OR_COMPONENT"); System.err.println(" pm disable [--user USER_ID] PACKAGE_OR_COMPONENT"); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 10f5960..16a2430 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1556,6 +1556,7 @@ final class ApplicationPackageManager extends PackageManager { } } + @Override public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() { final StorageManager storage = mContext.getSystemService(StorageManager.class); final String volumeUuid = storage.getPrimaryStorageUuid(); @@ -1568,6 +1569,7 @@ final class ApplicationPackageManager extends PackageManager { } } + @Override public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() { final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo currentVol = getPrimaryStorageCurrentVolume(); diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 0b1031c..16e0bf7 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -16,6 +16,7 @@ package android.os.storage; +import android.content.pm.IPackageMoveObserver; import android.os.Binder; import android.os.IBinder; import android.os.IInterface; @@ -1082,12 +1083,14 @@ public interface IMountService extends IInterface { } @Override - public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException { + public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) + throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(volumeUuid); + _data.writeStrongBinder((callback != null ? callback.asBinder() : null)); mRemote.transact(Stub.TRANSACTION_setPrimaryStorageUuid, _data, _reply, 0); _reply.readException(); } finally { @@ -1714,7 +1717,9 @@ public interface IMountService extends IInterface { case TRANSACTION_setPrimaryStorageUuid: { data.enforceInterface(DESCRIPTOR); String volumeUuid = data.readString(); - setPrimaryStorageUuid(volumeUuid); + IPackageMoveObserver listener = IPackageMoveObserver.Stub.asInterface( + data.readStrongBinder()); + setPrimaryStorageUuid(volumeUuid, listener); reply.writeNoException(); return true; } @@ -2020,5 +2025,6 @@ public interface IMountService extends IInterface { public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException; public String getPrimaryStorageUuid() throws RemoteException; - public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException; + public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) + throws RemoteException; } diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 747fb40..6116aef 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -22,6 +22,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.IPackageMoveObserver; +import android.content.pm.PackageManager; import android.os.Environment; import android.os.FileUtils; import android.os.Handler; @@ -642,7 +644,12 @@ public class StorageManager { } } - /** {@hide} */ + /** + * This is not the API you're looking for. + * + * @see PackageManager#getPrimaryStorageCurrentVolume() + * @hide + */ public String getPrimaryStorageUuid() { try { return mMountService.getPrimaryStorageUuid(); @@ -651,10 +658,15 @@ public class StorageManager { } } - /** {@hide} */ - public void setPrimaryStorageUuid(String volumeUuid) { + /** + * This is not the API you're looking for. + * + * @see PackageManager#movePrimaryStorage(VolumeInfo) + * @hide + */ + public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { try { - mMountService.setPrimaryStorageUuid(volumeUuid); + mMountService.setPrimaryStorageUuid(volumeUuid, callback); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 81088c4..7172ab7 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -30,6 +30,8 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.pm.IPackageMoveObserver; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.ObbInfo; import android.mtp.MtpStorage; @@ -178,6 +180,9 @@ class MountService extends IMountService.Stub /** Maximum number of ASEC containers allowed to be mounted. */ private static final int MAX_CONTAINERS = 250; + /** Magic value sent by MoveTask.cpp */ + private static final int MOVE_STATUS_COPY_FINISHED = 82; + /* * Internal vold response code constants */ @@ -226,6 +231,8 @@ class MountService extends IMountService.Stub public static final int VOLUME_PATH_CHANGED = 655; public static final int VOLUME_DESTROYED = 659; + public static final int MOVE_STATUS = 660; + /* * 700 series - fstrim */ @@ -314,6 +321,11 @@ class MountService extends IMountService.Stub @GuardedBy("mLock") private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>(); + @GuardedBy("mLock") + private IPackageMoveObserver mMoveCallback; + @GuardedBy("mLock") + private String mMoveTargetUuid; + private DiskInfo findDiskById(String id) { synchronized (mLock) { final DiskInfo disk = mDisks.get(id); @@ -347,6 +359,17 @@ class MountService extends IMountService.Stub throw new IllegalArgumentException("No volume found for path " + path); } + private VolumeInfo findStorageForUuid(String volumeUuid) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { + return findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL); + } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { + return storage.getPrimaryPhysicalVolume(); + } else { + return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid)); + } + } + private VolumeMetadata findOrCreateMetadataLocked(VolumeInfo vol) { VolumeMetadata meta = mMetadata.get(vol.fsUuid); if (meta == null) { @@ -937,6 +960,12 @@ class MountService extends IMountService.Stub break; } + case VoldResponseCode.MOVE_STATUS: { + final int status = Integer.parseInt(cooked[1]); + onMoveStatusLocked(status); + break; + } + case VoldResponseCode.FstrimCompleted: { EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime()); break; @@ -972,24 +1001,36 @@ class MountService extends IMountService.Stub } private void onVolumeCreatedLocked(VolumeInfo vol) { - final boolean primaryPhysical = SystemProperties.getBoolean( - StorageManager.PROP_PRIMARY_PHYSICAL, false); - // TODO: enable switching to another emulated primary - if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.id) && !primaryPhysical) { - vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; - vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; - mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); + if (vol.type == VolumeInfo.TYPE_EMULATED) { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final VolumeInfo privateVol = storage.findPrivateForEmulated(vol); + + if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid) + && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) { + Slog.v(TAG, "Found primary storage at " + vol); + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); + + } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) { + Slog.v(TAG, "Found primary storage at " + vol); + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); + } } else if (vol.type == VolumeInfo.TYPE_PUBLIC) { - if (primaryPhysical) { + // TODO: only look at first public partition + if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) + && vol.disk.isDefaultPrimary()) { + Slog.v(TAG, "Found primary storage at " + vol); vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; } // Adoptable public disks are visible to apps, since they meet // public API requirement of being in a stable location. - final DiskInfo disk = mDisks.get(vol.getDiskId()); - if (disk != null && disk.isAdoptable()) { + if (vol.disk.isAdoptable()) { vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; } @@ -1066,6 +1107,35 @@ class MountService extends IMountService.Stub } } + private void onMoveStatusLocked(int status) { + if (mMoveCallback == null) { + Slog.w(TAG, "Odd, status but no move requested"); + return; + } + + // TODO: estimate remaining time + try { + mMoveCallback.onStatusChanged(-1, status, -1); + } catch (RemoteException ignored) { + } + + // We've finished copying and we're about to clean up old data, so + // remember that move was successful if we get rebooted + if (status == MOVE_STATUS_COPY_FINISHED) { + Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting"); + + mPrimaryStorageUuid = mMoveTargetUuid; + writeMetadataLocked(); + } + + if (PackageManager.isMoveStatusFinished(status)) { + Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status); + + mMoveCallback = null; + mMoveTargetUuid = null; + } + } + /** * Refresh latest metadata into any currently active {@link VolumeInfo}. */ @@ -1322,12 +1392,17 @@ class MountService extends IMountService.Stub final VolumeInfo vol = findVolumeById(volId); // TODO: expand PMS to know about multiple volumes - if (vol.isPrimary()) { - synchronized (mUnmountLock) { - mUnmountSignal = new CountDownLatch(1); - mPms.updateExternalMediaStatus(false, true); - waitForLatch(mUnmountSignal, "mUnmountSignal"); - mUnmountSignal = null; + if (vol.isPrimaryPhysical()) { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mUnmountLock) { + mUnmountSignal = new CountDownLatch(1); + mPms.updateExternalMediaStatus(false, true); + waitForLatch(mUnmountSignal, "mUnmountSignal"); + mUnmountSignal = null; + } + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -1424,20 +1499,41 @@ class MountService extends IMountService.Stub } @Override - public String getPrimaryStorageUuid() throws RemoteException { + public String getPrimaryStorageUuid() { + enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); + synchronized (mLock) { return mPrimaryStorageUuid; } } @Override - public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException { + public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { + enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); + synchronized (mLock) { - Slog.d(TAG, "Changing primary storage UUID to " + volumeUuid); - mPrimaryStorageUuid = volumeUuid; - writeMetadataLocked(); + final VolumeInfo from = Preconditions.checkNotNull( + findStorageForUuid(mPrimaryStorageUuid)); + final VolumeInfo to = Preconditions.checkNotNull( + findStorageForUuid(volumeUuid)); - // TODO: reevaluate all volumes we know about! + if (Objects.equals(from, to)) { + throw new IllegalArgumentException("Primary storage already at " + from); + } + + if (mMoveCallback != null) { + throw new IllegalStateException("Move already in progress"); + } + mMoveCallback = callback; + mMoveTargetUuid = volumeUuid; + + try { + mConnector.execute("volume", "move_storage", from.id, to.id); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } } } @@ -2875,6 +2971,9 @@ class MountService extends IMountService.Stub meta.dump(pw); } pw.decreaseIndent(); + + pw.println(); + pw.println("Primary storage UUID: " + mPrimaryStorageUuid); } synchronized (mObbMounts) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index bd22524..f087c33 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -14307,12 +14307,22 @@ public class PackageManagerService extends IPackageManager.Stub { public int movePrimaryStorage(String volumeUuid) throws RemoteException { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); - final int moveId = mNextMoveId.getAndIncrement(); + final int realMoveId = mNextMoveId.getAndIncrement(); + final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() { + @Override + public void onStarted(int moveId, String title) { + // Ignored + } - // TODO: ask mountservice to take down both, connect over to DCS to - // migrate, and then bring up new storage + @Override + public void onStatusChanged(int moveId, int status, long estMillis) { + mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis); + } + }; - return moveId; + final StorageManager storage = mContext.getSystemService(StorageManager.class); + storage.setPrimaryStorageUuid(volumeUuid, callback); + return realMoveId; } @Override |