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

Commit bb7b7bea authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

More progress towards split APKs in ASECs.

Teach DefaultContainerService to install split APKs, which will be
needed when moving to/from ASECs.  Also support forward locking for
testing purposes, even though its deprecated.

Move native library unpacking code to NativeLibraryHelper location
where it can be shared by both DCS and PMS.  Also update footprint
calculation logic to mirror the later unpack codepaths.

Immediately persist sealed sessions.  When resolving install
locations, prefer location of any existing install of that
package.  Lightweight parse requesting certificates now always
verifies that all contents are signed correctly.

Bug: 16514385
Change-Id: Ida1c4eb0f95b065104dd971e19126d4085ebf1f0
parent 80030f90
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -8667,7 +8667,7 @@ package android.content.pm {
    method public void removeSessionCallback(android.content.pm.PackageInstaller.SessionCallback);
    method public void uninstall(java.lang.String, android.content.IntentSender);
    field public static final java.lang.String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
    field public static final java.lang.String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";
    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
    field public static final java.lang.String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID";
    field public static final java.lang.String EXTRA_STATUS = "android.content.pm.extra.STATUS";
    field public static final java.lang.String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";
+8 −4
Original line number Diff line number Diff line
@@ -105,10 +105,14 @@ public class PackageInstaller {
    public static final String EXTRA_STATUS_MESSAGE = "android.content.pm.extra.STATUS_MESSAGE";

    /**
     * List of package names that are relevant to a status.
     * Package name relevant to a status.
     *
     * @see Intent#getStringArrayExtra(String)
     * @see Intent#getStringExtra(String)
     */
    public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";

    /** {@hide} */
    @Deprecated
    public static final String EXTRA_PACKAGE_NAMES = "android.content.pm.extra.PACKAGE_NAMES";

    /** {@hide} */
@@ -178,8 +182,8 @@ public class PackageInstaller {
     * permission, incompatible certificates, etc. The user may be able to
     * uninstall another app to fix the issue.
     * <p>
     * The result may also contain {@link #EXTRA_PACKAGE_NAMES} with the
     * specific packages identified as the cause of the conflict.
     * The result may also contain {@link #EXTRA_PACKAGE_NAME} with the
     * specific package identified as the cause of the conflict.
     *
     * @see #EXTRA_STATUS_MESSAGE
     */
+8 −34
Original line number Diff line number Diff line
@@ -596,7 +596,7 @@ public class PackageParser {
    public final static int PARSE_ON_SDCARD = 1<<5;
    public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
    public final static int PARSE_IS_PRIVILEGED = 1<<7;
    public final static int PARSE_GET_SIGNATURES = 1<<8;
    public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
    public final static int PARSE_TRUSTED_OVERLAY = 1<<9;

    private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
@@ -1087,34 +1087,6 @@ public class PackageParser {
        }
    }

    /**
     * Only collect certificates on the manifest; does not validate signatures
     * across remainder of package.
     */
    private static Signature[] collectManifestCertificates(File apkFile)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();
        try {
            final StrictJarFile jarFile = new StrictJarFile(apkPath);
            try {
                final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
                if (jarEntry == null) {
                    throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                            "Package " + apkPath + " has no manifest");
                }

                final Certificate[][] certs = loadCertificates(jarFile, jarEntry);
                return convertToSignatures(certs);

            } finally {
                jarFile.close();
            }
        } catch (GeneralSecurityException | IOException | RuntimeException e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                    "Failed to collect certificates from " + apkPath, e);
        }
    }

    private static Signature[] convertToSignatures(Certificate[][] certs)
            throws CertificateEncodingException {
        final Signature[] res = new Signature[certs.length];
@@ -1129,7 +1101,8 @@ public class PackageParser {
     * file, including package name, split name, and install location.
     *
     * @param apkFile path to a single APK
     * @param flags optional parse flags, such as {@link #PARSE_GET_SIGNATURES}
     * @param flags optional parse flags, such as
     *            {@link #PARSE_COLLECT_CERTIFICATES}
     */
    public static ApkLite parseApkLite(File apkFile, int flags)
            throws PackageParserException {
@@ -1154,11 +1127,12 @@ public class PackageParser {
            final Resources res = new Resources(assets, metrics, null);
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            // Only collect certificates on the manifest; does not validate
            // signatures across remainder of package.
            final Signature[] signatures;
            if ((flags & PARSE_GET_SIGNATURES) != 0) {
                signatures = collectManifestCertificates(apkFile);
            if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                // TODO: factor signature related items out of Package object
                final Package tempPkg = new Package(null);
                collectCertificates(tempPkg, apkFile, 0);
                signatures = tempPkg.mSignatures;
            } else {
                signatures = null;
            }
+181 −17
Original line number Diff line number Diff line
@@ -19,15 +19,25 @@ package com.android.internal.content;
import static android.content.pm.PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS;
import static android.content.pm.PackageManager.INSTALL_SUCCEEDED;
import static android.content.pm.PackageManager.NO_NATIVE_LIBRARIES;
import static android.system.OsConstants.S_IRGRP;
import static android.system.OsConstants.S_IROTH;
import static android.system.OsConstants.S_IRWXU;
import static android.system.OsConstants.S_IXGRP;
import static android.system.OsConstants.S_IXOTH;

import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.os.Build;
import android.os.SELinux;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Slog;

import dalvik.system.CloseGuard;
import dalvik.system.VMRuntime;

import java.io.Closeable;
import java.io.File;
@@ -44,6 +54,10 @@ public class NativeLibraryHelper {

    private static final boolean DEBUG_NATIVE = false;

    // Special value for {@code PackageParser.Package#cpuAbiOverride} to indicate
    // that the cpuAbiOverride must be clear.
    public static final String CLEAR_ABI_OVERRIDE = "-";

    /**
     * A handle to an opened package, consisting of one or more APKs. Used as
     * input to the various NativeLibraryHelper methods. Allows us to scan and
@@ -126,27 +140,17 @@ public class NativeLibraryHelper {

    private static native long nativeSumNativeBinaries(long handle, String cpuAbi);

    /**
     * Sums the size of native binaries in an APK for a given ABI.
     *
     * @return size of all native binary files in bytes
     */
    public static long sumNativeBinariesLI(Handle handle, String[] abis) {
    private native static int nativeCopyNativeBinaries(long handle,
            String sharedLibraryPath, String abiToCopy);

    private static long sumNativeBinaries(Handle handle, String abi) {
        long sum = 0;
        for (long apkHandle : handle.apkHandles) {
            // NOTE: For a given APK handle, we parse the central directory precisely
            // once, but prefix matching of entries requires a CD traversal, which can
            // take a while (even if it needs no additional I/O).
            for (String abi : abis) {
            sum += nativeSumNativeBinaries(apkHandle, abi);
        }
        }
        return sum;
    }

    private native static int nativeCopyNativeBinaries(long handle,
            String sharedLibraryPath, String abiToCopy);

    /**
     * Copies native binaries to a shared library directory.
     *
@@ -244,9 +248,171 @@ public class NativeLibraryHelper {
        }
    }

    private static void createNativeLibrarySubdir(File path) throws IOException {
        if (!path.isDirectory()) {
            path.delete();

            if (!path.mkdir()) {
                throw new IOException("Cannot create " + path.getPath());
            }

            try {
                Os.chmod(path.getPath(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
            } catch (ErrnoException e) {
                throw new IOException("Cannot chmod native library directory "
                        + path.getPath(), e);
            }
        } else if (!SELinux.restorecon(path)) {
            throw new IOException("Cannot set SELinux context for " + path.getPath());
        }
    }

    private static long sumNativeBinaries(Handle handle, String[] abiList) {
        int abi = findSupportedAbi(handle, abiList);
        if (abi >= 0) {
            return sumNativeBinaries(handle, abiList[abi]);
        } else {
            return 0;
        }
    }

    public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
            String[] abiList, boolean useIsaSubdir) throws IOException {
        createNativeLibrarySubdir(libraryRoot);

        /*
         * If this is an internal application or our nativeLibraryPath points to
         * the app-lib directory, unpack the libraries if necessary.
         */
        int abi = findSupportedAbi(handle, abiList);
        if (abi >= 0) {
            /*
             * If we have a matching instruction set, construct a subdir under the native
             * library root that corresponds to this instruction set.
             */
            final String instructionSet = VMRuntime.getInstructionSet(abiList[abi]);
            final File subDir;
            if (useIsaSubdir) {
                final File isaSubdir = new File(libraryRoot, instructionSet);
                createNativeLibrarySubdir(isaSubdir);
                subDir = isaSubdir;
            } else {
                subDir = libraryRoot;
            }

            int copyRet = copyNativeBinariesIfNeededLI(handle, subDir, abiList[abi]);
            if (copyRet != PackageManager.INSTALL_SUCCEEDED) {
                return copyRet;
            }
        }

        return abi;
    }

    public static int copyNativeBinariesIfNeededLI(Handle handle, File libraryRoot,
            String abiOverride, boolean multiArch) {
        try {
            if (multiArch) {
                // Warn if we've set an abiOverride for multi-lib packages..
                // By definition, we need to copy both 32 and 64 bit libraries for
                // such packages.
                if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
                    Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
                }

                int copyRet = PackageManager.NO_NATIVE_LIBRARIES;
                if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
                    copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
                            Build.SUPPORTED_32_BIT_ABIS, true /* use isa specific subdirs */);
                    if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
                            copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
                        Slog.w(TAG, "Failure copying 32 bit native libraries; copyRet=" +copyRet);
                        return copyRet;
                    }
                }

                if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
                    copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot,
                            Build.SUPPORTED_64_BIT_ABIS, true /* use isa specific subdirs */);
                    if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES &&
                            copyRet != PackageManager.INSTALL_FAILED_NO_MATCHING_ABIS) {
                        Slog.w(TAG, "Failure copying 64 bit native libraries; copyRet=" +copyRet);
                        return copyRet;
                    }
                }
            } else {
                String cpuAbiOverride = null;
                if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
                    cpuAbiOverride = null;
                } else if (abiOverride != null) {
                    cpuAbiOverride = abiOverride;
                }

                String[] abiList = (cpuAbiOverride != null) ?
                        new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
                        hasRenderscriptBitcode(handle)) {
                    abiList = Build.SUPPORTED_32_BIT_ABIS;
                }

                int copyRet = copyNativeBinariesIfNeededLI(handle, libraryRoot, abiList,
                        true /* use isa specific subdirs */);
                if (copyRet < 0 && copyRet != PackageManager.NO_NATIVE_LIBRARIES) {
                    Slog.w(TAG, "Failure copying native libraries [errorCode=" + copyRet + "]");
                    return copyRet;
                }
            }

            return PackageManager.INSTALL_SUCCEEDED;
        } catch (IOException e) {
            Slog.e(TAG, "Copying native libraries failed", e);
            return PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
        }
    }

    public static long sumNativeBinaries(Handle handle, String abiOverride, boolean multiArch)
            throws IOException {
        long sum = 0;
        if (multiArch) {
            // Warn if we've set an abiOverride for multi-lib packages..
            // By definition, we need to copy both 32 and 64 bit libraries for
            // such packages.
            if (abiOverride != null && !CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
                Slog.w(TAG, "Ignoring abiOverride for multi arch application.");
            }

            if (Build.SUPPORTED_32_BIT_ABIS.length > 0) {
                sum += sumNativeBinaries(handle, Build.SUPPORTED_32_BIT_ABIS);
            }

            if (Build.SUPPORTED_64_BIT_ABIS.length > 0) {
                sum += sumNativeBinaries(handle, Build.SUPPORTED_64_BIT_ABIS);
            }
        } else {
            String cpuAbiOverride = null;
            if (CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
                cpuAbiOverride = null;
            } else if (abiOverride != null) {
                cpuAbiOverride = abiOverride;
            }

            String[] abiList = (cpuAbiOverride != null) ?
                    new String[] { cpuAbiOverride } : Build.SUPPORTED_ABIS;
            if (Build.SUPPORTED_64_BIT_ABIS.length > 0 && cpuAbiOverride == null &&
                    hasRenderscriptBitcode(handle)) {
                abiList = Build.SUPPORTED_32_BIT_ABIS;
            }

            sum += sumNativeBinaries(handle, abiList);
        }
        return sum;
    }

    // We don't care about the other return values for now.
    private static final int BITCODE_PRESENT = 1;

    private static native int hasRenderscriptBitcode(long apkHandle);

    public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
        for (long apkHandle : handle.apkHandles) {
            final int res = hasRenderscriptBitcode(apkHandle);
@@ -258,6 +424,4 @@ public class NativeLibraryHelper {
        }
        return false;
    }

    private static native int hasRenderscriptBitcode(long apkHandle);
}
+40 −10
Original line number Diff line number Diff line
@@ -17,13 +17,17 @@
package com.android.internal.content;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.storage.IMountService;
import android.os.storage.StorageManager;
import android.os.storage.StorageResultCode;
@@ -215,7 +219,7 @@ public class PackageHelper {
    /**
     * Extract public files for the single given APK.
     */
    public static int extractPublicFiles(String apkPath, File publicZipFile)
    public static long extractPublicFiles(File apkFile, File publicZipFile)
            throws IOException {
        final FileOutputStream fstr;
        final ZipOutputStream publicZipOutStream;
@@ -226,12 +230,13 @@ public class PackageHelper {
        } else {
            fstr = new FileOutputStream(publicZipFile);
            publicZipOutStream = new ZipOutputStream(fstr);
            Log.d(TAG, "Extracting " + apkFile + " to " + publicZipFile);
        }

        int size = 0;
        long size = 0L;

        try {
            final ZipFile privateZip = new ZipFile(apkPath);
            final ZipFile privateZip = new ZipFile(apkFile.getAbsolutePath());
            try {
                // Copy manifest, resources.arsc and res directory to public zip
                for (final ZipEntry zipEntry : Collections.list(privateZip.entries())) {
@@ -308,8 +313,15 @@ public class PackageHelper {
     * Given a requested {@link PackageInfo#installLocation} and calculated
     * install size, pick the actual location to install the app.
     */
    public static int resolveInstallLocation(Context context, int installLocation, long sizeBytes,
            int installFlags) {
    public static int resolveInstallLocation(Context context, String packageName,
            int installLocation, long sizeBytes, int installFlags) {
        ApplicationInfo existingInfo = null;
        try {
            existingInfo = context.getPackageManager().getApplicationInfo(packageName,
                    PackageManager.GET_UNINSTALLED_PACKAGES);
        } catch (NameNotFoundException ignored) {
        }

        final int prefer;
        final boolean checkBoth;
        if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
@@ -325,7 +337,16 @@ public class PackageHelper {
            prefer = RECOMMEND_INSTALL_EXTERNAL;
            checkBoth = true;
        } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
            // When app is already installed, prefer same medium
            if (existingInfo != null) {
                if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                    prefer = RECOMMEND_INSTALL_EXTERNAL;
                } else {
                    prefer = RECOMMEND_INSTALL_INTERNAL;
                }
            } else {
                prefer = RECOMMEND_INSTALL_INTERNAL;
            }
            checkBoth = true;
        } else {
            prefer = RECOMMEND_INSTALL_INTERNAL;
@@ -337,14 +358,15 @@ public class PackageHelper {

        boolean fitsOnInternal = false;
        if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
            fitsOnInternal = (sizeBytes
                    <= storage.getStorageBytesUntilLow(Environment.getDataDirectory()));
            final File target = Environment.getDataDirectory();
            fitsOnInternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
        }

        boolean fitsOnExternal = false;
        if (!emulated && (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL)) {
            fitsOnExternal = (sizeBytes
                    <= storage.getStorageBytesUntilLow(Environment.getExternalStorageDirectory()));
            final File target = new UserEnvironment(UserHandle.USER_OWNER)
                    .getExternalStorageDirectory();
            fitsOnExternal = (sizeBytes <= storage.getStorageBytesUntilLow(target));
        }

        if (prefer == RECOMMEND_INSTALL_INTERNAL) {
@@ -377,4 +399,12 @@ public class PackageHelper {
            return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
        }
    }

    public static String replaceEnd(String str, String before, String after) {
        if (!str.endsWith(before)) {
            throw new IllegalArgumentException(
                    "Expected " + str + " to end with " + before);
        }
        return str.substring(0, str.length() - before.length()) + after;
    }
}
Loading