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

Commit 192400cf authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

Fail install when resources.arsc is compressed

If an application targets R+, prevent the application from being
installed if the app has a compressed resources.arsc or if the
resources.arsc is not aligned on a 4-byte boundary. Resources tables
that cannot be memory mapped have to be read into a buffer in RAM
and exert unnecessary memory pressure on the system.

Bug: 132742131
Test: manual (adding CTS tests)
Change-Id: Ieef764c87643863de24531fac12cc520fe6d90d0
parent 2d8d9812
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -1550,6 +1550,16 @@ public abstract class PackageManager {
     */
    public static final int INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED = -123;

    /**
     * Installation failed return code: the {@code resources.arsc} of one of the APKs being
     * installed is compressed or not aligned on a 4-byte boundary. Resource tables that cannot be
     * memory mapped exert excess memory pressure on the system and drastically slow down
     * construction of {@link Resources} objects.
     *
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED = -124;

    /** @hide */
    @IntDef(flag = true, prefix = { "DELETE_" }, value = {
            DELETE_KEEP_DATA,
+15 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFES
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
import static android.os.Build.VERSION_CODES.DONUT;
import static android.os.Build.VERSION_CODES.O;
@@ -347,7 +348,20 @@ public class ParsingPackageUtils {
                                + result.getErrorMessage());
            }

            ParsingPackage pkg = result.getResult();
            final ParsingPackage pkg = result.getResult();
            if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.R
                    && assets.containsAllocatedTable()) {
                final ParseResult<?> deferResult = input.deferError(
                        "Targeting R+ (version" + Build.VERSION_CODES.R + " and above) requires the"
                                + " resources.arsc of installed APKs to be stored uncompressed and"
                                + " aligned on a 4-byte boundary",
                        DeferredError.RESOURCES_ARSC_COMPRESSED);
                if (deferResult.isError()) {
                    return input.error(INSTALL_PARSE_FAILED_RESOURCES_ARSC_COMPRESSED,
                            deferResult.getErrorMessage());
                }
            }

            ApkAssets apkAssets = assets.getApkAssets()[0];
            if (apkAssets.definesOverlayable()) {
                SparseArray<String> packageNames = assets.getAssignedPackageIdentifiers();
+10 −0
Original line number Diff line number Diff line
@@ -59,6 +59,16 @@ public interface ParseInput {
        @ChangeId
        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
        public static final long EMPTY_INTENT_ACTION_CATEGORY = 151163173;

        /**
         * The {@code resources.arsc} of one of the APKs being installed is compressed or not
         * aligned on a 4-byte boundary. Resource tables that cannot be memory mapped exert excess
         * memory pressure on the system and drastically slow down construction of
         * {@link android.content.res.Resources} objects.
         */
        @ChangeId
        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
        public static final long RESOURCES_ARSC_COMPRESSED = 132742131;
    }

    <ResultType> ParseResult<ResultType> success(ResultType result);
+14 −0
Original line number Diff line number Diff line
@@ -819,6 +819,19 @@ public final class AssetManager implements AutoCloseable {
        }
    }

    /**
     * Returns whether the {@code resources.arsc} of any loaded apk assets is allocated in RAM
     * (not mmapped).
     *
     * @hide
     */
    public boolean containsAllocatedTable() {
        synchronized (this) {
            ensureValidLocked();
            return nativeContainsAllocatedTable(mObject);
        }
    }

    CharSequence getPooledStringForCookie(int cookie, int id) {
        // Cookies map to ApkAssets starting at 1.
        return getApkAssets()[cookie - 1].getStringFromPool(id);
@@ -1482,6 +1495,7 @@ public final class AssetManager implements AutoCloseable {
            long ptr, boolean includeOverlays, boolean includeLoaders);

    // File native methods.
    private static native boolean nativeContainsAllocatedTable(long ptr);
    private static native @Nullable String[] nativeList(long ptr, @NonNull String path)
            throws IOException;
    private static native long nativeOpenAsset(long ptr, @NonNull String fileName, int accessMode);
+6 −0
Original line number Diff line number Diff line
@@ -481,6 +481,11 @@ static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/
  return sparse_array;
}

static jboolean ContainsAllocatedTable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
  ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
  return assetmanager->ContainsAllocatedTable();
}

static jobjectArray NativeList(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring path) {
  ScopedUtfChars path_utf8(env, path);
  if (path_utf8.c_str() == nullptr) {
@@ -1495,6 +1500,7 @@ static const JNINativeMethod gAssetManagerMethods[] = {
     (void*)NativeGetAssignedPackageIdentifiers},

    // AssetManager file methods.
    {"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable},
    {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
    {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
    {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
Loading