Install constraints API
* commitSessionAfterInstallConstraintsAreMet Bug: 235306967 Test: atest CtsStagedInstallHostTestCases Change-Id: I11de4493eedf075a1957d2dacfffdb413b6cfa02
This commit is contained in:
@@ -11845,6 +11845,7 @@ package android.content.pm {
|
||||
public class PackageInstaller {
|
||||
method public void abandonSession(int);
|
||||
method public void checkInstallConstraints(@NonNull java.util.List<java.lang.String>, @NonNull android.content.pm.PackageInstaller.InstallConstraints, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.content.pm.PackageInstaller.InstallConstraintsResult>);
|
||||
method public void commitSessionAfterInstallConstraintsAreMet(int, @NonNull android.content.IntentSender, @NonNull android.content.pm.PackageInstaller.InstallConstraints, long);
|
||||
method public int createSession(@NonNull android.content.pm.PackageInstaller.SessionParams) throws java.io.IOException;
|
||||
method @Deprecated @Nullable public android.content.pm.PackageInstaller.SessionInfo getActiveStagedSession();
|
||||
method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getActiveStagedSessions();
|
||||
@@ -11889,6 +11890,7 @@ package android.content.pm {
|
||||
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_STORAGE = 6; // 0x6
|
||||
field public static final int STATUS_FAILURE_TIMEOUT = 8; // 0x8
|
||||
field public static final int STATUS_PENDING_USER_ACTION = -1; // 0xffffffff
|
||||
field public static final int STATUS_SUCCESS = 0; // 0x0
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ interface IPackageInstallerSession {
|
||||
void commit(in IntentSender statusReceiver, boolean forTransferred);
|
||||
void transfer(in String packageName);
|
||||
void abandon();
|
||||
void seal();
|
||||
List<String> fetchPackageNames();
|
||||
|
||||
DataLoaderParamsParcel getDataLoaderParams();
|
||||
void addFile(int location, String name, long lengthBytes, in byte[] metadata, in byte[] signature);
|
||||
|
||||
@@ -44,8 +44,11 @@ import android.annotation.SuppressLint;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.AppGlobals;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.IIntentSender;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentSender;
|
||||
import android.content.pm.PackageManager.DeleteFlags;
|
||||
@@ -59,9 +62,11 @@ import android.graphics.Bitmap;
|
||||
import android.icu.util.ULocale;
|
||||
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;
|
||||
@@ -226,8 +231,8 @@ 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_STORAGE}.
|
||||
* {@link #STATUS_FAILURE_INCOMPATIBLE}, {@link #STATUS_FAILURE_INVALID},
|
||||
* {@link #STATUS_FAILURE_STORAGE}, or {@link #STATUS_FAILURE_TIMEOUT}.
|
||||
* <p>
|
||||
* More information about a status may be available through additional
|
||||
* extras; see the individual status documentation for details.
|
||||
@@ -440,6 +445,13 @@ public class PackageInstaller {
|
||||
*/
|
||||
public static final int STATUS_FAILURE_INCOMPATIBLE = 7;
|
||||
|
||||
/**
|
||||
* The operation failed because it didn't complete within the specified timeout.
|
||||
*
|
||||
* @see #EXTRA_STATUS_MESSAGE
|
||||
*/
|
||||
public static final int STATUS_FAILURE_TIMEOUT = 8;
|
||||
|
||||
/**
|
||||
* Default value, non-streaming installation session.
|
||||
*
|
||||
@@ -975,6 +987,61 @@ public class PackageInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the session when all constraints are satisfied. This is a convenient method to
|
||||
* combine {@link #waitForInstallConstraints(List, InstallConstraints, IntentSender, long)}
|
||||
* and {@link Session#commit(IntentSender)}.
|
||||
* <p>
|
||||
* Once this method is called, the session is sealed and no additional mutations
|
||||
* may be performed on the session. In the case of timeout, you may commit the
|
||||
* session again using this method or {@link Session#commit(IntentSender)} for retries.
|
||||
*
|
||||
* @param statusReceiver Called when the state of the session changes. Intents
|
||||
* sent to this receiver contain {@link #EXTRA_STATUS}.
|
||||
* Refer to the individual status codes on how to handle them.
|
||||
* @param constraints The requirements to satisfy before committing the session.
|
||||
* @param timeoutMillis The maximum time to wait, in milliseconds until the
|
||||
* constraints are satisfied. The caller will be notified via
|
||||
* {@code statusReceiver} if timeout happens before commit.
|
||||
*/
|
||||
public void commitSessionAfterInstallConstraintsAreMet(int sessionId,
|
||||
@NonNull IntentSender statusReceiver, @NonNull InstallConstraints constraints,
|
||||
@DurationMillisLong long timeoutMillis) {
|
||||
try {
|
||||
var session = mInstaller.openSession(sessionId);
|
||||
session.seal();
|
||||
var packageNames = session.fetchPackageNames();
|
||||
var intentSender = new IntentSender((IIntentSender) new IIntentSender.Stub() {
|
||||
@Override
|
||||
public void send(int code, Intent intent, String resolvedType,
|
||||
IBinder allowlistToken, IIntentReceiver finishedReceiver,
|
||||
String requiredPermission, Bundle options) {
|
||||
var result = intent.getParcelableExtra(
|
||||
PackageInstaller.EXTRA_INSTALL_CONSTRAINTS_RESULT,
|
||||
InstallConstraintsResult.class);
|
||||
try {
|
||||
if (result.isAllConstraintsSatisfied()) {
|
||||
session.commit(statusReceiver, false);
|
||||
} else {
|
||||
// timeout
|
||||
final Intent fillIn = new Intent();
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_STATUS, STATUS_FAILURE_TIMEOUT);
|
||||
fillIn.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE,
|
||||
"Install constraints not satisfied within timeout");
|
||||
statusReceiver.sendIntent(
|
||||
ActivityThread.currentApplication(), 0, fillIn, null, null);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
});
|
||||
waitForInstallConstraints(packageNames, constraints, intentSender, timeoutMillis);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Events for observing session lifecycle.
|
||||
* <p>
|
||||
|
||||
@@ -1796,6 +1796,60 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
dispatchSessionSealed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seal() {
|
||||
assertNotChild("seal");
|
||||
assertCallerIsOwnerOrRoot();
|
||||
try {
|
||||
sealInternal();
|
||||
for (var child : getChildSessions()) {
|
||||
child.sealInternal();
|
||||
}
|
||||
} catch (PackageManagerException e) {
|
||||
throw new IllegalStateException("Package is not valid", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sealInternal() throws PackageManagerException {
|
||||
synchronized (mLock) {
|
||||
sealLocked();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> fetchPackageNames() {
|
||||
assertNotChild("fetchPackageNames");
|
||||
assertCallerIsOwnerOrRoot();
|
||||
var sessions = getSelfOrChildSessions();
|
||||
var result = new ArrayList<String>(sessions.size());
|
||||
for (var s : sessions) {
|
||||
result.add(s.fetchPackageName());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String fetchPackageName() {
|
||||
assertSealed("fetchPackageName");
|
||||
synchronized (mLock) {
|
||||
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
|
||||
final List<File> addedFiles = getAddedApksLocked();
|
||||
for (File addedFile : addedFiles) {
|
||||
final ParseResult<ApkLite> result =
|
||||
ApkLiteParseUtils.parseApkLite(input.reset(), addedFile, 0);
|
||||
if (result.isError()) {
|
||||
throw new IllegalStateException(
|
||||
"Can't parse package for session=" + sessionId, result.getException());
|
||||
}
|
||||
final ApkLite apk = result.getResult();
|
||||
var packageName = apk.getPackageName();
|
||||
if (packageName != null) {
|
||||
return packageName;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Can't fetch package name for session=" + sessionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks off the install flow. The first step is to persist 'sealed' flags
|
||||
* to prevent mutations of hard links created later.
|
||||
@@ -2049,6 +2103,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private List<PackageInstallerSession> getSelfOrChildSessions() {
|
||||
return isMultiPackage() ? getChildSessions() : Collections.singletonList(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seal the session to prevent further modification.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user