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

Commit 9046d82b authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Allow apps to define featureIds in the manifest

Test: CtsAppOpsTestCases (with newly added test for featureIds declared
      in manifest)
Bug: 144997947
Change-Id: I5563ae6861318e4cc1d196e3f6aea378a4dcf748
parent 2bf1ce3d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -610,6 +610,7 @@ package android {
    field public static final int fastScrollTextColor = 16843609; // 0x1010359
    field public static final int fastScrollThumbDrawable = 16843574; // 0x1010336
    field public static final int fastScrollTrackDrawable = 16843577; // 0x1010339
    field public static final int featureId = 16844301; // 0x101060d
    field public static final int fillAfter = 16843197; // 0x10101bd
    field public static final int fillAlpha = 16843980; // 0x10104cc
    field public static final int fillBefore = 16843196; // 0x10101bc
+1 −0
Original line number Diff line number Diff line
@@ -202,6 +202,7 @@ public class PackageParser {
    public static final String TAG_OVERLAY = "overlay";
    public static final String TAG_PACKAGE = "package";
    public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
    public static final String TAG_FEATURE = "feature";
    public static final String TAG_PERMISSION = "permission";
    public static final String TAG_PERMISSION_GROUP = "permission-group";
    public static final String TAG_PERMISSION_TREE = "permission-tree";
+4 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.content.pm.PackageUserState;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivity;
import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
import android.content.pm.parsing.ComponentParseUtils.ParsedFeature;
import android.content.pm.parsing.ComponentParseUtils.ParsedInstrumentation;
import android.content.pm.parsing.ComponentParseUtils.ParsedPermission;
import android.content.pm.parsing.ComponentParseUtils.ParsedPermissionGroup;
@@ -244,6 +245,9 @@ public interface AndroidPackage extends Parcelable {
    @Nullable
    List<ParsedInstrumentation> getInstrumentations();

    @Nullable
    List<ParsedFeature> getFeatures();

    @Nullable
    List<ParsedPermissionGroup> getPermissionGroups();

+36 −0
Original line number Diff line number Diff line
@@ -793,6 +793,10 @@ public class ApkParseUtils {
                    parseResult = parseKeySets(parseInput, parsingPackage, res, parser);
                    success = parseResult.isSuccess();
                    break;
                case PackageParser.TAG_FEATURE:
                    parseResult = parseFeature(parseInput, parsingPackage, res, parser);
                    success = parseResult.isSuccess();
                    break;
                case PackageParser.TAG_PERMISSION_GROUP:
                    parseResult = parsePermissionGroup(parseInput, parsingPackage, res,
                            parser);
@@ -880,6 +884,13 @@ public class ApkParseUtils {
            );
        }

        if (!ComponentParseUtils.ParsedFeature.isCombinationValid(parsingPackage.getFeatures())) {
            return parseInput.error(
                    INSTALL_PARSE_FAILED_BAD_MANIFEST,
                    "Combination <feature> tags are not valid"
            );
        }

        convertNewPermissions(parsingPackage);

        convertSplitPermissions(parsingPackage);
@@ -1260,6 +1271,31 @@ public class ApkParseUtils {
        return parseInput.success(parsingPackage);
    }

    private static ParseResult parseFeature(
            ParseInput parseInput,
            ParsingPackage parsingPackage,
            Resources res,
            XmlResourceParser parser
    ) throws IOException, XmlPullParserException {
        // TODO(b/135203078): Remove, replace with ParseResult
        String[] outError = new String[1];

        ComponentParseUtils.ParsedFeature parsedFeature =
                ComponentParseUtils.parseFeature(res, parser, outError);

        if (parsedFeature == null || outError[0] != null) {
            return parseInput.error(
                    PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    outError[0]
            );
        }

        parsingPackage.addFeature(parsedFeature);

        return parseInput.success(parsingPackage);
    }


    private static ParseResult parsePermissionGroup(
            ParseInput parseInput,
            ParsingPackage parsingPackage,
+250 −0
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;

import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.UnsupportedAppUsage;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
@@ -47,6 +50,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
@@ -54,6 +58,7 @@ import android.util.TypedValue;
import android.view.Gravity;

import com.android.internal.R;
import com.android.internal.util.DataClass;
import com.android.internal.util.XmlUtils;

import org.xmlpull.v1.XmlPullParser;
@@ -62,6 +67,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -931,6 +937,176 @@ public class ComponentParseUtils {
        };
    }

    /**
     * A {@link android.R.styleable#AndroidManifestFeature &lt;feature&gt;} tag parsed from the
     * manifest.
     */
    // @DataClass verifier is broken, hence comment out for now
    public static class ParsedFeature implements Parcelable {
        /** Id of the feature */
        public final @NonNull String id;

        /** User visible label fo the feature */
        public final @StringRes int label;

        /** Ids of previously declared features this feature inherits from */
        public final @NonNull List<String> inheritFrom;

        /**
         * @return Is this set of features a valid combination for a single package?
         */
        public static boolean isCombinationValid(@Nullable List<ParsedFeature> features) {
            if (features == null) {
                return true;
            }

            ArraySet<String> featureIds = new ArraySet<>(features.size());
            ArraySet<String> inheritFromFeatureIds = new ArraySet<>();

            int numFeatures = features.size();
            for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
                boolean wasAdded = featureIds.add(features.get(featureNum).id);
                if (!wasAdded) {
                    // feature id is not unique
                    return false;
                }
            }

            for (int featureNum = 0; featureNum < numFeatures; featureNum++) {
                ParsedFeature feature = features.get(featureNum);

                int numInheritFrom = feature.inheritFrom.size();
                for (int inheritFromNum = 0; inheritFromNum < numInheritFrom; inheritFromNum++) {
                    String inheritFrom = feature.inheritFrom.get(inheritFromNum);

                    if (featureIds.contains(inheritFrom)) {
                        // Cannot inherit from a feature that is still defined
                        return false;
                    }

                    boolean wasAdded = inheritFromFeatureIds.add(inheritFrom);
                    if (!wasAdded) {
                        // inheritFrom is not unique
                        return false;
                    }
                }
            }

            return true;
        }



        // Code below generated by codegen v1.0.14.
        //
        // DO NOT MODIFY!
        // CHECKSTYLE:OFF Generated code
        //
        // To regenerate run:
        // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java
        //
        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
        //   Settings > Editor > Code Style > Formatter Control
        //@formatter:off


        /**
         * Creates a new ParsedFeature.
         *
         * @param id
         *   Id of the feature
         * @param label
         *   User visible label fo the feature (if defined as resource)
         * @param inheritFrom
         *   Ids of previously declared features this feature inherits from
         */
        @DataClass.Generated.Member
        public ParsedFeature(
                @NonNull String id,
                @StringRes int label,
                @NonNull List<String> inheritFrom) {
            this.id = id;
            com.android.internal.util.AnnotationValidations.validate(
                    NonNull.class, null, id);
            this.label = label;
            com.android.internal.util.AnnotationValidations.validate(
                    StringRes.class, null, label);
            this.inheritFrom = inheritFrom;
            com.android.internal.util.AnnotationValidations.validate(
                    NonNull.class, null, inheritFrom);

            // onConstructed(); // You can define this method to get a callback
        }

        @Override
        @DataClass.Generated.Member
        public void writeToParcel(@NonNull Parcel dest, int flags) {
            // You can override field parcelling by defining methods like:
            // void parcelFieldName(Parcel dest, int flags) { ... }

            dest.writeString(id);
            dest.writeInt(label);
            dest.writeStringList(inheritFrom);
        }

        @Override
        @DataClass.Generated.Member
        public int describeContents() { return 0; }

        /** @hide */
        @SuppressWarnings({"unchecked", "RedundantCast"})
        @DataClass.Generated.Member
        protected ParsedFeature(@NonNull Parcel in) {
            // You can override field unparcelling by defining methods like:
            // static FieldType unparcelFieldName(Parcel in) { ... }

            String _id = in.readString();
            int _label = in.readInt();
            List<String> _inheritFrom = new ArrayList<>();
            in.readStringList(_inheritFrom);

            this.id = _id;
            com.android.internal.util.AnnotationValidations.validate(
                    NonNull.class, null, id);
            this.label = _label;
            com.android.internal.util.AnnotationValidations.validate(
                    StringRes.class, null, label);
            this.inheritFrom = _inheritFrom;
            com.android.internal.util.AnnotationValidations.validate(
                    NonNull.class, null, inheritFrom);

            // onConstructed(); // You can define this method to get a callback
        }

        @DataClass.Generated.Member
        public static final @NonNull Parcelable.Creator<ParsedFeature> CREATOR
                = new Parcelable.Creator<ParsedFeature>() {
            @Override
            public ParsedFeature[] newArray(int size) {
                return new ParsedFeature[size];
            }

            @Override
            public ParsedFeature createFromParcel(@NonNull Parcel in) {
                return new ParsedFeature(in);
            }
        };

        /*@DataClass.Generated(
                time = 1576783172965L,
                codegenVersion = "1.0.14",
                sourceFile = "frameworks/base/core/java/android/content/pm/parsing/ComponentParseUtils.java",
                inputSignatures = "public final @android.annotation.NonNull java.lang.String id\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.ParsedFeature>)\nclass ParsedFeature extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
         */
        @Deprecated
        private void __metadata() {}


        //@formatter:on
        // End of generated code

    }

    public static class ParsedPermission extends ParsedComponent<ParsedIntentInfo> {

        public String backgroundPermission;
@@ -2566,6 +2742,80 @@ public class ComponentParseUtils {
        return result;
    }

    public static ParsedFeature parseFeature(
            Resources res,
            XmlResourceParser parser,
            String[] outError
    ) throws IOException, XmlPullParserException {
        String featureId;
        int label;
        List<String> inheritFrom = null;

        TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature);
        if (sa == null) {
            outError[0] = "<feature> could not be parsed";
            return null;
        }

        try {
            featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId,
                    0);
            if (featureId == null) {
                outError[0] = "<featureId> does not specify android:featureId";
                return null;
            }

            label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0);
            if (label == Resources.ID_NULL) {
                outError[0] = "<featureId> does not specify android:label";
                return null;
            }
        } finally {
            sa.recycle();
        }

        int type;
        final int innerDepth = parser.getDepth();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            String tagName = parser.getName();
            if (tagName.equals("inherit-from")) {
                sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeatureInheritFrom);
                if (sa == null) {
                    outError[0] = "<inherit-from> could not be parsed";
                    return null;
                }

                try {
                    String inheritFromId = sa.getNonConfigurationString(
                            R.styleable.AndroidManifestFeatureInheritFrom_featureId,0);

                    if (inheritFrom == null) {
                        inheritFrom = new ArrayList<>();
                    }
                    inheritFrom.add(inheritFromId);
                } finally {
                    sa.recycle();
                }
            } else {
                outError[0] = "Bad element under <feature>: " + tagName;
                return null;
            }
        }

        if (inheritFrom == null) {
            inheritFrom = Collections.emptyList();
        } else {
            ((ArrayList) inheritFrom).trimToSize();
        }

        return new ParsedFeature(featureId, label, inheritFrom);
    }

    public static ParsedPermission parsePermission(
            ParsingPackage parsingPackage,
            Resources res,
Loading