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

Commit 7029b75f authored by Winson's avatar Winson
Browse files

Remove PlatformCompat dependency from getPackageArchiveInfo

It requires a permission which we can't force apps to take to
maintain backwards compatibility. We also arguably cannot because
it leaks visibility, although only for debuggable apps/non-release
builds.

Instead, there's a new static method for getting the raw targetSdk
to gate against and the check is done manually, ignoring
enabled/disabled state. This will cause a mismatch between certain
apps and some system services like AppIntegrityManager, but the
effects should be minimal if we assume that most people ship
valid APKs. At worse the integrity check will pass an APK that
PM will fail, which doesn't break the feature.

Bug: 156356591
Bug: 156778241

Test: manual device boots

Change-Id: I877a5061476b86b9d63c34e75f16b38be8c3e1c2
parent 21ebdf80
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -51,7 +51,9 @@ import android.content.pm.dex.ArtManager;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.Rect;
@@ -6061,7 +6063,8 @@ public abstract class PackageManager {
        boolean collectCertificates = (flags & PackageManager.GET_SIGNATURES) != 0
                || (flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0;

        ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefaultOneTime(
        ParseInput input = ParseTypeImpl.forParsingWithoutPlatformCompat().reset();
        ParseResult<ParsingPackage> result = ParsingPackageUtils.parseDefault(input,
                new File(archiveFilePath), 0, collectCertificates);
        if (result.isError()) {
            return null;
+11 −2
Original line number Diff line number Diff line
@@ -130,15 +130,24 @@ public class ParsingPackageUtils {

    public static final String TAG = ParsingUtils.TAG;

    /**
     * @see #parseDefault(ParseInput, File, int, boolean)
     */
    @NonNull
    public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
            @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
        return parseDefault(input, file, parseFlags, collectCertificates);
    }

    /**
     * For cases outside of PackageManagerService when an APK needs to be parsed as a one-off
     * request, without caching the input object and without querying the internal system state
     * for feature support.
     */
    @NonNull
    public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
    public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
            @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
        ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
        ParseResult<ParsingPackage> result;

        ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() {
+20 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.content.pm.parsing.result;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.ChangeId;
@@ -69,6 +70,25 @@ public interface ParseInput {
        @ChangeId
        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
        public static final long RESOURCES_ARSC_COMPRESSED = 132742131;

        /**
         * TODO(chiuwinson): This is required because PackageManager#getPackageArchiveInfo
         *   cannot read the targetSdk info from the changeId because it requires the
         *   READ_COMPAT_CHANGE_CONFIG which cannot be obtained automatically without entering the
         *   server process. This should be removed once an alternative is found, or if the API
         *   is removed.
         * @return the targetSdk that this change is gated on (> check), or -1 if disabled
         */
        @IntRange(from = -1, to = Integer.MAX_VALUE)
        public static int getTargetSdkForChange(long changeId) {
            if (changeId == MISSING_APP_TAG
                    || changeId == EMPTY_INTENT_ACTION_CATEGORY
                    || changeId == RESOURCES_ARSC_COMPRESSED) {
                return Build.VERSION_CODES.Q;
            }

            return -1;
        }
    }

    <ResultType> ParseResult<ResultType> success(ResultType result);
+15 −0
Original line number Diff line number Diff line
@@ -64,6 +64,21 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> {
    private String mPackageName;
    private Integer mTargetSdkVersion;

    /**
     * Specifically for {@link PackageManager#getPackageArchiveInfo(String, int)} where
     * {@link IPlatformCompat} cannot be used because the cross-package READ_COMPAT_CHANGE_CONFIG
     * permission cannot be obtained.
     */
    public static ParseTypeImpl forParsingWithoutPlatformCompat() {
        return new ParseTypeImpl((changeId, packageName, targetSdkVersion) -> {
            int gateSdkVersion = DeferredError.getTargetSdkForChange(changeId);
            if (gateSdkVersion == -1) {
                return false;
            }
            return targetSdkVersion > gateSdkVersion;
        });
    }

    /**
     * Assumes {@link Context#PLATFORM_COMPAT_SERVICE} is available to the caller. For use
     * with {@link android.content.pm.parsing.ApkLiteParseUtils} or similar where parsing is