diff options
17 files changed, 631 insertions, 232 deletions
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index d5cc8cc..b84b1e2 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -51,9 +51,11 @@ import android.os.Bundle; import android.os.IUserManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; +import android.text.format.DateUtils; import android.util.Log; import libcore.io.IoUtils; @@ -1283,20 +1285,6 @@ public final class Pm { } } - class LocalPackageMoveObserver extends IPackageMoveObserver.Stub { - boolean finished; - int returnCode; - - @Override - public void packageMoved(String packageName, int returnCode) throws RemoteException { - synchronized (this) { - this.finished = true; - this.returnCode = returnCode; - notifyAll(); - } - } - } - public int runMove() { final String packageName = nextArg(); String volumeUuid = nextArg(); @@ -1304,24 +1292,21 @@ public final class Pm { volumeUuid = null; } - final LocalPackageMoveObserver obs = new LocalPackageMoveObserver(); try { - mPm.movePackageAndData(packageName, volumeUuid, obs); + final int moveId = mPm.movePackage(packageName, volumeUuid); - synchronized (obs) { - while (!obs.finished) { - try { - obs.wait(); - } catch (InterruptedException e) { - } - } - if (obs.returnCode == PackageManager.MOVE_SUCCEEDED) { - System.out.println("Success"); - return 0; - } else { - System.err.println("Failure [" + obs.returnCode + "]"); - return 1; - } + 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(); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index dfe7e18..10f5960 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -62,6 +62,9 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.Process; import android.os.RemoteException; import android.os.SystemProperties; @@ -81,7 +84,9 @@ import com.android.internal.util.UserIcons; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Objects; /*package*/ final class ApplicationPackageManager extends PackageManager { @@ -98,6 +103,9 @@ final class ApplicationPackageManager extends PackageManager { @GuardedBy("mLock") private PackageInstaller mInstaller; + @GuardedBy("mDelegates") + private final ArrayList<MoveCallbackDelegate> mDelegates = new ArrayList<>(); + UserManager getUserManager() { synchronized (mLock) { if (mUserManager == null) { @@ -1410,57 +1418,100 @@ final class ApplicationPackageManager extends PackageManager { } @Override - public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { + public String getInstallerPackageName(String packageName) { + try { + return mPM.getInstallerPackageName(packageName); + } catch (RemoteException e) { + // Should never happen! + } + return null; + } + + @Override + public int getMoveStatus(int moveId) { try { - mPM.movePackage(packageName, observer, flags); + return mPM.getMoveStatus(moveId); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override - public void movePackageAndData(String packageName, String volumeUuid, - IPackageMoveObserver observer) { + public void registerMoveCallback(MoveCallback callback, Handler handler) { + synchronized (mDelegates) { + final MoveCallbackDelegate delegate = new MoveCallbackDelegate(callback, + handler.getLooper()); + try { + mPM.registerMoveCallback(delegate); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + mDelegates.add(delegate); + } + } + + @Override + public void unregisterMoveCallback(MoveCallback callback) { + synchronized (mDelegates) { + for (Iterator<MoveCallbackDelegate> i = mDelegates.iterator(); i.hasNext();) { + final MoveCallbackDelegate delegate = i.next(); + if (delegate.mCallback == callback) { + try { + mPM.unregisterMoveCallback(delegate); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + i.remove(); + } + } + } + } + + @Override + public int movePackage(String packageName, VolumeInfo vol) { try { - mPM.movePackageAndData(packageName, volumeUuid, observer); + final String volumeUuid; + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { + volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; + } else if (vol.isPrimaryPhysical()) { + volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + volumeUuid = Preconditions.checkNotNull(vol.fsUuid); + } + + return mPM.movePackage(packageName, volumeUuid); } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); } } @Override - public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) { + public @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); if (app.isInternal()) { - return Preconditions.checkNotNull( - storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL)); + return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); } else if (app.isExternalAsec()) { - final List<VolumeInfo> vols = storage.getVolumes(); - for (VolumeInfo vol : vols) { - if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) { - return vol; - } - } - throw new IllegalStateException("Failed to find primary public volume"); + return storage.getPrimaryPhysicalVolume(); } else { - return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid)); + return storage.findVolumeByUuid(app.volumeUuid); } } @Override - public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) { + public @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) { final StorageManager storage = mContext.getSystemService(StorageManager.class); + final VolumeInfo currentVol = getPackageCurrentVolume(app); final List<VolumeInfo> vols = storage.getVolumes(); final List<VolumeInfo> candidates = new ArrayList<>(); for (VolumeInfo vol : vols) { - if (isCandidateVolume(app, vol)) { + if (Objects.equals(vol, currentVol) || isPackageCandidateVolume(app, vol)) { candidates.add(vol); } } return candidates; } - private static boolean isCandidateVolume(ApplicationInfo app, VolumeInfo vol) { + private static boolean isPackageCandidateVolume(ApplicationInfo app, VolumeInfo vol) { // Private internal is always an option if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { return true; @@ -1473,10 +1524,14 @@ final class ApplicationPackageManager extends PackageManager { return false; } - // Moving into an ASEC on public primary is only an option when app is - // internal, or already in ASEC - if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isPrimary()) { - return app.isInternal() || app.isExternalAsec(); + // Gotta be able to write there + if (!vol.isMountedWritable()) { + return false; + } + + // Moving into an ASEC on public primary is only option internal + if (vol.isPrimaryPhysical()) { + return app.isInternal(); } // Otherwise we can move to any private volume @@ -1484,13 +1539,66 @@ final class ApplicationPackageManager extends PackageManager { } @Override - public String getInstallerPackageName(String packageName) { + public int movePrimaryStorage(VolumeInfo vol) { try { - return mPM.getInstallerPackageName(packageName); + final String volumeUuid; + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) { + volumeUuid = StorageManager.UUID_PRIVATE_INTERNAL; + } else if (vol.isPrimaryPhysical()) { + volumeUuid = StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + volumeUuid = Preconditions.checkNotNull(vol.fsUuid); + } + + return mPM.movePrimaryStorage(volumeUuid); } catch (RemoteException e) { - // Should never happen! + throw e.rethrowAsRuntimeException(); } - return null; + } + + 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); + } + } + + public @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final VolumeInfo currentVol = getPrimaryStorageCurrentVolume(); + final List<VolumeInfo> vols = storage.getVolumes(); + final List<VolumeInfo> candidates = new ArrayList<>(); + for (VolumeInfo vol : vols) { + if (Objects.equals(vol, currentVol) || isPrimaryStorageCandidateVolume(vol)) { + candidates.add(vol); + } + } + return candidates; + } + + private static boolean isPrimaryStorageCandidateVolume(VolumeInfo vol) { + // Private internal is always an option + if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.getId())) { + return true; + } + + // Gotta be able to write there + if (!vol.isMountedWritable()) { + return false; + } + + // We can move to public volumes on legacy devices + if ((vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.getDisk().isDefaultPrimary()) { + return true; + } + + // Otherwise we can move to any private volume + return (vol.getType() == VolumeInfo.TYPE_PRIVATE); } @Override @@ -1941,6 +2049,45 @@ final class ApplicationPackageManager extends PackageManager { return null; } + /** {@hide} */ + private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements + Handler.Callback { + private static final int MSG_STARTED = 1; + private static final int MSG_STATUS_CHANGED = 2; + + final MoveCallback mCallback; + final Handler mHandler; + + public MoveCallbackDelegate(MoveCallback callback, Looper looper) { + mCallback = callback; + mHandler = new Handler(looper, this); + } + + @Override + public boolean handleMessage(Message msg) { + final int moveId = msg.arg1; + switch (msg.what) { + case MSG_STARTED: + mCallback.onStarted(moveId, (String) msg.obj); + return true; + case MSG_STATUS_CHANGED: + mCallback.onStatusChanged(moveId, msg.arg2, (long) msg.obj); + return true; + } + return false; + } + + @Override + public void onStarted(int moveId, String title) { + mHandler.obtainMessage(MSG_STARTED, moveId, 0, title).sendToTarget(); + } + + @Override + public void onStatusChanged(int moveId, int status, long estMillis) { + mHandler.obtainMessage(MSG_STATUS_CHANGED, moveId, status, estMillis).sendToTarget(); + } + } + private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 447c668..ae59bfc 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -50,7 +50,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.content.IntentSender; -import com.android.internal.os.IResultReceiver; /** * See {@link PackageManager} for documentation on most of the APIs @@ -431,8 +430,13 @@ interface IPackageManager { PackageCleanItem nextPackageToClean(in PackageCleanItem lastPackage); - void movePackage(String packageName, IPackageMoveObserver observer, int flags); - void movePackageAndData(String packageName, String volumeUuid, IPackageMoveObserver observer); + int getMoveStatus(int moveId); + + void registerMoveCallback(in IPackageMoveObserver callback); + void unregisterMoveCallback(in IPackageMoveObserver callback); + + int movePackage(in String packageName, in String volumeUuid); + int movePrimaryStorage(in String volumeUuid); boolean addPermissionAsync(in PermissionInfo info); diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl index baa1595..50ab3b5 100644 --- a/core/java/android/content/pm/IPackageMoveObserver.aidl +++ b/core/java/android/content/pm/IPackageMoveObserver.aidl @@ -22,6 +22,6 @@ package android.content.pm; * @hide */ oneway interface IPackageMoveObserver { - void packageMoved(in String packageName, int returnCode); + void onStarted(int moveId, String title); + 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 e4108b1..e1c271d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -42,6 +42,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Environment; +import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.storage.VolumeInfo; @@ -875,7 +876,8 @@ public abstract class PackageManager { * * @hide */ - public static final int MOVE_SUCCEEDED = 1; + public static final int MOVE_SUCCEEDED = -100; + /** * Error code that is passed to the {@link IPackageMoveObserver} by * {@link #movePackage(android.net.Uri, IPackageMoveObserver)} @@ -941,6 +943,7 @@ public abstract class PackageManager { * been installed on external media. * @hide */ + @Deprecated public static final int MOVE_INTERNAL = 0x00000001; /** @@ -948,8 +951,12 @@ public abstract class PackageManager { * the package should be moved to external media. * @hide */ + @Deprecated public static final int MOVE_EXTERNAL_MEDIA = 0x00000002; + /** {@hide} */ + public static final String EXTRA_MOVE_ID = "android.content.pm.extra.MOVE_ID"; + /** * Usable by the required verifier as the {@code verificationCode} argument * for {@link PackageManager#verifyPendingInstall} to indicate that it will @@ -4183,17 +4190,42 @@ public abstract class PackageManager { * @hide */ @Deprecated - public abstract void movePackage(String packageName, IPackageMoveObserver observer, int flags); + public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { + throw new UnsupportedOperationException(); + } /** {@hide} */ - public abstract void movePackageAndData(String packageName, String volumeUuid, - IPackageMoveObserver observer); + public static boolean isMoveStatusFinished(int status) { + return (status < 0 || status > 100); + } + + /** {@hide} */ + public static abstract class MoveCallback { + public abstract void onStarted(int moveId, String title); + public abstract void onStatusChanged(int moveId, int status, long estMillis); + } /** {@hide} */ - public abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app); + public abstract int getMoveStatus(int moveId); /** {@hide} */ - public abstract @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app); + public abstract void registerMoveCallback(MoveCallback callback, Handler handler); + /** {@hide} */ + public abstract void unregisterMoveCallback(MoveCallback callback); + + /** {@hide} */ + public abstract int movePackage(String packageName, VolumeInfo vol); + /** {@hide} */ + public abstract @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app); + /** {@hide} */ + public abstract @NonNull List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app); + + /** {@hide} */ + public abstract int movePrimaryStorage(VolumeInfo vol); + /** {@hide} */ + public abstract @Nullable VolumeInfo getPrimaryStorageCurrentVolume(); + /** {@hide} */ + public abstract @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes(); /** * Returns the device identity that verifiers can use to associate their scheme to a particular diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index 64f2a05..9623695 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -36,7 +36,10 @@ import java.util.Objects; * @hide */ public class DiskInfo implements Parcelable { - public static final String EXTRA_DISK_ID = "android.os.storage.extra.DISK_ID"; + public static final String ACTION_DISK_SCANNED = + "android.os.storage.action.DISK_SCANNED"; + public static final String EXTRA_DISK_ID = + "android.os.storage.extra.DISK_ID"; public static final int FLAG_ADOPTABLE = 1 << 0; public static final int FLAG_DEFAULT_PRIMARY = 1 << 1; @@ -96,6 +99,14 @@ public class DiskInfo implements Parcelable { } } + public boolean isAdoptable() { + return (flags & FLAG_ADOPTABLE) != 0; + } + + public boolean isDefaultPrimary() { + return (flags & FLAG_DEFAULT_PRIMARY) != 0; + } + public boolean isSd() { return (flags & FLAG_SD) != 0; } @@ -104,10 +115,6 @@ public class DiskInfo implements Parcelable { return (flags & FLAG_USB) != 0; } - public boolean isAdoptable() { - return (flags & FLAG_ADOPTABLE) != 0; - } - @Override public String toString() { final CharArrayWriter writer = new CharArrayWriter(); diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 0a8187e..0b1031c 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -1063,6 +1063,38 @@ public interface IMountService extends IInterface { _data.recycle(); } } + + @Override + public String getPrimaryStorageUuid() throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + String _result; + try { + _data.writeInterfaceToken(DESCRIPTOR); + mRemote.transact(Stub.TRANSACTION_getPrimaryStorageUuid, _data, _reply, 0); + _reply.readException(); + _result = _reply.readString(); + } finally { + _reply.recycle(); + _data.recycle(); + } + return _result; + } + + @Override + public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(volumeUuid); + mRemote.transact(Stub.TRANSACTION_setPrimaryStorageUuid, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } } private static final String DESCRIPTOR = "IMountService"; @@ -1169,6 +1201,9 @@ public interface IMountService extends IInterface { static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 52; static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 53; + static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 54; + static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 55; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -1669,6 +1704,20 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_getPrimaryStorageUuid: { + data.enforceInterface(DESCRIPTOR); + String volumeUuid = getPrimaryStorageUuid(); + reply.writeNoException(); + reply.writeString(volumeUuid); + return true; + } + case TRANSACTION_setPrimaryStorageUuid: { + data.enforceInterface(DESCRIPTOR); + String volumeUuid = data.readString(); + setPrimaryStorageUuid(volumeUuid); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); } @@ -1969,4 +2018,7 @@ public interface IMountService extends IInterface { public void setVolumeNickname(String volId, String nickname) throws RemoteException; public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException; + + public String getPrimaryStorageUuid() throws RemoteException; + public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException; } diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java index 8e878a4..fcb4779 100644 --- a/core/java/android/os/storage/IMountServiceListener.java +++ b/core/java/android/os/storage/IMountServiceListener.java @@ -98,10 +98,11 @@ public interface IMountServiceListener extends IInterface { reply.writeNoException(); return true; } - case TRANSACTION_onDiskUnsupported: { + case TRANSACTION_onDiskScanned: { data.enforceInterface(DESCRIPTOR); final DiskInfo disk = (DiskInfo) data.readParcelable(null); - onDiskUnsupported(disk); + final int volumeCount = data.readInt(); + onDiskScanned(disk, volumeCount); reply.writeNoException(); return true; } @@ -207,13 +208,14 @@ public interface IMountServiceListener extends IInterface { } @Override - public void onDiskUnsupported(DiskInfo disk) throws RemoteException { + public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeParcelable(disk, 0); - mRemote.transact(Stub.TRANSACTION_onDiskUnsupported, _data, _reply, + _data.writeInt(volumeCount); + mRemote.transact(Stub.TRANSACTION_onDiskScanned, _data, _reply, android.os.IBinder.FLAG_ONEWAY); _reply.readException(); } finally { @@ -224,12 +226,10 @@ 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_onDiskUnsupported = (IBinder.FIRST_CALL_TRANSACTION + 4); + static final int TRANSACTION_onDiskScanned = (IBinder.FIRST_CALL_TRANSACTION + 4); } /** @@ -255,5 +255,5 @@ public interface IMountServiceListener extends IInterface { public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException; - public void onDiskUnsupported(DiskInfo disk) 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 ad2fae0..6a0140e 100644 --- a/core/java/android/os/storage/StorageEventListener.java +++ b/core/java/android/os/storage/StorageEventListener.java @@ -44,6 +44,6 @@ public class StorageEventListener { public void onVolumeMetadataChanged(VolumeInfo vol) { } - public void onDiskUnsupported(DiskInfo disk) { + 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 f101352..747fb40 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -73,6 +73,11 @@ public class StorageManager { public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; /** {@hide} */ + public static final String UUID_PRIVATE_INTERNAL = null; + /** {@hide} */ + public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; + + /** {@hide} */ public static final int FLAG_ALL_METADATA = 1 << 0; private final Context mContext; @@ -89,7 +94,7 @@ public class StorageManager { 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_UNSUPPORTED = 4; + private static final int MSG_DISK_SCANNED = 4; final StorageEventListener mCallback; final Handler mHandler; @@ -116,8 +121,8 @@ public class StorageManager { mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1); args.recycle(); return true; - case MSG_DISK_UNSUPPORTED: - mCallback.onDiskUnsupported((DiskInfo) args.arg1); + case MSG_DISK_SCANNED: + mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); args.recycle(); return true; } @@ -156,10 +161,11 @@ public class StorageManager { } @Override - public void onDiskUnsupported(DiskInfo disk) { + public void onDiskScanned(DiskInfo disk, int volumeCount) { final SomeArgs args = SomeArgs.obtain(); args.arg1 = disk; - mHandler.obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget(); + args.argi2 = volumeCount; + mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); } } @@ -534,17 +540,26 @@ public class StorageManager { /** {@hide} */ public @Nullable String getBestVolumeDescription(VolumeInfo vol) { String descrip = vol.getDescription(); - if (vol.disk != null) { if (TextUtils.isEmpty(descrip)) { descrip = vol.disk.getDescription(); } } - return descrip; } /** {@hide} */ + public @Nullable VolumeInfo getPrimaryPhysicalVolume() { + final List<VolumeInfo> vols = getVolumes(); + for (VolumeInfo vol : vols) { + if (vol.isPrimaryPhysical()) { + return vol; + } + } + return null; + } + + /** {@hide} */ public void mount(String volId) { try { mMountService.mount(volId); @@ -628,6 +643,24 @@ public class StorageManager { } /** {@hide} */ + public String getPrimaryStorageUuid() { + try { + return mMountService.getPrimaryStorageUuid(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ + public void setPrimaryStorageUuid(String volumeUuid) { + try { + mMountService.setPrimaryStorageUuid(volumeUuid); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ public @Nullable StorageVolume getStorageVolume(File file) { return getStorageVolume(getVolumeList(), file); } diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index f3498d5..4e9cfc7 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -242,6 +242,10 @@ public class VolumeInfo implements Parcelable { return (mountFlags & MOUNT_FLAG_PRIMARY) != 0; } + public boolean isPrimaryPhysical() { + return isPrimary() && (getType() == TYPE_PUBLIC); + } + public boolean isVisible() { return (mountFlags & MOUNT_FLAG_VISIBLE) != 0; } diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index 414b7bc..b692a18 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -24,6 +24,12 @@ import java.util.Collection; */ public class Preconditions { + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + /** * Ensures that an object reference passed as a parameter to the calling * method is not null. diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 279bfbf..baa772e 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -1587,91 +1587,13 @@ public class PackageManagerTests extends AndroidTestCase { } } - private class PackageMoveObserver extends IPackageMoveObserver.Stub { - public int returnCode; - - private boolean doneFlag = false; - - public String packageName; - - public PackageMoveObserver(String pkgName) { - packageName = pkgName; - } - - public void packageMoved(String packageName, int returnCode) { - Log.i("DEBUG_MOVE::", "pkg = " + packageName + ", " + "ret = " + returnCode); - if (!packageName.equals(this.packageName)) { - return; - } - synchronized (this) { - this.returnCode = returnCode; - doneFlag = true; - notifyAll(); - } - } - - public boolean isDone() { - return doneFlag; - } - } - public boolean invokeMovePackage(String pkgName, int flags, GenericReceiver receiver) throws Exception { - PackageMoveObserver observer = new PackageMoveObserver(pkgName); - final boolean received = false; - mContext.registerReceiver(receiver, receiver.filter); - try { - // Wait on observer - synchronized (observer) { - synchronized (receiver) { - getPm().movePackage(pkgName, observer, flags); - long waitTime = 0; - while ((!observer.isDone()) && (waitTime < MAX_WAIT_TIME)) { - observer.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; - } - if (!observer.isDone()) { - throw new Exception("Timed out waiting for pkgmove callback"); - } - if (observer.returnCode != PackageManager.MOVE_SUCCEEDED) { - return false; - } - // Verify we received the broadcast - waitTime = 0; - while ((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME)) { - receiver.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; - } - if (!receiver.isDone()) { - throw new Exception("Timed out waiting for MOVE notifications"); - } - return receiver.received; - } - } - } finally { - mContext.unregisterReceiver(receiver); - } + throw new UnsupportedOperationException(); } private boolean invokeMovePackageFail(String pkgName, int flags, int errCode) throws Exception { - PackageMoveObserver observer = new PackageMoveObserver(pkgName); - try { - // Wait on observer - synchronized (observer) { - getPm().movePackage(pkgName, observer, flags); - long waitTime = 0; - while ((!observer.isDone()) && (waitTime < MAX_WAIT_TIME)) { - observer.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; - } - if (!observer.isDone()) { - throw new Exception("Timed out waiting for pkgmove callback"); - } - assertEquals(errCode, observer.returnCode); - } - } finally { - } - return true; + throw new UnsupportedOperationException(); } private int getDefaultInstallLoc() { diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index f88802a..89a7173 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -232,7 +232,12 @@ class MountService extends IMountService.Stub public static final int FstrimCompleted = 700; } + private static final int VERSION_INIT = 1; + private static final int VERSION_ADD_PRIMARY = 2; + private static final String TAG_VOLUMES = "volumes"; + private static final String ATTR_VERSION = "version"; + private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid"; private static final String TAG_VOLUME = "volume"; private static final String ATTR_TYPE = "type"; private static final String ATTR_FS_UUID = "fsUuid"; @@ -302,6 +307,8 @@ class MountService extends IMountService.Stub /** Map from UUID to metadata */ @GuardedBy("mLock") private ArrayMap<String, VolumeMetadata> mMetadata = new ArrayMap<>(); + @GuardedBy("mLock") + private String mPrimaryStorageUuid; /** Map from disk ID to latches */ @GuardedBy("mLock") @@ -943,22 +950,25 @@ class MountService extends IMountService.Stub } private void onDiskScannedLocked(DiskInfo disk) { + final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, + android.Manifest.permission.WRITE_MEDIA_STORAGE); + final CountDownLatch latch = mDiskScanLatches.remove(disk.id); if (latch != null) { latch.countDown(); } - boolean empty = true; + int volumeCount = 0; for (int i = 0; i < mVolumes.size(); i++) { final VolumeInfo vol = mVolumes.valueAt(i); if (Objects.equals(disk.id, vol.getDiskId())) { - empty = false; + volumeCount++; } } - if (empty) { - mCallbacks.notifyDiskUnsupported(disk); - } + mCallbacks.notifyDiskScanned(disk, volumeCount); } private void onVolumeCreatedLocked(VolumeInfo vol) { @@ -1022,8 +1032,8 @@ class MountService extends IMountService.Stub if (isBroadcastWorthy(vol)) { final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - // TODO: require receiver to hold permission - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, + android.Manifest.permission.WRITE_MEDIA_STORAGE); } final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState); @@ -1166,7 +1176,21 @@ class MountService extends IMountService.Stub while ((type = in.next()) != END_DOCUMENT) { if (type == START_TAG) { final String tag = in.getName(); - if (TAG_VOLUME.equals(tag)) { + if (TAG_VOLUMES.equals(tag)) { + final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT); + if (version >= VERSION_ADD_PRIMARY) { + mPrimaryStorageUuid = readStringAttribute(in, + ATTR_PRIMARY_STORAGE_UUID); + } else { + if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, + false)) { + mPrimaryStorageUuid = StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + mPrimaryStorageUuid = StorageManager.UUID_PRIVATE_INTERNAL; + } + } + + } else if (TAG_VOLUME.equals(tag)) { final VolumeMetadata meta = VolumeMetadata.read(in); mMetadata.put(meta.fsUuid, meta); } @@ -1192,6 +1216,8 @@ class MountService extends IMountService.Stub out.setOutput(fos, "utf-8"); out.startDocument(null, true); out.startTag(null, TAG_VOLUMES); + writeIntAttribute(out, ATTR_VERSION, VERSION_ADD_PRIMARY); + writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); final int size = mMetadata.size(); for (int i = 0; i < size; i++) { final VolumeMetadata meta = mMetadata.valueAt(i); @@ -1398,6 +1424,24 @@ class MountService extends IMountService.Stub } @Override + public String getPrimaryStorageUuid() throws RemoteException { + synchronized (mLock) { + return mPrimaryStorageUuid; + } + } + + @Override + public void setPrimaryStorageUuid(String volumeUuid) throws RemoteException { + synchronized (mLock) { + Slog.d(TAG, "Changing primary storage UUID to " + volumeUuid); + mPrimaryStorageUuid = volumeUuid; + writeMetadataLocked(); + + // TODO: reevaluate all volumes we know about! + } + } + + @Override public int[] getStorageUsers(String path) { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); @@ -2700,7 +2744,7 @@ class MountService extends IMountService.Stub 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_UNSUPPORTED = 4; + private static final int MSG_DISK_SCANNED = 4; private final RemoteCallbackList<IMountServiceListener> mCallbacks = new RemoteCallbackList<>(); @@ -2748,8 +2792,8 @@ class MountService extends IMountService.Stub callback.onVolumeMetadataChanged((VolumeInfo) args.arg1); break; } - case MSG_DISK_UNSUPPORTED: { - callback.onDiskUnsupported((DiskInfo) args.arg1); + case MSG_DISK_SCANNED: { + callback.onDiskScanned((DiskInfo) args.arg1, args.argi2); break; } } @@ -2777,10 +2821,11 @@ class MountService extends IMountService.Stub obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget(); } - private void notifyDiskUnsupported(DiskInfo disk) { + private void notifyDiskScanned(DiskInfo disk, int volumeCount) { final SomeArgs args = SomeArgs.obtain(); args.arg1 = disk; - obtainMessage(MSG_DISK_UNSUPPORTED, args).sendToTarget(); + args.argi2 = volumeCount; + obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 89fa320..a406175 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -717,7 +717,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub { } else { final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid); if (vol != null && vol.type == VolumeInfo.TYPE_PRIVATE - && vol.state == VolumeInfo.STATE_MOUNTED) { + && vol.isMountedWritable()) { return new File(vol.path, "app"); } else { throw new FileNotFoundException("Failed to find volume for UUID " + volumeUuid); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 1c339f5..bd22524 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -49,12 +49,10 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; -import static android.content.pm.PackageManager.MOVE_EXTERNAL_MEDIA; import static android.content.pm.PackageManager.MOVE_FAILED_DOESNT_EXIST; import static android.content.pm.PackageManager.MOVE_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING; import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; -import static android.content.pm.PackageManager.MOVE_INTERNAL; import static android.content.pm.PackageParser.isApkFile; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; @@ -144,6 +142,7 @@ import android.os.Message; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SELinux; import android.os.ServiceManager; @@ -174,6 +173,7 @@ import android.util.PrintStreamPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseIntArray; import android.util.Xml; import android.view.Display; @@ -189,11 +189,14 @@ import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.PackageHelper; import com.android.internal.os.IParcelFileDescriptorFactory; +import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; +import com.android.internal.util.Preconditions; import com.android.server.EventLogTags; +import com.android.server.FgThread; import com.android.server.IntentResolver; import com.android.server.LocalServices; import com.android.server.ServiceThread; @@ -237,6 +240,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** @@ -504,6 +508,10 @@ public class PackageManagerService extends IPackageManager.Stub { final PackageInstallerService mInstallerService; private final PackageDexOptimizer mPackageDexOptimizer; + + private AtomicInteger mNextMoveId = new AtomicInteger(); + private final MoveCallbacks mMoveCallbacks; + // Cache of users who need badging. SparseBooleanArray mUserNeedsBadging = new SparseBooleanArray(); @@ -1698,6 +1706,7 @@ public class PackageManagerService extends IPackageManager.Stub { mInstaller = installer; mPackageDexOptimizer = new PackageDexOptimizer(this); + mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); getDefaultDisplayMetrics(context, mMetrics); @@ -14137,49 +14146,25 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public void movePackage(final String packageName, final IPackageMoveObserver observer, - final int flags) { + public int movePackage(final String packageName, final String volumeUuid) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); - final int installFlags; - if ((flags & MOVE_INTERNAL) != 0) { - installFlags = INSTALL_INTERNAL; - } else if ((flags & MOVE_EXTERNAL_MEDIA) != 0) { - installFlags = INSTALL_EXTERNAL; - } else { - throw new IllegalArgumentException("Unsupported move flags " + flags); - } - - try { - movePackageInternal(packageName, null, installFlags, false, observer); - } catch (PackageManagerException e) { - Slog.d(TAG, "Failed to move " + packageName, e); - try { - observer.packageMoved(packageName, e.error); - } catch (RemoteException ignored) { - } - } - } - - @Override - public void movePackageAndData(final String packageName, final String volumeUuid, - final IPackageMoveObserver observer) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); + final int moveId = mNextMoveId.getAndIncrement(); try { - movePackageInternal(packageName, volumeUuid, INSTALL_INTERNAL, true, observer); + movePackageInternal(packageName, volumeUuid, moveId); } catch (PackageManagerException e) { Slog.d(TAG, "Failed to move " + packageName, e); - try { - observer.packageMoved(packageName, e.error); - } catch (RemoteException ignored) { - } + mMoveCallbacks.notifyStatusChanged(moveId, PackageManager.MOVE_FAILED_INTERNAL_ERROR); } + return moveId; } - private void movePackageInternal(final String packageName, String volumeUuid, int installFlags, - boolean andData, final IPackageMoveObserver observer) throws PackageManagerException { + private void movePackageInternal(final String packageName, final String volumeUuid, + final int moveId) throws PackageManagerException { final UserHandle user = new UserHandle(UserHandle.getCallingUserId()); + final PackageManager pm = mContext.getPackageManager(); + final boolean currentAsec; final String currentVolumeUuid; final File codeFile; final String installerPackageName; @@ -14205,8 +14190,13 @@ public class PackageManagerService extends IPackageManager.Stub { // TODO: yell if already in desired location + mMoveCallbacks.notifyStarted(moveId, + String.valueOf(pm.getApplicationLabel(pkg.applicationInfo))); + pkg.mOperationPending = true; + currentAsec = pkg.applicationInfo.isForwardLocked() + || pkg.applicationInfo.isExternalAsec(); currentVolumeUuid = ps.volumeUuid; codeFile = new File(pkg.codePath); installerPackageName = ps.installerPackageName; @@ -14215,10 +14205,36 @@ public class PackageManagerService extends IPackageManager.Stub { seinfo = pkg.applicationInfo.seinfo; } - if (andData) { - Slog.d(TAG, "Moving " + packageName + " private data from " + currentVolumeUuid + " to " - + volumeUuid); + int installFlags; + final boolean moveData; + + if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { + installFlags = INSTALL_INTERNAL; + moveData = !currentAsec; + } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { + installFlags = INSTALL_EXTERNAL; + moveData = false; + } else { + final StorageManager storage = mContext.getSystemService(StorageManager.class); + final VolumeInfo volume = storage.findVolumeByUuid(volumeUuid); + if (volume == null || volume.getType() != VolumeInfo.TYPE_PRIVATE + || !volume.isMountedWritable()) { + throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR, + "Move location not mounted private volume"); + } + + Preconditions.checkState(!currentAsec); + + installFlags = INSTALL_INTERNAL; + moveData = true; + } + + Slog.d(TAG, "Moving " + packageName + " from " + currentVolumeUuid + " to " + volumeUuid); + mMoveCallbacks.notifyStatusChanged(moveId, 10, -1); + + if (moveData) { synchronized (mInstallLock) { + // TODO: split this into separate copy and delete operations if (mInstaller.moveUserDataDirs(currentVolumeUuid, volumeUuid, packageName, appId, seinfo) != 0) { synchronized (mPackages) { @@ -14234,6 +14250,8 @@ public class PackageManagerService extends IPackageManager.Stub { } } + mMoveCallbacks.notifyStatusChanged(moveId, 50); + final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() { @Override public void onUserActionRequired(Intent intent) throws RemoteException { @@ -14259,13 +14277,16 @@ public class PackageManagerService extends IPackageManager.Stub { final int status = PackageManager.installStatusToPublicStatus(returnCode); switch (status) { case PackageInstaller.STATUS_SUCCESS: - observer.packageMoved(packageName, PackageManager.MOVE_SUCCEEDED); + mMoveCallbacks.notifyStatusChanged(moveId, + PackageManager.MOVE_SUCCEEDED); break; case PackageInstaller.STATUS_FAILURE_STORAGE: - observer.packageMoved(packageName, PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE); + mMoveCallbacks.notifyStatusChanged(moveId, + PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE); break; default: - observer.packageMoved(packageName, PackageManager.MOVE_FAILED_INTERNAL_ERROR); + mMoveCallbacks.notifyStatusChanged(moveId, + PackageManager.MOVE_FAILED_INTERNAL_ERROR); break; } } @@ -14283,6 +14304,39 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override + public int movePrimaryStorage(String volumeUuid) throws RemoteException { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); + + final int moveId = mNextMoveId.getAndIncrement(); + + // TODO: ask mountservice to take down both, connect over to DCS to + // migrate, and then bring up new storage + + return moveId; + } + + @Override + public int getMoveStatus(int moveId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null); + return mMoveCallbacks.mLastStatus.get(moveId); + } + + @Override + public void registerMoveCallback(IPackageMoveObserver callback) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null); + mMoveCallbacks.register(callback); + } + + @Override + public void unregisterMoveCallback(IPackageMoveObserver callback) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS, null); + mMoveCallbacks.unregister(callback); + } + + @Override public boolean setInstallLocation(int loc) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS, null); @@ -14605,4 +14659,82 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + + private static class MoveCallbacks extends Handler { + private static final int MSG_STARTED = 1; + private static final int MSG_STATUS_CHANGED = 2; + + private final RemoteCallbackList<IPackageMoveObserver> + mCallbacks = new RemoteCallbackList<>(); + + private final SparseIntArray mLastStatus = new SparseIntArray(); + + public MoveCallbacks(Looper looper) { + super(looper); + } + + public void register(IPackageMoveObserver callback) { + mCallbacks.register(callback); + } + + public void unregister(IPackageMoveObserver callback) { + mCallbacks.unregister(callback); + } + + @Override + public void handleMessage(Message msg) { + final SomeArgs args = (SomeArgs) msg.obj; + final int n = mCallbacks.beginBroadcast(); + for (int i = 0; i < n; i++) { + final IPackageMoveObserver callback = mCallbacks.getBroadcastItem(i); + try { + invokeCallback(callback, msg.what, args); + } catch (RemoteException ignored) { + } + } + mCallbacks.finishBroadcast(); + args.recycle(); + } + + private void invokeCallback(IPackageMoveObserver callback, int what, SomeArgs args) + throws RemoteException { + switch (what) { + case MSG_STARTED: { + callback.onStarted(args.argi1, (String) args.arg2); + break; + } + case MSG_STATUS_CHANGED: { + callback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3); + break; + } + } + } + + private void notifyStarted(int moveId, String title) { + Slog.v(TAG, "Move " + moveId + " started with title " + title); + + final SomeArgs args = SomeArgs.obtain(); + args.argi1 = moveId; + args.arg2 = title; + obtainMessage(MSG_STARTED, args).sendToTarget(); + } + + 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.argi2 = status; + args.arg3 = estMillis; + obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget(); + + synchronized (mLastStatus) { + mLastStatus.put(moveId, status); + } + } + } } diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 276b713..9efea0d 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -16,7 +16,6 @@ package android.test.mock; -import android.annotation.NonNull; import android.app.PackageInstallObserver; import android.content.ComponentName; import android.content.Intent; @@ -29,7 +28,6 @@ import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; import android.content.pm.IPackageInstallObserver; -import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; @@ -46,11 +44,14 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.VerificationParams; import android.content.pm.VerifierDeviceIdentity; +import android.content.pm.PackageManager.MoveCallback; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Handler; +import android.os.ResultReceiver; import android.os.UserHandle; import android.os.storage.VolumeInfo; @@ -487,33 +488,62 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + @Override + public String getInstallerPackageName(String packageName) { + throw new UnsupportedOperationException(); + } + /** {@hide} */ @Override - public void movePackage(String packageName, IPackageMoveObserver observer, int flags) { + public int getMoveStatus(int moveId) { throw new UnsupportedOperationException(); } /** {@hide} */ @Override - public void movePackageAndData(String packageName, String volumeUuid, - IPackageMoveObserver observer) { + public void registerMoveCallback(MoveCallback callback, Handler handler) { throw new UnsupportedOperationException(); } /** {@hide} */ @Override - public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) { + public void unregisterMoveCallback(MoveCallback callback) { throw new UnsupportedOperationException(); } /** {@hide} */ @Override - public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) { + public int movePackage(String packageName, VolumeInfo vol) { throw new UnsupportedOperationException(); } + /** {@hide} */ @Override - public String getInstallerPackageName(String packageName) { + public VolumeInfo getPackageCurrentVolume(ApplicationInfo app) { + throw new UnsupportedOperationException(); + } + + /** {@hide} */ + @Override + public List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) { + throw new UnsupportedOperationException(); + } + + /** {@hide} */ + @Override + public int movePrimaryStorage(VolumeInfo vol) { + throw new UnsupportedOperationException(); + } + + /** {@hide} */ + @Override + public VolumeInfo getPrimaryStorageCurrentVolume() { + throw new UnsupportedOperationException(); + } + + /** {@hide} */ + @Override + public List<VolumeInfo> getPrimaryStorageCandidateVolumes() { throw new UnsupportedOperationException(); } |