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

Commit e7b5a8db authored by Victor Hsieh's avatar Victor Hsieh
Browse files

Introduce android:preferCodeIntegrity, private for now

This attribute is default to false, and if set to true, indicates that
the app wants to run with strong integrity guarantee currently the
platform can provide the best.

In this change, this flag implies that on install time,
 1) .dex and .so must be stored uncompressed and aligned to install
 2) android:extractNativeLibs must be false to install

At run time, ART will run from the dex within the APK directly, and NDK
libraries will be mapped directly from the APK.  This way, thest files
stay protected by signature.

The attribute currently stays in private to make development progress.
We plan to make it public once we confirm the demand from some apps.

Test: atest AppIntegrityTest (to be added in ag/5554864 after publicized)
Bug: 112037137
Change-Id: Ifde90cb0666fbb57e8b61f90b4ba1a2dd2a2b4ae
parent 252e8d04
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -639,6 +639,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
     */
    public static final int PRIVATE_FLAG_HAS_FRAGILE_USER_DATA = 1 << 24;

    /**
     * Indicate whether this application prefers code integrity, that is, run only code that is
     * signed. This requires android:extractNativeLibs to be "false", as well as .dex and .so (if
     * any) stored uncompressed inside the APK, which is signed. At run time, the implications
     * include:
     *
     * <ul>
     * <li>ART will JIT the dex code directly from the APK. There may be performance characteristic
     * changes depend on the actual workload.
     * </ul>
     *
     * @hide
     */
    public static final int PRIVATE_FLAG_PREFER_CODE_INTEGRITY = 1 << 25;

    /** @hide */
    @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -654,6 +669,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
            PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
            PRIVATE_FLAG_OEM,
            PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
            PRIVATE_FLAG_PREFER_CODE_INTEGRITY,
            PRIVATE_FLAG_PRIVILEGED,
            PRIVATE_FLAG_PRODUCT,
            PRIVATE_FLAG_PRODUCT_SERVICES,
+20 −2
Original line number Diff line number Diff line
@@ -475,6 +475,7 @@ public class PackageParser {
        public final boolean extractNativeLibs;
        public final boolean isolatedSplits;
        public final boolean isSplitRequired;
        public final boolean preferCodeIntegrity;

        public ApkLite(String codePath, String packageName, String splitName,
                boolean isFeatureSplit,
@@ -483,7 +484,7 @@ public class PackageParser {
                int revisionCode, int installLocation, List<VerifierInfo> verifiers,
                SigningDetails signingDetails, boolean coreApp,
                boolean debuggable, boolean multiArch, boolean use32bitAbi,
                boolean extractNativeLibs, boolean isolatedSplits) {
                boolean preferCodeIntegrity, boolean extractNativeLibs, boolean isolatedSplits) {
            this.codePath = codePath;
            this.packageName = packageName;
            this.splitName = splitName;
@@ -500,6 +501,7 @@ public class PackageParser {
            this.debuggable = debuggable;
            this.multiArch = multiArch;
            this.use32bitAbi = use32bitAbi;
            this.preferCodeIntegrity = preferCodeIntegrity;
            this.extractNativeLibs = extractNativeLibs;
            this.isolatedSplits = isolatedSplits;
            this.isSplitRequired = isSplitRequired;
@@ -1722,6 +1724,7 @@ public class PackageParser {
        boolean isolatedSplits = false;
        boolean isFeatureSplit = false;
        boolean isSplitRequired = false;
        boolean preferCodeIntegrity = false;
        String configForSplit = null;
        String usesSplitName = null;

@@ -1784,6 +1787,9 @@ public class PackageParser {
                    if ("extractNativeLibs".equals(attr)) {
                        extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
                    }
                    if ("preferCodeIntegrity".equals(attr)) {
                        preferCodeIntegrity = attrs.getAttributeBooleanValue(i, false);
                    }
                }
            } else if (TAG_USES_SPLIT.equals(parser.getName())) {
                if (usesSplitName != null) {
@@ -1800,10 +1806,16 @@ public class PackageParser {
            }
        }

        if (preferCodeIntegrity && extractNativeLibs) {
            throw new PackageParserException(
                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Can't request both preferCodeIntegrity and extractNativeLibs");
        }

        return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
                configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor,
                revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable,
                multiArch, use32bitAbi, extractNativeLibs, isolatedSplits);
                multiArch, use32bitAbi, preferCodeIntegrity, extractNativeLibs, isolatedSplits);
    }

    /**
@@ -3654,6 +3666,12 @@ public class PackageParser {
            ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
        }

        if (sa.getBoolean(
                R.styleable.AndroidManifestApplication_preferCodeIntegrity,
                false)) {
            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY;
        }

        if (sa.getBoolean(
                R.styleable.AndroidManifestApplication_defaultToDeviceProtectedStorage,
                false)) {
+6 −0
Original line number Diff line number Diff line
@@ -1112,6 +1112,11 @@
         resource] to be present in order to function. Default value is false. -->
    <attr name="isSplitRequired" format="boolean" />

    <!-- Flag to specify if this app prioritizes code integrity. The system may choose
         to run with better integrity guarantee in various components if possible based on the app's
         <code>targetSdkVersion</code>. -->
    <attr name="preferCodeIntegrity" format="boolean" />

    <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
         {@code <application>} tag. If specified on the {@code <application>}
         tag these will be considered defaults for all activities in the
@@ -1580,6 +1585,7 @@
             to honor this flag as well. -->
        <attr name="usesCleartextTraffic" />
        <attr name="multiArch" />
        <attr name="preferCodeIntegrity" />
        <attr name="extractNativeLibs" />
        <attr name="defaultToDeviceProtectedStorage" format="boolean" />
        <attr name="directBootAware" />
+3 −2
Original line number Diff line number Diff line
@@ -1362,8 +1362,9 @@ public final class ProcessList {
                mService.mNativeDebuggingApp = null;
            }

            if (app.info.isPrivilegedApp() &&
                    DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet())) {
            if ((app.info.privateFlags & ApplicationInfo.PRIVATE_FLAG_PREFER_CODE_INTEGRITY) != 0
                    || (app.info.isPrivilegedApp()
                        && DexManager.isPackageSelectedToRunOob(app.pkgList.mPkgList.keySet()))) {
                runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
            }

+11 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.PackageInstallerService.PackageInstallObserverAdapter;
import com.android.server.pm.dex.DexManager;

import libcore.io.IoUtils;

@@ -1594,6 +1595,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
                }
            }
        }
        if (baseApk.preferCodeIntegrity) {
            for (File file : mResolvedStagedFiles) {
                if (file.getName().endsWith(".apk")
                        && !DexManager.auditUncompressedCodeInApk(file.getPath())) {
                    throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                            "Some code are not uncompressed and aligned correctly for "
                            + mPackageName);
                }
            }
        }
        if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
            throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
                    "Missing split for " + mPackageName);
Loading