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

Commit c3966611 authored by Akhil Gangu's avatar Akhil Gangu Committed by Android (Google) Code Review
Browse files

Merge "Add parsing support for new purpose declaration manifest elements." into main

parents ccf86e2b d3a000be
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -508,6 +508,21 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
    @SystemApi
    public @NonNull Set<String> knownCerts = Collections.emptySet();

    /**
     * A boolean to signify if purposes are required to be declared in order to use the permission.
     *
     * @hide
     */
    public boolean requiresPurpose;

    /**
     * A {@link Set} of valid purposes defined for using this permission. Apps that request this
     * permission will be required to declare at least one permission from this set.
     *
     * @hide
     */
    public @NonNull Set<String> validPurposes = Collections.emptySet();

    /** @hide */
    public static int fixProtectionLevel(int level) {
        if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
@@ -679,6 +694,8 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
        nonLocalizedDescription = orig.nonLocalizedDescription;
        // Note that knownCerts wasn't properly copied before Android U.
        knownCerts = orig.knownCerts;
        requiresPurpose = orig.requiresPurpose;
        validPurposes = orig.validPurposes;
    }

    /**
@@ -744,6 +761,8 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
        dest.writeInt(requestRes);
        TextUtils.writeToParcel(nonLocalizedDescription, dest, parcelableFlags);
        sForStringSet.parcel(knownCerts, dest, parcelableFlags);
        dest.writeBoolean(requiresPurpose);
        sForStringSet.parcel(validPurposes, dest, parcelableFlags);
    }

    /** @hide */
@@ -805,5 +824,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable {
        requestRes = source.readInt();
        nonLocalizedDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
        knownCerts = sForStringSet.unparcel(source);
        requiresPurpose = source.readBoolean();
        validPurposes = sForStringSet.unparcel(source);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -509,6 +509,8 @@ public class PackageInfoCommonUtils {
        pi.descriptionRes = p.getDescriptionRes();
        pi.flags = p.getFlags();
        pi.knownCerts = p.getKnownCerts();
        pi.requiresPurpose = p.isPurposeRequired();
        pi.validPurposes = p.getValidPurposes();

        if ((flags & PackageManager.GET_META_DATA) == 0) {
            pi.metaData = null;
+49 −4
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.permission.flags.Flags;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -283,6 +284,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
    @NonNull
    private List<ParsedUsesPermission> usesPermissions = emptyList();
    @NonNull
    private Map<String, ParsedUsesPermission> usesPermissionMapping = emptyMap();
    @NonNull
    @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
    private Set<String> implicitPermissions = emptySet();
    @NonNull
@@ -545,7 +548,9 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,

    @Override
    public PackageImpl addImplicitPermission(String permission) {
        addUsesPermission(new ParsedUsesPermissionImpl(permission, 0 /*usesPermissionFlags*/));
        addUsesPermission(
                new ParsedUsesPermissionImpl(
                        permission, /* usesPermissionFlags= */ 0, /* purposes= */ emptySet()));
        this.implicitPermissions = CollectionUtils.add(this.implicitPermissions,
                TextUtils.safeIntern(permission));
        return this;
@@ -722,14 +727,23 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        return this;
    }

    // TODO(419394776) - Use single source of truth for storing uses permission metadata.
    @Override
    public PackageImpl addUsesPermission(ParsedUsesPermission permission) {
        this.usesPermissions = CollectionUtils.add(this.usesPermissions, permission);

        // Continue populating legacy data structures to avoid performance
        // issues until all that code can be migrated
        this.requestedPermissions = CollectionUtils.add(this.requestedPermissions,
                permission.getName());
        this.requestedPermissions =
                CollectionUtils.add(this.requestedPermissions, permission.getName());

        if (Flags.purposeDeclarationEnabled()) {
            // During manifest parsing, we ignore duplicate permission requests. Therefore, it's
            // safe to directly add to the mapping.
            this.usesPermissionMapping =
                    CollectionUtils.add(
                            this.usesPermissionMapping, permission.getName(), permission);
        }

        return this;
    }
@@ -1493,7 +1507,15 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,

    @NonNull
    @Override
    public List<String> getUsesSdkLibraries() { return usesSdkLibraries; }
    public Map<String, ParsedUsesPermission> getUsesPermissionMapping() {
        return usesPermissionMapping;
    }

    @NonNull
    @Override
    public List<String> getUsesSdkLibraries() {
        return usesSdkLibraries;
    }

    @NonNull
    @Override
@@ -2857,6 +2879,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        reqFeatures = Collections.unmodifiableList(reqFeatures);
        featureGroups = Collections.unmodifiableList(featureGroups);
        usesPermissions = Collections.unmodifiableList(usesPermissions);
        usesPermissionMapping = Collections.unmodifiableMap(usesPermissionMapping);
        usesSdkLibraries = Collections.unmodifiableList(usesSdkLibraries);
        implicitPermissions = Collections.unmodifiableSet(implicitPermissions);
        upgradeKeySets = Collections.unmodifiableSet(upgradeKeySets);
@@ -3237,6 +3260,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        sForInternedStringList.parcel(this.adoptPermissions, dest, flags);
        sForInternedStringSet.parcel(this.requestedPermissions, dest, flags);
        ParsingUtils.writeParcelableList(dest, this.usesPermissions);
        writeUsesPermissionMapping(dest);
        sForInternedStringSet.parcel(this.implicitPermissions, dest, flags);
        sForStringSet.parcel(this.upgradeKeySets, dest, flags);
        ParsingPackageUtils.writeKeySetMapping(dest, this.keySetMapping);
@@ -3325,6 +3349,15 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        dest.writeInt(this.mPageSizeAppCompatFlags);
    }

    private void writeUsesPermissionMapping(@NonNull Parcel dest) {
        // No need to deal with null case because the mapping is already instantiated to be empty.
        final Bundle bundle = new Bundle();
        for (Map.Entry<String, ParsedUsesPermission> entry : usesPermissionMapping.entrySet()) {
            bundle.putParcelable(entry.getKey(), (ParsedUsesPermissionImpl) entry.getValue());
        }
        dest.writeBundle(bundle);
    }

    private void writeFeatureFlagState(@NonNull Parcel dest) {
        // Use a string array to encode flag state. One string per flag in the form `<flag>=<value>`
        // where value is 0 (disabled), 1 (enabled) or ? (unknown flag or value).
@@ -3426,6 +3459,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        this.requestedPermissions = sForInternedStringSet.unparcel(in);
        this.usesPermissions = ParsingUtils.createTypedInterfaceList(in,
                ParsedUsesPermissionImpl.CREATOR);
        readUsesPermissionMapping(in);
        this.implicitPermissions = sForInternedStringSet.unparcel(in);
        this.upgradeKeySets = sForStringSet.unparcel(in);
        this.keySetMapping = ParsingPackageUtils.readKeySetMapping(in);
@@ -3526,6 +3560,17 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
        // to mutate this instance before it's finalized.
    }

    private void readUsesPermissionMapping(@NonNull Parcel in) {
        final Bundle bundle = in.readBundle(ParsedUsesPermissionImpl.class.getClassLoader());
        final Map<String, ParsedUsesPermission> mapping = new ArrayMap<>();
        if (bundle != null) {
            for (String key : bundle.keySet()) {
                mapping.put(key, bundle.getParcelable(key, ParsedUsesPermissionImpl.class));
            }
        }
        usesPermissionMapping = mapping;
    }

    private void readFeatureFlagState(@NonNull Parcel in) {
        // See comment in writeFeatureFlagState() for encoding of flag state.
        String[] featureFlagStateAsArray = in.createStringArray();
+37 −1
Original line number Diff line number Diff line
@@ -27,8 +27,11 @@ import android.content.pm.parsing.result.ParseResult;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.permission.flags.Flags;
import android.text.TextUtils;
import android.util.ArraySet;

import com.android.internal.R;
import com.android.internal.pm.pkg.parsing.ParsingPackage;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.pm.pkg.parsing.ParsingUtils;
@@ -38,6 +41,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.util.Set;

/**
 * @hide
@@ -55,6 +59,14 @@ public class ComponentParseUtils {
    static <Component extends ParsedComponentImpl> ParseResult<Component> parseAllMetaData(
            ParsingPackage pkg, Resources res, XmlResourceParser parser, String tag,
            Component component, ParseInput input) throws XmlPullParserException, IOException {
        // Beginning in Android 17, permissions may specify valid usage purposes. Currently, valid
        // purposes are only processed for enforcement if the permission is defined within the
        // Android platform manifest. This limitation might be lifted in future versions.
        final boolean shouldParseValidPurposes =
                Flags.purposeDeclarationEnabled()
                        && component instanceof ParsedPermissionImpl
                        && "android".equals(pkg.getPackageName());
        final Set<String> validPurposes = new ArraySet<>();
        final int depth = parser.getDepth();
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -67,9 +79,16 @@ public class ComponentParseUtils {
                continue;
            }

            final ParseResult result;
            final ParseResult<?> result;
            if ("meta-data".equals(parser.getName())) {
                result = ParsedComponentUtils.addMetaData(component, pkg, res, parser, input);
            } else if (shouldParseValidPurposes && "valid-purpose".equals(parser.getName())) {
                final ParseResult<String> validPurposeResult =
                        parseValidPurpose(res, parser, input);
                result = validPurposeResult;
                if (validPurposeResult.isSuccess() && validPurposeResult.getResult() != null) {
                    validPurposes.add(validPurposeResult.getResult());
                }
            } else {
                result = ParsingUtils.unknownTag(tag, pkg, parser, input);
            }
@@ -79,9 +98,26 @@ public class ComponentParseUtils {
            }
        }

        if (!validPurposes.isEmpty()) {
            // There can only be valid purposes if component is an instance of ParsedPermissionImpl.
            final ParsedPermissionImpl permission = (ParsedPermissionImpl) component;
            permission.setValidPurposes(validPurposes);
        }

        return input.success(component);
    }

    private static ParseResult<String> parseValidPurpose(
            Resources res, XmlResourceParser parser, ParseInput input) {
        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestValidPurpose);
        try {
            final String validPurpose = sa.getString(R.styleable.AndroidManifestValidPurpose_name);
            return input.success(TextUtils.isEmpty(validPurpose) ? null : validPurpose);
        } finally {
            sa.recycle();
        }
    }

    @NonNull
    public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
            CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
+5 −0
Original line number Diff line number Diff line
@@ -41,5 +41,10 @@ public interface ParsedPermission extends ParsedComponent {

    int getRequestRes();

    boolean isPurposeRequired();

    @NonNull
    Set<String> getValidPurposes();

    boolean isTree();
}
Loading