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)
|
public abstract void reserveSpaceForCompressedApex(CompressedApexInfoList infoList)
|
||||||
throws RemoteException;
|
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.
|
* Dumps various state information to the provided {@link PrintWriter} object.
|
||||||
*
|
*
|
||||||
@@ -974,6 +979,22 @@ public abstract class ApexManager {
|
|||||||
waitForApexService().reserveSpaceForCompressedApex(infoList);
|
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
|
* Dump information about the packages contained in a particular cache
|
||||||
* @param packagesCache the cache to print information about.
|
* @param packagesCache the cache to print information about.
|
||||||
@@ -1240,6 +1261,11 @@ public abstract class ApexManager {
|
|||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void installPackage(String packagePath) {
|
||||||
|
throw new UnsupportedOperationException("APEX updates are not supported");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void dump(PrintWriter pw, String packageName) {
|
void dump(PrintWriter pw, String packageName) {
|
||||||
// No-op
|
// No-op
|
||||||
|
|||||||
@@ -635,13 +635,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
|
|||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"This device doesn't support the installation of APEX files");
|
"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) {
|
if (params.isMultiPackage) {
|
||||||
throw new IllegalArgumentException("A multi-session can't be set as APEX.");
|
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
|
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) {
|
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);
|
final File sessionStagingDir = Environment.getDataStagingDirectory(params.volumeUuid);
|
||||||
return new File(sessionStagingDir, "session_" + sessionId);
|
return new File(sessionStagingDir, "session_" + sessionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2246,12 +2246,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isApexSession()) {
|
|
||||||
destroyInternal();
|
|
||||||
dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
|
|
||||||
"APEX packages can only be installed using staged sessions.", null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
verify();
|
verify();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2276,6 +2270,13 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
childSessions = getChildSessionsLocked();
|
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 =
|
List<PackageManagerService.VerificationParams> verifyingChildSessions =
|
||||||
new ArrayList<>(childSessions.size());
|
new ArrayList<>(childSessions.size());
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
@@ -2320,8 +2321,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
|||||||
|
|
||||||
private void installNonStaged()
|
private void installNonStaged()
|
||||||
throws PackageManagerException {
|
throws PackageManagerException {
|
||||||
Preconditions.checkArgument(containsApkSession());
|
|
||||||
|
|
||||||
final PackageManagerService.InstallParams installingSession = makeInstallParams();
|
final PackageManagerService.InstallParams installingSession = makeInstallParams();
|
||||||
if (installingSession == null) {
|
if (installingSession == null) {
|
||||||
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
|
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.
|
// Do not try to install staged apex session. Parent session will have at least one apk
|
||||||
if (!isMultiPackage() && isApexSession()) {
|
// session.
|
||||||
|
if (!isMultiPackage() && isApexSession() && params.isStaged) {
|
||||||
sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
|
sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED,
|
||||||
"Apex package should have been installed by apexd", null);
|
"Apex package should have been installed by apexd", null);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -16428,25 +16428,95 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
private void processInstallRequestsAsync(boolean success,
|
private void processInstallRequestsAsync(boolean success,
|
||||||
List<InstallRequest> installRequests) {
|
List<InstallRequest> installRequests) {
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
if (success) {
|
List<InstallRequest> apexInstallRequests = new ArrayList<>();
|
||||||
|
List<InstallRequest> apkInstallRequests = new ArrayList<>();
|
||||||
for (InstallRequest request : installRequests) {
|
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 : apkInstallRequests) {
|
||||||
request.args.doPreInstall(request.installResult.returnCode);
|
request.args.doPreInstall(request.installResult.returnCode);
|
||||||
}
|
}
|
||||||
synchronized (mInstallLock) {
|
synchronized (mInstallLock) {
|
||||||
installPackagesTracedLI(installRequests);
|
installPackagesTracedLI(apkInstallRequests);
|
||||||
}
|
}
|
||||||
for (InstallRequest request : installRequests) {
|
for (InstallRequest request : apkInstallRequests) {
|
||||||
request.args.doPostInstall(
|
request.args.doPostInstall(
|
||||||
request.installResult.returnCode, request.installResult.uid);
|
request.installResult.returnCode, request.installResult.uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (InstallRequest request : installRequests) {
|
for (InstallRequest request : apkInstallRequests) {
|
||||||
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
|
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
|
||||||
new PostInstallData(request.args, request.installResult, null));
|
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(
|
private PackageInstalledInfo createPackageInstalledInfo(
|
||||||
int currentStatus) {
|
int currentStatus) {
|
||||||
PackageInstalledInfo res = new PackageInstalledInfo();
|
PackageInstalledInfo res = new PackageInstalledInfo();
|
||||||
@@ -17058,6 +17128,10 @@ public class PackageManagerService extends IPackageManager.Stub
|
|||||||
* on the install location.
|
* on the install location.
|
||||||
*/
|
*/
|
||||||
public void handleStartCopy() {
|
public void handleStartCopy() {
|
||||||
|
if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
|
||||||
|
mRet = INSTALL_SUCCEEDED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
|
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
|
||||||
mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
|
mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
|
||||||
|
|
||||||
|
|||||||
@@ -2689,6 +2689,7 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
|
|
||||||
String opt;
|
String opt;
|
||||||
boolean replaceExisting = true;
|
boolean replaceExisting = true;
|
||||||
|
boolean forceNonStaged = false;
|
||||||
while ((opt = getNextOption()) != null) {
|
while ((opt = getNextOption()) != null) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case "-r": // ignore
|
case "-r": // ignore
|
||||||
@@ -2783,6 +2784,9 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
sessionParams.setInstallAsApex();
|
sessionParams.setInstallAsApex();
|
||||||
sessionParams.setStaged();
|
sessionParams.setStaged();
|
||||||
break;
|
break;
|
||||||
|
case "--force-non-staged":
|
||||||
|
forceNonStaged = true;
|
||||||
|
break;
|
||||||
case "--multi-package":
|
case "--multi-package":
|
||||||
sessionParams.setMultiPackage();
|
sessionParams.setMultiPackage();
|
||||||
break;
|
break;
|
||||||
@@ -2818,6 +2822,9 @@ class PackageManagerShellCommand extends ShellCommand {
|
|||||||
if (replaceExisting) {
|
if (replaceExisting) {
|
||||||
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
|
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
|
||||||
}
|
}
|
||||||
|
if (forceNonStaged) {
|
||||||
|
sessionParams.isStaged = false;
|
||||||
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user