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

Commit d8bf22e8 authored by Dario Freni's avatar Dario Freni
Browse files

Initial support for installing APEX via adb.

Test: adb install package-signed.apex (succeeds)
adb install package-unsigned.apex (fails)
Change-Id: I3ac7971ce6923511a7d574291fe9002c5d55fa1b
parent d0e5bc8d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -692,6 +692,7 @@ java_defaults {
    ],

    static_libs: [
        "apex_aidl_interface-java",
        "framework-protos",
        "mediaplayer2-protos",
        "android.hidl.base-V1.0-java",
+8 −0
Original line number Diff line number Diff line
@@ -855,6 +855,14 @@ public abstract class PackageManager {
     */
    public static final int INSTALL_VIRTUAL_PRELOAD = 0x00010000;

    /**
     * Flag parameter for {@link #installPackage} to indicate that this package
     * is an APEX package
     *
     * @hide
     */
    public static final int INSTALL_APEX = 0x00020000;

    /** @hide */
    @IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
            DONT_KILL_APP
+2 −3
Original line number Diff line number Diff line
@@ -58,7 +58,6 @@ import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -646,8 +645,8 @@ public class PackageInstallerService extends IPackageInstaller.Stub {
        }

        try {
            Os.mkdir(stageDir.getAbsolutePath(), 0755);
            Os.chmod(stageDir.getAbsolutePath(), 0755);
            Os.mkdir(stageDir.getAbsolutePath(), 0775);
            Os.chmod(stageDir.getAbsolutePath(), 0775);
        } catch (ErrnoException e) {
            // This purposefully throws if directory already exists
            throw new IOException("Failed to prepare session dir: " + stageDir, e);
+87 −6
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import static com.android.server.pm.PackageInstallerService.prepareStageDir;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.apex.IApexService;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -75,6 +76,7 @@ import android.os.ParcelableException;
import android.os.Process;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.storage.StorageManager;
@@ -838,12 +840,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        resolveStageDirLocked();

        mSealed = true;

        try {
            if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
                validateApexInstallLocked(pkgInfo);
            } else {
                // Verify that stage looks sane with respect to existing application.
                // This currently only ensures packageName, versionCode, and certificate
                // consistency.
        try {
            validateInstallLocked(pkgInfo);
                validateApkInstallLocked(pkgInfo);
            }
        } catch (PackageManagerException e) {
            throw e;
        } catch (Throwable e) {
@@ -926,6 +931,31 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
        Preconditions.checkNotNull(mSigningDetails);
        Preconditions.checkNotNull(mResolvedBaseFile);

        if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
            commitApexLocked();
        } else {
            commitApkLocked();
        }
    }

    @GuardedBy("mLock")
    private void commitApexLocked() throws PackageManagerException {
        try {
            IApexService apex = IApexService.Stub.asInterface(
                    ServiceManager.getService("apexservice"));
            apex.installPackage(mResolvedBaseFile.toString());
        } catch (Throwable e) {
            // Convert all exceptions into package manager exceptions as only those are handled
            // in the code above
            throw new PackageManagerException(e);
        } finally {
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "APEX installed", null);
        }
    }

    @GuardedBy("mLock")
    private void commitApkLocked() throws PackageManagerException {
        if (needToAskForPermissionsLocked()) {
            // User needs to confirm installation; give installer an intent they can use to involve
            // user.
@@ -1047,6 +1077,57 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                (params.installFlags & PackageManager.DONT_KILL_APP) != 0;
    }

    @GuardedBy("mLock")
    private void validateApexInstallLocked(@Nullable PackageInfo pkgInfo)
            throws PackageManagerException {
        mResolvedStagedFiles.clear();
        mResolvedInheritedFiles.clear();

        try {
            resolveStageDirLocked();
        } catch (IOException e) {
            throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
                "Failed to resolve stage location", e);
        }

        final File[] addedFiles = mResolvedStageDir.listFiles(sAddedFilter);
        if (ArrayUtils.isEmpty(addedFiles)) {
            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "No packages staged");
        }

        if (addedFiles.length > 1) {
            throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                "Only one APEX file at a time might be installed");
        }
        File addedFile = addedFiles[0];
        final ApkLite apk;
        try {
            apk = PackageParser.parseApkLite(
                addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
        } catch (PackageParserException e) {
            throw PackageManagerException.from(e);
        }

        mPackageName = apk.packageName;
        mVersionCode = apk.getLongVersionCode();
        mSigningDetails = apk.signingDetails;
        mResolvedBaseFile = addedFile;

        assertApkConsistentLocked(String.valueOf(addedFile), apk);

        if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
            try {
                // STOPSHIP: For APEX we should also implement proper APK Signature verification.
                mSigningDetails = ApkSignatureVerifier.plsCertsNoVerifyOnlyCerts(
                    pkgInfo.applicationInfo.sourceDir,
                    PackageParser.SigningDetails.SignatureSchemeVersion.JAR);
            } catch (PackageParserException e) {
                throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                    "Couldn't obtain signatures from base APK");
            }
        }
    }

    /**
     * Validate install by confirming that all application packages are have
     * consistent package name, version code, and signing certificates.
@@ -1060,7 +1141,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
     * {@link PackageManagerService}.
     */
    @GuardedBy("mLock")
    private void validateInstallLocked(@Nullable PackageInfo pkgInfo)
    private void validateApkInstallLocked(@Nullable PackageInfo pkgInfo)
            throws PackageManagerException {
        ApkLite baseApk = null;
        mPackageName = null;
+7 −1
Original line number Diff line number Diff line
@@ -920,7 +920,10 @@ class PackageManagerShellCommand extends ShellCommand {
                pw.println("Error: must either specify a package size or an APK file");
                return 1;
            }
            if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
            final boolean isApex =
                    (params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;
            String splitName = "base." + (isApex ? "apex" : "apk");
            if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, splitName,
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
@@ -2262,6 +2265,9 @@ class PackageManagerShellCommand extends ShellCommand {
                case "--force-sdk":
                    sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK;
                    break;
                case "--apex":
                    sessionParams.installFlags |= PackageManager.INSTALL_APEX;
                    break;
                default:
                    throw new IllegalArgumentException("Unknown option " + opt);
            }