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

Commit b1eeab34 authored by Dario Freni's avatar Dario Freni Committed by Android (Google) Code Review
Browse files

Merge "Initial support for installing APEX via adb."

parents 85d47fd9 d8bf22e8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -693,6 +693,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);
            }