Merge "Package and storage movement callbacks."
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.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 @@
}
}
- 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 @@
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.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 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 @@
@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 @@
}
@Override
- public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
+ public String getInstallerPackageName(String packageName) {
try {
- mPM.movePackage(packageName, observer, flags);
+ return mPM.getInstallerPackageName(packageName);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return null;
+ }
+
+ @Override
+ public int getMoveStatus(int moveId) {
+ try {
+ return mPM.getMoveStatus(moveId);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
@Override
- public void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer) {
- try {
- mPM.movePackageAndData(packageName, volumeUuid, observer);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
+ 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 @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
- final StorageManager storage = mContext.getSystemService(StorageManager.class);
- if (app.isInternal()) {
- return Preconditions.checkNotNull(
- 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;
+ 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();
}
}
- throw new IllegalStateException("Failed to find primary public volume");
- } else {
- return Preconditions.checkNotNull(storage.findVolumeByUuid(app.volumeUuid));
}
}
@Override
- public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
+ public int movePackage(String packageName, VolumeInfo vol) {
+ try {
+ 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 @Nullable VolumeInfo getPackageCurrentVolume(ApplicationInfo app) {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
+ if (app.isInternal()) {
+ return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
+ } else if (app.isExternalAsec()) {
+ return storage.getPrimaryPhysicalVolume();
+ } else {
+ return storage.findVolumeByUuid(app.volumeUuid);
+ }
+ }
+
+ @Override
+ 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 @@
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 @@
}
@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 @@
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.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 @@
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 @@
* @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.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 @@
*
* @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 @@
* been installed on external media.
* @hide
*/
+ @Deprecated
public static final int MOVE_INTERNAL = 0x00000001;
/**
@@ -948,8 +951,12 @@
* 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 @@
* @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 abstract @Nullable VolumeInfo getApplicationCurrentVolume(ApplicationInfo app);
+ 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 @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app);
+ public abstract int getMoveStatus(int moveId);
+
+ /** {@hide} */
+ 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 @@
* @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 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 @@
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 @@
_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 @@
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 @@
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 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 @@
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 @@
}
@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 @@
}
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 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 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 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 @@
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 @@
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 @@
}
@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 @@
/** {@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 @@
}
/** {@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 @@
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 @@
*/
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 @@
}
}
- 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 @@
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 @@
/** 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 @@
}
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 @@
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 @@
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 @@
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 @@
}
@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 @@
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 @@
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 @@
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 @@
} 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_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.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.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.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.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 @@
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 @@
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(this);
+ mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
getDefaultDisplayMetrics(context, mMetrics);
@@ -14137,49 +14146,25 @@
}
@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);
- }
-
+ final int moveId = mNextMoveId.getAndIncrement();
try {
- movePackageInternal(packageName, null, installFlags, false, 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;
}
- @Override
- public void movePackageAndData(final String packageName, final String volumeUuid,
- final IPackageMoveObserver observer) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null);
- try {
- movePackageInternal(packageName, volumeUuid, INSTALL_INTERNAL, true, observer);
- } catch (PackageManagerException e) {
- Slog.d(TAG, "Failed to move " + packageName, e);
- try {
- observer.packageMoved(packageName, e.error);
- } catch (RemoteException ignored) {
- }
- }
- }
-
- 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 @@
// 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 @@
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 @@
}
}
+ mMoveCallbacks.notifyStatusChanged(moveId, 50);
+
final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() {
@Override
public void onUserActionRequired(Intent intent) throws RemoteException {
@@ -14259,13 +14277,16 @@
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 @@
}
@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 @@
}
}
}
+
+ 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.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.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,36 +488,65 @@
throw new UnsupportedOperationException();
}
- /** {@hide} */
- @Override
- public void movePackage(String packageName, IPackageMoveObserver observer, int flags) {
- throw new UnsupportedOperationException();
- }
-
- /** {@hide} */
- @Override
- public void movePackageAndData(String packageName, String volumeUuid,
- IPackageMoveObserver observer) {
- throw new UnsupportedOperationException();
- }
-
- /** {@hide} */
- @Override
- public @NonNull VolumeInfo getApplicationCurrentVolume(ApplicationInfo app) {
- throw new UnsupportedOperationException();
- }
-
- /** {@hide} */
- @Override
- public @NonNull List<VolumeInfo> getApplicationCandidateVolumes(ApplicationInfo app) {
- throw new UnsupportedOperationException();
- }
-
@Override
public String getInstallerPackageName(String packageName) {
throw new UnsupportedOperationException();
}
+ /** {@hide} */
+ @Override
+ public int getMoveStatus(int moveId) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public void registerMoveCallback(MoveCallback callback, Handler handler) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public void unregisterMoveCallback(MoveCallback callback) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ public int movePackage(String packageName, VolumeInfo vol) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** {@hide} */
+ @Override
+ 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();
+ }
+
/**
* @hide - to match hiding in superclass
*/