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

Commit 70779c71 authored by Narayan Kamath's avatar Narayan Kamath Committed by Android (Google) Code Review
Browse files

Merge "Multi-arch application installs."

parents a12a9a5d ff110bd6
Loading
Loading
Loading
Loading
+56 −5
Original line number Diff line number Diff line
@@ -491,13 +491,55 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
    public String nativeLibraryDir;

    /**
     * The ABI that this application requires, This is inferred from the ABIs
     * The path under the apps data directory we store unpacked libraries. For
     * new installs, we create subdirectories under legacyNativeLibraryDir that are
     * architecture specific. For legacy installs, the shared libraries are
     * placed directly under this path.
     *
     * For "legacy" installs {@code nativeLibraryDir} will be equal to this path.
     * For newer installs, it will be derived based on the codePath and the primary
     * cpu abi.
     *
     * @hide.
     */
    public String legacyNativeLibraryDir;

    /**
     * The primary ABI that this application requires, This is inferred from the ABIs
     * of the native JNI libraries the application bundles. Will be {@code null}
     * if this application does not require any particular ABI.
     *
     * If non-null, the application will always be launched with this ABI.
     *
     * {@hide}
     */
    public String primaryCpuAbi;

    /**
     * The secondary ABI for this application. Might be non-null for multi-arch
     * installs. The application itself never uses this ABI, but other applications that
     * use its code might.
     *
     * {@hide}
     */
    public String secondaryCpuAbi;

    /**
     * The derived APK "root" for the given package. Will be non-null for bundled and
     * updated system apps. This will be a top level path under which apks and libraries
     * are installed, for eg. {@code /system}, {@code /oem} or {@code /vendor}. This is
     * used to calculate the location of native code for a given package, for e.g
     * {@code /vendor/lib} or {@code /vendor/lib64}.
     *
     * For app updates or fresh app installs, this will be {@code null} and we will use
     * {@code legacyNativeLibraryDir}
     *
     * NOTE: This can be removed if we have a unified layout for bundled and installed
     * apps.
     *
     * {@hide}
     */
    public String cpuAbi;
    public String apkRoot;

    /**
     * The kernel user-ID that has been assigned to this application;
@@ -641,7 +683,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        splitSourceDirs = orig.splitSourceDirs;
        splitPublicSourceDirs = orig.splitPublicSourceDirs;
        nativeLibraryDir = orig.nativeLibraryDir;
        cpuAbi = orig.cpuAbi;
        legacyNativeLibraryDir = orig.legacyNativeLibraryDir;
        primaryCpuAbi = orig.primaryCpuAbi;
        secondaryCpuAbi = orig.secondaryCpuAbi;
        apkRoot = orig.apkRoot;
        resourceDirs = orig.resourceDirs;
        seinfo = orig.seinfo;
        sharedLibraryFiles = orig.sharedLibraryFiles;
@@ -685,7 +730,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        dest.writeStringArray(splitSourceDirs);
        dest.writeStringArray(splitPublicSourceDirs);
        dest.writeString(nativeLibraryDir);
        dest.writeString(cpuAbi);
        dest.writeString(legacyNativeLibraryDir);
        dest.writeString(primaryCpuAbi);
        dest.writeString(secondaryCpuAbi);
        dest.writeString(apkRoot);
        dest.writeStringArray(resourceDirs);
        dest.writeString(seinfo);
        dest.writeStringArray(sharedLibraryFiles);
@@ -728,7 +776,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
        splitSourceDirs = source.readStringArray();
        splitPublicSourceDirs = source.readStringArray();
        nativeLibraryDir = source.readString();
        cpuAbi = source.readString();
        legacyNativeLibraryDir = source.readString();
        primaryCpuAbi = source.readString();
        secondaryCpuAbi = source.readString();
        apkRoot = source.readString();
        resourceDirs = source.readStringArray();
        seinfo = source.readString();
        sharedLibraryFiles = source.readStringArray();
+7 −0
Original line number Diff line number Diff line
@@ -36,6 +36,13 @@ public class PackageInfoLite implements Parcelable {
     */
    public int versionCode;

    /**
     * The android:multiArch flag from the package manifest. If set,
     * we will extract all native libraries for the given app, not just those
     * from the preferred ABI.
     */
    public boolean multiArch;

    /**
     * Specifies the recommended install location. Can be one of
     * {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage
+13 −3
Original line number Diff line number Diff line
@@ -249,6 +249,8 @@ public class PackageParser {
        /** Paths of any split APKs, ordered by parsed splitName */
        public final String[] splitCodePaths;

        public final boolean multiArch;

        private PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
                String[] splitCodePaths) {
            this.packageName = baseApk.packageName;
@@ -259,6 +261,7 @@ public class PackageParser {
            this.codePath = codePath;
            this.baseCodePath = baseApk.codePath;
            this.splitCodePaths = splitCodePaths;
            this.multiArch = baseApk.multiArch;
        }

        public List<String> getAllCodePaths() {
@@ -282,9 +285,11 @@ public class PackageParser {
        public final int installLocation;
        public final VerifierInfo[] verifiers;
        public final Signature[] signatures;
        public final boolean multiArch;

        public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                int installLocation, List<VerifierInfo> verifiers, Signature[] signatures) {
                int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
                boolean multiArch) {
            this.codePath = codePath;
            this.packageName = packageName;
            this.splitName = splitName;
@@ -292,6 +297,7 @@ public class PackageParser {
            this.installLocation = installLocation;
            this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
            this.signatures = signatures;
            this.multiArch = multiArch;
        }
    }

@@ -1114,6 +1120,7 @@ public class PackageParser {
        int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
        int versionCode = 0;
        int numFound = 0;
        boolean multiArch = false;
        for (int i = 0; i < attrs.getAttributeCount(); i++) {
            String attr = attrs.getAttributeName(i);
            if (attr.equals("installLocation")) {
@@ -1123,8 +1130,11 @@ public class PackageParser {
            } else if (attr.equals("versionCode")) {
                versionCode = attrs.getAttributeIntValue(i, 0);
                numFound++;
            } else if (attr.equals("multiArch")) {
                multiArch = attrs.getAttributeBooleanValue(i, false);
                numFound++;
            }
            if (numFound >= 2) {
            if (numFound >= 3) {
                break;
            }
        }
@@ -1149,7 +1159,7 @@ public class PackageParser {
        }

        return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
                installLocation, verifiers, signatures);
                installLocation, verifiers, signatures, multiArch);
    }

    /**
+31 −24
Original line number Diff line number Diff line
@@ -131,11 +131,16 @@ public class NativeLibraryHelper {
     *
     * @return size of all native binary files in bytes
     */
    public static long sumNativeBinariesLI(Handle handle, String abi) {
    public static long sumNativeBinariesLI(Handle handle, String[] abis) {
        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;
    }

@@ -196,45 +201,47 @@ public class NativeLibraryHelper {
    private native static int nativeFindSupportedAbi(long handle, String[] supportedAbis);

    // Convenience method to call removeNativeBinariesFromDirLI(File)
    public static boolean removeNativeBinariesLI(String nativeLibraryPath) {
        if (nativeLibraryPath == null) return false;
        return removeNativeBinariesFromDirLI(new File(nativeLibraryPath));
    public static void removeNativeBinariesLI(String nativeLibraryPath) {
        if (nativeLibraryPath == null) return;
        removeNativeBinariesFromDirLI(new File(nativeLibraryPath), false /* delete root dir */);
    }

    // Remove the native binaries of a given package. This simply
    // gets rid of the files in the 'lib' sub-directory.
    public static boolean removeNativeBinariesFromDirLI(File nativeLibraryDir) {
    /**
     * Remove the native binaries of a given package. This deletes the files
     */
    public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir) {
        if (DEBUG_NATIVE) {
            Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryDir.getPath());
            Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
        }

        boolean deletedFiles = false;

        /*
         * Just remove any file in the directory. Since the directory is owned
         * by the 'system' UID, the application is not supposed to have written
         * anything there.
         */
        if (nativeLibraryDir.exists()) {
            final File[] binaries = nativeLibraryDir.listFiles();
            if (binaries != null) {
                for (int nn = 0; nn < binaries.length; nn++) {
        if (nativeLibraryRoot.exists()) {
            final File[] files = nativeLibraryRoot.listFiles();
            if (files != null) {
                for (int nn = 0; nn < files.length; nn++) {
                    if (DEBUG_NATIVE) {
                        Slog.d(TAG, "    Deleting " + binaries[nn].getName());
                        Slog.d(TAG, "    Deleting " + files[nn].getName());
                    }

                    if (!binaries[nn].delete()) {
                        Slog.w(TAG, "Could not delete native binary: " + binaries[nn].getPath());
                    } else {
                        deletedFiles = true;
                    if (files[nn].isDirectory()) {
                        removeNativeBinariesFromDirLI(files[nn], true /* delete root dir */);
                    } else if (!files[nn].delete()) {
                        Slog.w(TAG, "Could not delete native binary: " + files[nn].getPath());
                    }
                }
            }
            // Do not delete 'lib' directory itself, or this will prevent
            // installation of future updates.
            // Do not delete 'lib' directory itself, unless we're specifically
            // asked to or this will prevent installation of future updates.
            if (deleteRootDir) {
                if (!nativeLibraryRoot.delete()) {
                    Slog.w(TAG, "Could not delete native binary directory: " + nativeLibraryRoot.getPath());
                }
            }
        }

        return deletedFiles;
    }

    // We don't care about the other return values for now.
+71 −35
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import com.android.internal.content.PackageHelper;
import com.android.internal.os.IParcelFileDescriptorFactory;
import com.android.internal.util.ArrayUtils;

import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
import libcore.io.Streams;

@@ -186,6 +187,7 @@ public class DefaultContainerService extends IntentService {
            ret.verifiers = pkg.verifiers;
            ret.recommendedInstallLocation = recommendAppInstallLocation(pkg, flags, threshold,
                    abiOverride);
            ret.multiArch = pkg.multiArch;

            return ret;
        }
@@ -363,31 +365,26 @@ public class DefaultContainerService extends IntentService {
        final String resFileName = "pkg.apk";
        final String publicResFileName = "res.zip";

        // The .apk file
        String codePath = pkg.baseCodePath;
        File codeFile = new File(codePath);
        if (pkg.multiArch) {
            // TODO: Support multiArch installs on ASEC.
            throw new IllegalArgumentException("multiArch not supported on ASEC installs.");
        }

        String[] abiList = Build.SUPPORTED_ABIS;
        if (abiOverride != null) {
            abiList = new String[] { abiOverride };
        } else {
        // The .apk file
        final String codePath = pkg.baseCodePath;
        final File codeFile = new File(codePath);
        final String[] abis;
        try {
                if (Build.SUPPORTED_64_BIT_ABIS.length > 0 &&
                        NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
                    abiList = Build.SUPPORTED_32_BIT_ABIS;
                }
            abis = calculateAbiList(handle, abiOverride, pkg.multiArch);
        } catch (IOException ioe) {
                Slog.w(TAG, "Problem determining ABI for: " + codeFile.getPath());
            Slog.w(TAG, "Problem determining app ABIS: " + ioe);
            return null;
        }
        }

        final int abiIndex = NativeLibraryHelper.findSupportedAbi(handle, abiList);

        // Calculate size of container needed to hold base APK.
        final int sizeMb;
        try {
            sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abiIndex);
            sizeMb = calculateContainerSize(pkg, handle, isForwardLocked, abis);
        } catch (IOException e) {
            Slog.w(TAG, "Problem when trying to copy " + codeFile.getPath());
            return null;
@@ -451,18 +448,19 @@ public class DefaultContainerService extends IntentService {
        final File sharedLibraryDir = new File(newCachePath, LIB_DIR_NAME);
        if (sharedLibraryDir.mkdir()) {
            int ret = PackageManager.INSTALL_SUCCEEDED;
            if (abiIndex >= 0) {
            if (abis != null) {
                // TODO(multiArch): Support multi-arch installs on asecs. Note that we are NOT
                // using an ISA specific subdir here for now.
                final String abi = abis[0];
                ret = NativeLibraryHelper.copyNativeBinariesIfNeededLI(handle,
                        sharedLibraryDir, abiList[abiIndex]);
            } else if (abiIndex != PackageManager.NO_NATIVE_LIBRARIES) {
                ret = abiIndex;
            }
                        sharedLibraryDir, abi);

                if (ret != PackageManager.INSTALL_SUCCEEDED) {
                    Slog.e(TAG, "Could not copy native libraries to " + sharedLibraryDir.getPath());
                    PackageHelper.destroySdDir(newCid);
                    return null;
                }
            }
        } else {
            Slog.e(TAG, "Could not create native lib directory: " + sharedLibraryDir.getPath());
            PackageHelper.destroySdDir(newCid);
@@ -687,14 +685,53 @@ public class DefaultContainerService extends IntentService {
        NativeLibraryHelper.Handle handle = null;
        try {
            handle = NativeLibraryHelper.Handle.create(pkg);
            final int abi = NativeLibraryHelper.findSupportedAbi(handle,
                    (abiOverride != null) ? new String[] { abiOverride } : Build.SUPPORTED_ABIS);
            return calculateContainerSize(pkg, handle, isForwardLocked, abi);
            return calculateContainerSize(pkg, handle, isForwardLocked,
                    calculateAbiList(handle, abiOverride, pkg.multiArch));
        } finally {
            IoUtils.closeQuietly(handle);
        }
    }

    private String[] calculateAbiList(NativeLibraryHelper.Handle handle, String abiOverride,
                                      boolean isMultiArch) throws IOException {
        if (isMultiArch) {
            final int abi32 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_32_BIT_ABIS);
            final int abi64 = NativeLibraryHelper.findSupportedAbi(handle, Build.SUPPORTED_64_BIT_ABIS);

            if (abi32 >= 0 && abi64 >= 0) {
                return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64], Build.SUPPORTED_32_BIT_ABIS[abi32] };
            } else if (abi64 >= 0) {
                return new String[] { Build.SUPPORTED_64_BIT_ABIS[abi64] };
            } else if (abi32 >= 0) {
                return new String[] { Build.SUPPORTED_32_BIT_ABIS[abi32] };
            }

            if (abi64 != PackageManager.NO_NATIVE_LIBRARIES || abi32 != PackageManager.NO_NATIVE_LIBRARIES) {
                throw new IOException("Error determining ABI list: errorCode=[" + abi32 + "," + abi64 + "]");
            }

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

            final int abi = NativeLibraryHelper.findSupportedAbi(handle,abiList);
            if (abi >= 0) {
               return new String[]{Build.SUPPORTED_ABIS[abi]};
            }

            if (abi != PackageManager.NO_NATIVE_LIBRARIES) {
                throw new IOException("Error determining ABI list: errorCode=" + abi);
            }
        }

        return null;
    }

    /**
     * Calculate the container size for a package.
     * 
@@ -702,7 +739,7 @@ public class DefaultContainerService extends IntentService {
     * @throws IOException when there is a problem reading the file
     */
    private int calculateContainerSize(PackageLite pkg, NativeLibraryHelper.Handle handle,
            boolean isForwardLocked, int abiIndex) throws IOException {
            boolean isForwardLocked, String[] abis) throws IOException {
        // Calculate size of container needed to hold APKs.
        long sizeBytes = 0;
        for (String codePath : pkg.getAllCodePaths()) {
@@ -715,9 +752,8 @@ public class DefaultContainerService extends IntentService {

        // Check all the native files that need to be copied and add that to the
        // container size.
        if (abiIndex >= 0) {
            sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle,
                    Build.SUPPORTED_ABIS[abiIndex]);
        if (abis != null) {
            sizeBytes += NativeLibraryHelper.sumNativeBinariesLI(handle, abis);
        }

        int sizeMb = (int) (sizeBytes >> 20);
Loading