Adding a non-blocking version of transfer.
Along with blocking utility method. Test: atest PackageManagerShellCommandTest Bug: b/136132412 Change-Id: I4ff8504c54c989e4d6a18d53d8b015d9481b4781
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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}.
|
||||
*
|
||||
* <p>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<Intent> intentFuture = new CompletableFuture<Intent>();
|
||||
try {
|
||||
mSession.transfer(packageName);
|
||||
IIntentSender localSender = new IIntentSender.Stub() {
|
||||
@Override
|
||||
public void send(int code, Intent intent, String resolvedType,
|
||||
IBinder whitelistToken,
|
||||
IIntentReceiver finishedReceiver, String requiredPermission,
|
||||
Bundle options) {
|
||||
intentFuture.complete(intent);
|
||||
}
|
||||
};
|
||||
transfer(packageName, new IntentSender(localSender));
|
||||
} catch (ParcelableException e) {
|
||||
e.maybeRethrow(PackageManager.NameNotFoundException.class);
|
||||
throw new RuntimeException(e);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
try {
|
||||
Intent intent = intentFuture.get();
|
||||
final int status = intent.getIntExtra(EXTRA_STATUS, Integer.MIN_VALUE);
|
||||
final String statusMessage = intent.getStringExtra(EXTRA_STATUS_MESSAGE);
|
||||
switch (status) {
|
||||
case STATUS_SUCCESS:
|
||||
break;
|
||||
case STATUS_FAILURE_NAME_NOT_FOUND:
|
||||
throw new PackageManager.NameNotFoundException(statusMessage);
|
||||
case STATUS_FAILURE_ILLEGAL_STATE:
|
||||
throw new IllegalStateException(statusMessage);
|
||||
case STATUS_FAILURE_SECURITY:
|
||||
throw new SecurityException(statusMessage);
|
||||
default:
|
||||
throw new RuntimeException(statusMessage);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
private static final int MSG_COMMIT = 1;
|
||||
private static final int MSG_ON_PACKAGE_INSTALLED = 2;
|
||||
private static final int MSG_SEAL = 3;
|
||||
private static final int MSG_TRANSFER = 4;
|
||||
|
||||
/** XML constants used for persisting a session */
|
||||
static final String TAG_SESSION = "session";
|
||||
@@ -332,6 +333,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
SomeArgs args;
|
||||
String packageName;
|
||||
IntentSender statusReceiver;
|
||||
switch (msg.what) {
|
||||
case MSG_SEAL:
|
||||
handleSeal((IntentSender) msg.obj);
|
||||
@@ -339,12 +343,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
case MSG_COMMIT:
|
||||
handleCommit();
|
||||
break;
|
||||
case MSG_TRANSFER:
|
||||
args = (SomeArgs) msg.obj;
|
||||
packageName = (String) args.arg1;
|
||||
statusReceiver = (IntentSender) args.arg2;
|
||||
args.recycle();
|
||||
|
||||
handleTransfer(statusReceiver, packageName);
|
||||
break;
|
||||
case MSG_ON_PACKAGE_INSTALLED:
|
||||
final SomeArgs args = (SomeArgs) msg.obj;
|
||||
final String packageName = (String) args.arg1;
|
||||
args = (SomeArgs) msg.obj;
|
||||
packageName = (String) args.arg1;
|
||||
final String message = (String) args.arg2;
|
||||
final Bundle extras = (Bundle) args.arg3;
|
||||
final IntentSender statusReceiver = (IntentSender) args.arg4;
|
||||
statusReceiver = (IntentSender) args.arg4;
|
||||
final int returnCode = args.argi1;
|
||||
args.recycle();
|
||||
|
||||
@@ -378,7 +390,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
* Checks if the permissions still need to be confirmed.
|
||||
*
|
||||
* <p>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<PackageInstallerSession> childSessions = getChildSessions();
|
||||
return newOwnerAppInfo.uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transfer(String packageName, IntentSender statusReceiver) {
|
||||
Preconditions.checkNotNull(statusReceiver);
|
||||
Preconditions.checkNotNull(packageName);
|
||||
|
||||
try {
|
||||
assertCanBeTransferredAndReturnNewOwner(packageName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new ParcelableException(e);
|
||||
}
|
||||
|
||||
synchronized (mLock) {
|
||||
assertCallerIsOwnerOrRootLocked();
|
||||
assertPreparedAndNotSealedLocked("transfer");
|
||||
}
|
||||
|
||||
try {
|
||||
sealAndValidateLocked(childSessions);
|
||||
} catch (PackageManagerException e) {
|
||||
throw new IllegalArgumentException("Package is not valid", e);
|
||||
final SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = packageName;
|
||||
args.arg2 = statusReceiver;
|
||||
|
||||
mHandler.obtainMessage(MSG_TRANSFER, args).sendToTarget();
|
||||
}
|
||||
|
||||
private void handleTransfer(IntentSender statusReceiver, String packageName) {
|
||||
List<PackageInstallerSession> childSessions = getChildSessions();
|
||||
|
||||
try {
|
||||
final int uid = assertCanBeTransferredAndReturnNewOwner(packageName);
|
||||
|
||||
synchronized (mLock) {
|
||||
assertPreparedAndNotSealedLocked("transfer");
|
||||
|
||||
try {
|
||||
sealAndValidateLocked(childSessions);
|
||||
} catch (PackageManagerException e) {
|
||||
throw new IllegalArgumentException("Package is not valid", e);
|
||||
}
|
||||
|
||||
if (!mPackageName.equals(mInstallSource.installerPackageName)) {
|
||||
throw new SecurityException(
|
||||
"Can only transfer sessions that update the original installer");
|
||||
}
|
||||
|
||||
mInstallerUid = uid;
|
||||
mInstallSource = InstallSource.create(packageName, null, packageName, false);
|
||||
}
|
||||
|
||||
if (!mPackageName.equals(mInstallSource.installerPackageName)) {
|
||||
throw new SecurityException("Can only transfer sessions that update the original "
|
||||
+ "installer");
|
||||
}
|
||||
|
||||
mInstallerUid = newOwnerAppInfo.uid;
|
||||
mInstallSource = InstallSource.create(packageName, null, packageName, false);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
onSessionTransferStatus(statusReceiver, packageName,
|
||||
PackageInstaller.STATUS_FAILURE_NAME_NOT_FOUND, e.getMessage());
|
||||
return;
|
||||
} catch (IllegalStateException e) {
|
||||
onSessionTransferStatus(statusReceiver, packageName,
|
||||
PackageInstaller.STATUS_FAILURE_ILLEGAL_STATE, e.getMessage());
|
||||
return;
|
||||
} catch (SecurityException e) {
|
||||
onSessionTransferStatus(statusReceiver, packageName,
|
||||
PackageInstaller.STATUS_FAILURE_SECURITY, e.getMessage());
|
||||
return;
|
||||
} catch (Throwable e) {
|
||||
onSessionTransferStatus(statusReceiver, packageName, PackageInstaller.STATUS_FAILURE,
|
||||
e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// Persist the fact that we've sealed ourselves to prevent
|
||||
// mutations of any hard links we create. We do this without holding
|
||||
// the session lock, since otherwise it's a lock inversion.
|
||||
mCallback.onSessionSealedBlocking(this);
|
||||
|
||||
// Report success.
|
||||
onSessionTransferStatus(statusReceiver, packageName, PackageInstaller.STATUS_SUCCESS, null);
|
||||
}
|
||||
|
||||
private void onSessionTransferStatus(IntentSender statusReceiver, String otherPackageName,
|
||||
int status, String statusMessage) {
|
||||
final String packageName;
|
||||
synchronized (mLock) {
|
||||
packageName = mPackageName;
|
||||
}
|
||||
|
||||
final Intent fillIn = new Intent();
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_PACKAGE_NAME, packageName);
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_OTHER_PACKAGE_NAME, otherPackageName);
|
||||
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_STATUS, status);
|
||||
if (!TextUtils.isEmpty(statusMessage)) {
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
|
||||
}
|
||||
|
||||
try {
|
||||
statusReceiver.sendIntent(mContext, 0, fillIn, null, null);
|
||||
} catch (IntentSender.SendIntentException ignored) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void handleCommit() {
|
||||
|
||||
Reference in New Issue
Block a user