From 9046d82b8f6251508942aa74765a41118084f3f9 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Fri, 13 Dec 2019 15:59:49 -0800 Subject: [PATCH 1/3] Allow apps to define featureIds in the manifest Test: CtsAppOpsTestCases (with newly added test for featureIds declared in manifest) Bug: 144997947 Change-Id: I5563ae6861318e4cc1d196e3f6aea378a4dcf748 --- api/current.txt | 1 + .../android/content/pm/PackageParser.java | 1 + .../content/pm/parsing/AndroidPackage.java | 4 + .../content/pm/parsing/ApkParseUtils.java | 36 ++ .../pm/parsing/ComponentParseUtils.java | 250 +++++++++++++ .../content/pm/parsing/PackageImpl.java | 18 + .../content/pm/parsing/ParsingPackage.java | 3 + core/res/res/values/attrs_manifest.xml | 26 +- core/res/res/values/public.xml | 6 +- .../android/server/appop/AppOpsService.java | 344 +++++++++++++----- .../server/pm/PackageManagerService.java | 6 + .../server/appop/AppOpsServiceTest.java | 12 +- tools/aapt2/link/ManifestFixer.cpp | 2 + 13 files changed, 616 insertions(+), 93 deletions(-) diff --git a/api/current.txt b/api/current.txt index c734db5196a90..f6aa0a316b707 100644 --- a/api/current.txt +++ b/api/current.txt @@ -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 diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 47edf2e6fd3ae..32803ab8f859b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -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"; diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java index 0562dffc551ec..990c8359e6988 100644 --- a/core/java/android/content/pm/parsing/AndroidPackage.java +++ b/core/java/android/content/pm/parsing/AndroidPackage.java @@ -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 getInstrumentations(); + @Nullable + List getFeatures(); + @Nullable List getPermissionGroups(); diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java index 3f22967298913..a001ada8df4a2 100644 --- a/core/java/android/content/pm/parsing/ApkParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkParseUtils.java @@ -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 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, diff --git a/core/java/android/content/pm/parsing/ComponentParseUtils.java b/core/java/android/content/pm/parsing/ComponentParseUtils.java index fc210b2660401..88e98da510ff6 100644 --- a/core/java/android/content/pm/parsing/ComponentParseUtils.java +++ b/core/java/android/content/pm/parsing/ComponentParseUtils.java @@ -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 <feature>} 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 inheritFrom; + + /** + * @return Is this set of features a valid combination for a single package? + */ + public static boolean isCombinationValid(@Nullable List features) { + if (features == null) { + return true; + } + + ArraySet featureIds = new ArraySet<>(features.size()); + ArraySet 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 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 _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 CREATOR + = new Parcelable.Creator() { + @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 inheritFrom\npublic static boolean isCombinationValid(java.util.List)\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 { 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 inheritFrom = null; + + TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestFeature); + if (sa == null) { + outError[0] = " could not be parsed"; + return null; + } + + try { + featureId = sa.getNonConfigurationString(R.styleable.AndroidManifestFeature_featureId, + 0); + if (featureId == null) { + outError[0] = " does not specify android:featureId"; + return null; + } + + label = sa.getResourceId(R.styleable.AndroidManifestFeature_label, 0); + if (label == Resources.ID_NULL) { + outError[0] = " 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] = " 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 : " + 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, diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java index 18dee23dc6909..8677fced18fa6 100644 --- a/core/java/android/content/pm/parsing/PackageImpl.java +++ b/core/java/android/content/pm/parsing/PackageImpl.java @@ -36,6 +36,7 @@ import android.content.pm.ServiceInfo; 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.ParsedIntentInfo; import android.content.pm.parsing.ComponentParseUtils.ParsedPermission; @@ -174,6 +175,9 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android @Nullable private ArrayList providers; + @Nullable + private ArrayList features; + @Nullable private ArrayList permissions; @@ -580,6 +584,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android return permissions; } + @Nullable + @Override + public List getFeatures() { + return features; + } + @Override public String getCpuAbiOverride() { return cpuAbiOverride; @@ -791,6 +801,12 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android return this; } + @Override + public PackageImpl addFeature(ParsedFeature feature) { + this.features = ArrayUtils.add(this.features, feature); + return this; + } + @Override public PackageImpl addPermission(ParsedPermission permission) { this.permissions = ArrayUtils.add(this.permissions, permission); @@ -3021,6 +3037,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android dest.writeTypedList(this.receivers); dest.writeTypedList(this.services); dest.writeTypedList(this.providers); + dest.writeTypedList(this.features); dest.writeTypedList(this.permissions); dest.writeTypedList(this.permissionGroups); dest.writeTypedList(this.instrumentations); @@ -3173,6 +3190,7 @@ public final class PackageImpl implements ParsingPackage, ParsedPackage, Android this.receivers = in.createTypedArrayList(ParsedActivity.CREATOR); this.services = in.createTypedArrayList(ParsedService.CREATOR); this.providers = in.createTypedArrayList(ParsedProvider.CREATOR); + this.features = in.createTypedArrayList(ParsedFeature.CREATOR); this.permissions = in.createTypedArrayList(ParsedPermission.CREATOR); this.permissionGroups = in.createTypedArrayList(ParsedPermissionGroup.CREATOR); this.instrumentations = in.createTypedArrayList(ParsedInstrumentation.CREATOR); diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java index 47dac5584c894..411c749915947 100644 --- a/core/java/android/content/pm/parsing/ParsingPackage.java +++ b/core/java/android/content/pm/parsing/ParsingPackage.java @@ -24,6 +24,7 @@ import android.content.pm.FeatureInfo; import android.content.pm.PackageParser; 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; @@ -64,6 +65,8 @@ public interface ParsingPackage extends AndroidPackage { ParsingPackage addOverlayable(String overlayableName, String actorName); + ParsingPackage addFeature(ParsedFeature permission); + ParsingPackage addPermission(ParsedPermission permission); ParsingPackage addPermissionGroup(ParsedPermissionGroup permissionGroup); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 85f7d617352e0..6435cddebd1ee 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1533,7 +1533,8 @@ com.google.app.appname

Inside of the manifest tag, may appear the following tags - in any order: {@link #AndroidManifestPermission permission}, + in any order: {@link #AndroidManifestFeature feature}, + {@link #AndroidManifestPermission permission}, {@link #AndroidManifestPermissionGroup permission-group}, {@link #AndroidManifestPermissionTree permission-tree}, {@link #AndroidManifestUsesSdk uses-sdk}, @@ -1763,6 +1764,29 @@ The default value is {@code false}. --> + + + + + + + + + + + + + + +