Loading services/core/java/com/android/server/pm/ApexManager.java +26 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading @@ -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. Loading Loading @@ -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 Loading services/core/java/com/android/server/pm/PackageInstallerService.java +6 −5 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading services/core/java/com/android/server/pm/PackageInstallerSession.java +10 −10 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading services/core/java/com/android/server/pm/PackageManagerService.java +78 −4 Original line number Diff line number Diff line Loading @@ -16428,25 +16428,95 @@ public class PackageManagerService extends IPackageManager.Stub private void processInstallRequestsAsync(boolean success, List<InstallRequest> installRequests) { mHandler.post(() -> { if (success) { 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 : 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(); Loading Loading @@ -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); services/core/java/com/android/server/pm/PackageManagerShellCommand.java +7 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -2818,6 +2822,9 @@ class PackageManagerShellCommand extends ShellCommand { if (replaceExisting) { sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; } if (forceNonStaged) { sessionParams.isStaged = false; } return params; } Loading Loading
services/core/java/com/android/server/pm/ApexManager.java +26 −0 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading @@ -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. Loading Loading @@ -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 Loading
services/core/java/com/android/server/pm/PackageInstallerService.java +6 −5 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); } Loading
services/core/java/com/android/server/pm/PackageInstallerSession.java +10 −10 Original line number Diff line number Diff line Loading @@ -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(); } Loading @@ -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; Loading Loading @@ -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, Loading Loading @@ -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; Loading
services/core/java/com/android/server/pm/PackageManagerService.java +78 −4 Original line number Diff line number Diff line Loading @@ -16428,25 +16428,95 @@ public class PackageManagerService extends IPackageManager.Stub private void processInstallRequestsAsync(boolean success, List<InstallRequest> installRequests) { mHandler.post(() -> { if (success) { 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 : 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(); Loading Loading @@ -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);
services/core/java/com/android/server/pm/PackageManagerShellCommand.java +7 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -2818,6 +2822,9 @@ class PackageManagerShellCommand extends ShellCommand { if (replaceExisting) { sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; } if (forceNonStaged) { sessionParams.isStaged = false; } return params; } Loading