Merge "PackageManager plumbing to support non-staged APEX installs" into sc-dev am: fe29823f79 am: 91f2ddb478

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14684990

Change-Id: I75f9f1dcc7f008a9bc9247c78042891d459be872
This commit is contained in:
Nikita Ioffe
2021-05-24 11:32:48 +00:00
committed by Automerger Merge Worker
5 changed files with 127 additions and 19 deletions

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}