diff --git a/api/current.txt b/api/current.txt index b4be9e19da103..c1d3220471597 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11556,8 +11556,11 @@ package android.content.pm { field public static final int STATUS_FAILURE_ABORTED = 3; // 0x3 field public static final int STATUS_FAILURE_BLOCKED = 2; // 0x2 field public static final int STATUS_FAILURE_CONFLICT = 5; // 0x5 + field public static final int STATUS_FAILURE_ILLEGAL_STATE = 9; // 0x9 field public static final int STATUS_FAILURE_INCOMPATIBLE = 7; // 0x7 field public static final int STATUS_FAILURE_INVALID = 4; // 0x4 + field public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8; // 0x8 + field public static final int STATUS_FAILURE_SECURITY = 10; // 0xa field public static final int STATUS_FAILURE_STORAGE = 6; // 0x6 field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff field public static final int STATUS_SUCCESS = 0; // 0x0 @@ -11579,6 +11582,7 @@ package android.content.pm { method public void removeChildSessionId(int); method public void removeSplit(@NonNull String) throws java.io.IOException; method public void setStagingProgress(float); + method public void transfer(@NonNull String, @NonNull android.content.IntentSender) throws android.content.pm.PackageManager.NameNotFoundException; method public void transfer(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException; } diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl index 04e15c7957bd0..b74061793f9bb 100644 --- a/core/java/android/content/pm/IPackageInstallerSession.aidl +++ b/core/java/android/content/pm/IPackageInstallerSession.aidl @@ -36,7 +36,7 @@ interface IPackageInstallerSession { void close(); void commit(in IntentSender statusReceiver, boolean forTransferred); - void transfer(in String packageName); + void transfer(in String packageName, in IntentSender statusReceiver); void abandon(); boolean isMultiPackage(); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 0c529798622e8..4ab6f3cd92b4b 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -29,6 +29,8 @@ import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityManager; import android.app.AppGlobals; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.Intent; import android.content.IntentSender; import android.content.pm.PackageManager.DeleteFlags; @@ -36,9 +38,11 @@ import android.content.pm.PackageManager.InstallReason; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; +import android.os.Bundle; import android.os.FileBridge; import android.os.Handler; import android.os.HandlerExecutor; +import android.os.IBinder; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -67,6 +71,8 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.stream.Collectors; @@ -176,7 +182,7 @@ public class PackageInstaller { * {@link #STATUS_PENDING_USER_ACTION}, {@link #STATUS_SUCCESS}, * {@link #STATUS_FAILURE}, {@link #STATUS_FAILURE_ABORTED}, * {@link #STATUS_FAILURE_BLOCKED}, {@link #STATUS_FAILURE_CONFLICT}, - * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, or + * {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID}, * {@link #STATUS_FAILURE_STORAGE}. *
* More information about a status may be available through additional @@ -316,6 +322,34 @@ public class PackageInstaller { */ public static final int STATUS_FAILURE_INCOMPATIBLE = 7; + /** + * The transfer failed because a target package can't be found. For example + * transferring a session to a non-existing package. + *
+ * The result may also contain {@link #EXTRA_OTHER_PACKAGE_NAME} with the + * missing package. + * + * @see #EXTRA_STATUS_MESSAGE + * @see #EXTRA_OTHER_PACKAGE_NAME + */ + public static final int STATUS_FAILURE_NAME_NOT_FOUND = 8; + + /** + * The transfer failed because a session is in invalid state. For example + * transferring an already committed session. + * + * @see #EXTRA_STATUS_MESSAGE + */ + public static final int STATUS_FAILURE_ILLEGAL_STATE = 9; + + /** + * The transfer failed for security reasons. For example transferring + * to a package which does not have INSTALL_PACKAGES permission. + * + * @see #EXTRA_STATUS_MESSAGE + */ + public static final int STATUS_FAILURE_SECURITY = 10; + private final IPackageInstaller mInstaller; private final int mUserId; private final String mInstallerPackageName; @@ -1052,7 +1086,8 @@ public class PackageInstaller { } /** - * Attempt to commit a session that has been {@link #transfer(String) transferred}. + * Attempt to commit a session that has been {@link #transfer(String, IntentSender) + * transferred}. * *
If the device reboots before the session has been finalized, you may commit the
* session again.
@@ -1093,6 +1128,38 @@ public class PackageInstaller {
*
* @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
* permission.
+ * @param statusReceiver Called when the state of the session changes. Intents sent to this
+ * receiver contain {@link #EXTRA_STATUS}. Refer to the individual
+ * transfer status codes on how to handle them.
+ *
+ * @throws PackageManager.NameNotFoundException if the new owner could not be found.
+ * @throws SecurityException if called after the session has been committed or abandoned.
+ * @throws SecurityException if the session does not update the original installer
+ * @throws SecurityException if streams opened through
+ * {@link #openWrite(String, long, long) are still open.
+ */
+ public void transfer(@NonNull String packageName, @NonNull IntentSender statusReceiver)
+ throws PackageManager.NameNotFoundException {
+ Preconditions.checkNotNull(statusReceiver);
+ Preconditions.checkNotNull(packageName);
+
+ try {
+ mSession.transfer(packageName, statusReceiver);
+ } catch (ParcelableException e) {
+ e.maybeRethrow(PackageManager.NameNotFoundException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Transfer the session to a new owner.
+ * This is a convenience blocking wrapper around {@link #transfer(String, IntentSender)}.
+ * Converts all statuses into exceptions.
+ *
+ * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
+ * permission.
*
* @throws PackageManager.NameNotFoundException if the new owner could not be found.
* @throws SecurityException if called after the session has been committed or abandoned.
@@ -1104,13 +1171,43 @@ public class PackageInstaller {
throws PackageManager.NameNotFoundException {
Preconditions.checkNotNull(packageName);
+ CompletableFuture This is dependant on the identity of the installer, hence this cannot be cached if the
- * installer might still {@link #transfer(String) change}.
+ * installer might still {@link #transfer(String, IntentSender) change}.
*
* @return {@code true} iff we need to ask to confirm the permissions?
*/
@@ -1183,13 +1195,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
- @Override
- public void transfer(String packageName) {
- Preconditions.checkNotNull(packageName);
-
+ private int assertCanBeTransferredAndReturnNewOwner(String packageName)
+ throws PackageManager.NameNotFoundException {
ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
if (newOwnerAppInfo == null) {
- throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
+ throw new PackageManager.NameNotFoundException(packageName);
}
if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
@@ -1204,31 +1214,104 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
throw new SecurityException("Can only transfer sessions that use public options");
}
- List