Merge "PackageManager plumbing to support non-staged APEX installs" into sc-dev am: fe29823f79
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14684990 Change-Id: I15f3a6350cf707ebc8fd30c303964a41654dc28e
This commit is contained in:
@@ -389,6 +389,11 @@ public abstract class ApexManager {
|
||||
public abstract void reserveSpaceForCompressedApex(CompressedApexInfoList infoList)
|
||||
throws RemoteException;
|
||||
|
||||
/**
|
||||
* Performs a non-staged install of an APEX package with given {@code packagePath}.
|
||||
*/
|
||||
abstract void installPackage(String packagePath) throws PackageManagerException;
|
||||
|
||||
/**
|
||||
* Dumps various state information to the provided {@link PrintWriter} object.
|
||||
*
|
||||
@@ -974,6 +979,22 @@ public abstract class ApexManager {
|
||||
waitForApexService().reserveSpaceForCompressedApex(infoList);
|
||||
}
|
||||
|
||||
@Override
|
||||
void installPackage(String packagePath) throws PackageManagerException {
|
||||
try {
|
||||
// TODO(b/187864524): do pre-install verification.
|
||||
waitForApexService().installAndActivatePackage(packagePath);
|
||||
// TODO(b/187864524): update mAllPackagesCache.
|
||||
} catch (RemoteException e) {
|
||||
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
|
||||
"apexservice not available");
|
||||
} catch (Exception e) {
|
||||
// TODO(b/187864524): is INSTALL_FAILED_INTERNAL_ERROR is the right error code here?
|
||||
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump information about the packages contained in a particular cache
|
||||
* @param packagesCache the cache to print information about.
|
||||
@@ -1240,6 +1261,11 @@ public abstract class ApexManager {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void installPackage(String packagePath) {
|
||||
throw new UnsupportedOperationException("APEX updates are not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
void dump(PrintWriter pw, String packageName) {
|
||||
// No-op
|
||||
|
||||
@@ -635,13 +635,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
||||
throw new IllegalArgumentException(
|
||||
"This device doesn't support the installation of APEX files");
|
||||
}
|
||||
if (!params.isStaged) {
|
||||
throw new IllegalArgumentException(
|
||||
"APEX files can only be installed as part of a staged session.");
|
||||
}
|
||||
if (params.isMultiPackage) {
|
||||
throw new IllegalArgumentException("A multi-session can't be set as APEX.");
|
||||
}
|
||||
if (!params.isStaged
|
||||
&& (params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Non-staged APEX session doesn't support INSTALL_ENABLE_ROLLBACK");
|
||||
}
|
||||
}
|
||||
|
||||
if ((params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0
|
||||
@@ -877,7 +878,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
||||
}
|
||||
|
||||
private File buildSessionDir(int sessionId, SessionParams params) {
|
||||
if (params.isStaged) {
|
||||
if (params.isStaged || (params.installFlags & PackageManager.INSTALL_APEX) != 0) {
|
||||
final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
|
||||
return new File(sessionStagingDir, "session_" + sessionId);
|
||||
}
|
||||
|
||||
@@ -2246,12 +2246,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isApexSession()) {
|
||||
destroyInternal();
|
||||
dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
|
||||
"APEX packages can only be installed using staged sessions.", null);
|
||||
return;
|
||||
}
|
||||
verify();
|
||||
}
|
||||
|
||||
@@ -2276,6 +2270,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
synchronized (mLock) {
|
||||
childSessions = getChildSessionsLocked();
|
||||
}
|
||||
// Spot check to reject a non-staged multi package install of APEXes and APKs.
|
||||
if (!params.isStaged && containsApkSession()
|
||||
&& sessionContains(s -> s.isApexSession())) {
|
||||
throw new PackageManagerException(
|
||||
PackageManager.INSTALL_FAILED_SESSION_INVALID,
|
||||
"Non-staged multi package install of APEX and APK packages is not supported");
|
||||
}
|
||||
List<PackageManagerService.VerificationParams> verifyingChildSessions =
|
||||
new ArrayList<>(childSessions.size());
|
||||
boolean success = true;
|
||||
@@ -2320,8 +2321,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
|
||||
private void installNonStaged()
|
||||
throws PackageManagerException {
|
||||
Preconditions.checkArgument(containsApkSession());
|
||||
|
||||
final PackageManagerService.InstallParams installingSession = makeInstallParams();
|
||||
if (installingSession == null) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
|
||||
@@ -2609,8 +2608,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
// Do not try to install apex session. Parent session will have at least one apk session.
|
||||
if (!isMultiPackage() && isApexSession()) {
|
||||
// Do not try to install staged apex session. Parent session will have at least one apk
|
||||
// session.
|
||||
if (!isMultiPackage() && isApexSession() && params.isStaged) {
|
||||
sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
|
||||
"Apex package should have been installed by apexd", null);
|
||||
return null;
|
||||
|
||||
@@ -16428,25 +16428,95 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
private void processInstallRequestsAsync(boolean success,
|
||||
List<InstallRequest> installRequests) {
|
||||
mHandler.post(() -> {
|
||||
List<InstallRequest> apexInstallRequests = new ArrayList<>();
|
||||
List<InstallRequest> apkInstallRequests = new ArrayList<>();
|
||||
for (InstallRequest request : installRequests) {
|
||||
if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {
|
||||
apexInstallRequests.add(request);
|
||||
} else {
|
||||
apkInstallRequests.add(request);
|
||||
}
|
||||
}
|
||||
// Note: supporting multi package install of both APEXes and APKs might requir some
|
||||
// thinking to ensure atomicity of the install.
|
||||
if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
|
||||
// This should've been caught at the validation step, but for some reason wasn't.
|
||||
throw new IllegalStateException(
|
||||
"Attempted to do a multi package install of both APEXes and APKs");
|
||||
}
|
||||
if (!apexInstallRequests.isEmpty()) {
|
||||
if (success) {
|
||||
// Since installApexPackages requires talking to external service (apexd), we
|
||||
// schedule to run it async. Once it finishes, it will resume the install.
|
||||
Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
|
||||
"installApexPackages");
|
||||
t.start();
|
||||
} else {
|
||||
// Non-staged APEX installation failed somewhere before
|
||||
// processInstallRequestAsync. In that case just notify the observer about the
|
||||
// failure.
|
||||
InstallRequest request = apexInstallRequests.get(0);
|
||||
notifyInstallObserver(request.installResult, request.args.observer);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (success) {
|
||||
for (InstallRequest request : installRequests) {
|
||||
for (InstallRequest request : apkInstallRequests) {
|
||||
request.args.doPreInstall(request.installResult.returnCode);
|
||||
}
|
||||
synchronized (mInstallLock) {
|
||||
installPackagesTracedLI(installRequests);
|
||||
installPackagesTracedLI(apkInstallRequests);
|
||||
}
|
||||
for (InstallRequest request : installRequests) {
|
||||
for (InstallRequest request : apkInstallRequests) {
|
||||
request.args.doPostInstall(
|
||||
request.installResult.returnCode, request.installResult.uid);
|
||||
}
|
||||
}
|
||||
for (InstallRequest request : installRequests) {
|
||||
for (InstallRequest request : apkInstallRequests) {
|
||||
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
|
||||
new PostInstallData(request.args, request.installResult, null));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void installApexPackagesTraced(List<InstallRequest> requests) {
|
||||
try {
|
||||
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installApexPackages");
|
||||
installApexPackages(requests);
|
||||
} finally {
|
||||
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
||||
}
|
||||
}
|
||||
|
||||
private void installApexPackages(List<InstallRequest> requests) {
|
||||
if (requests.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (requests.size() != 1) {
|
||||
throw new IllegalStateException(
|
||||
"Only a non-staged install of a single APEX is supported");
|
||||
}
|
||||
InstallRequest request = requests.get(0);
|
||||
try {
|
||||
// Should directory scanning logic be moved to ApexManager for better test coverage?
|
||||
final File dir = request.args.origin.resolvedFile;
|
||||
final String[] apexes = dir.list();
|
||||
if (apexes == null) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
|
||||
dir.getAbsolutePath() + " is not a directory");
|
||||
}
|
||||
if (apexes.length != 1) {
|
||||
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
|
||||
"Expected exactly one .apex file under " + dir.getAbsolutePath()
|
||||
+ " got: " + apexes.length);
|
||||
}
|
||||
mApexManager.installPackage(dir.getAbsolutePath() + "/" + apexes[0]);
|
||||
} catch (PackageManagerException e) {
|
||||
request.installResult.setError("APEX installation failed", e);
|
||||
}
|
||||
notifyInstallObserver(request.installResult, request.args.observer);
|
||||
}
|
||||
|
||||
private PackageInstalledInfo createPackageInstalledInfo(
|
||||
int currentStatus) {
|
||||
PackageInstalledInfo res = new PackageInstalledInfo();
|
||||
@@ -17058,6 +17128,10 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
* on the install location.
|
||||
*/
|
||||
public void handleStartCopy() {
|
||||
if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
|
||||
mRet = INSTALL_SUCCEEDED;
|
||||
return;
|
||||
}
|
||||
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
|
||||
mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
|
||||
|
||||
|
||||
@@ -2689,6 +2689,7 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
|
||||
String opt;
|
||||
boolean replaceExisting = true;
|
||||
boolean forceNonStaged = false;
|
||||
while ((opt = getNextOption()) != null) {
|
||||
switch (opt) {
|
||||
case "-r": // ignore
|
||||
@@ -2783,6 +2784,9 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
sessionParams.setInstallAsApex();
|
||||
sessionParams.setStaged();
|
||||
break;
|
||||
case "--force-non-staged":
|
||||
forceNonStaged = true;
|
||||
break;
|
||||
case "--multi-package":
|
||||
sessionParams.setMultiPackage();
|
||||
break;
|
||||
@@ -2818,6 +2822,9 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
if (replaceExisting) {
|
||||
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
|
||||
}
|
||||
if (forceNonStaged) {
|
||||
sessionParams.isStaged = false;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user