Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 494d8a3e authored by Nikita Ioffe's avatar Nikita Ioffe Committed by Automerger Merge Worker
Browse files

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

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

Change-Id: I50c56cf6876065e5e070c153287cf1bff10fe221
parents af2a792b fe29823f
Loading
Loading
Loading
Loading
+26 −0
Original line number Diff line number Diff line
@@ -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
+6 −5
Original line number Diff line number Diff line
@@ -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);
        }
+10 −10
Original line number Diff line number Diff line
@@ -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;
+78 −4
Original line number Diff line number Diff line
@@ -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();
@@ -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);
+7 −0
Original line number Diff line number Diff line
@@ -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;
    }