Parse <property> tags
parse <property> tags defined on various components [application,
activity, activity alias, service, provider, and receiver]
Bug: 169258655
Test: atest FrameworksServicesTests:PackageParserTest
Change-Id: I4c94056db3909cc28636a9f93d875eff3d593f79
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 9cda4d3..8147599 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -23,6 +23,7 @@
import android.content.pm.ConfigurationInfo;
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
+import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
@@ -75,6 +76,9 @@
ParsingPackage addPreferredActivityFilter(String className, ParsedIntentInfo intentInfo);
+ /** Add a property to the application scope */
+ ParsingPackage addProperty(Property property);
+
ParsingPackage addProtectedBroadcast(String protectedBroadcast);
ParsingPackage addProvider(ParsedProvider parsedProvider);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 2a15e02..b826b7a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -31,6 +31,7 @@
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedAttribution;
@@ -273,6 +274,9 @@
@Nullable
private Bundle metaData;
+ @NonNull
+ private Map<String, Property> mProperties = emptyMap();
+
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
protected String volumeUuid;
@@ -623,6 +627,15 @@
}
@Override
+ public ParsingPackageImpl addProperty(@Nullable Property property) {
+ if (property == null) {
+ return this;
+ }
+ this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
+ return this;
+ }
+
+ @Override
public ParsingPackageImpl addProtectedBroadcast(String protectedBroadcast) {
if (!this.protectedBroadcasts.contains(protectedBroadcast)) {
this.protectedBroadcasts = CollectionUtils.add(this.protectedBroadcasts,
@@ -1172,6 +1185,7 @@
dest.writeInt(this.gwpAsanMode);
dest.writeSparseIntArray(this.minExtensionVersions);
dest.writeLong(this.mBooleans);
+ dest.writeMap(this.mProperties);
}
public ParsingPackageImpl(Parcel in) {
@@ -1290,7 +1304,7 @@
this.gwpAsanMode = in.readInt();
this.minExtensionVersions = in.readSparseIntArray();
this.mBooleans = in.readLong();
-
+ this.mProperties = in.createTypedArrayMap(Property.CREATOR);
assignDerivedFields();
}
@@ -1528,6 +1542,12 @@
@NonNull
@Override
+ public Map<String, Property> getProperties() {
+ return mProperties;
+ }
+
+ @NonNull
+ @Override
public Set<String> getUpgradeKeySets() {
return upgradeKeySets;
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index acd6305..13ae7a2 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -25,6 +25,7 @@
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.ServiceInfo;
import android.content.pm.parsing.component.ParsedActivity;
@@ -43,8 +44,6 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
-import com.android.internal.R;
-
import java.security.PublicKey;
import java.util.List;
import java.util.Map;
@@ -204,6 +203,12 @@
List<String> getRequestedPermissions();
/**
+ * Returns the properties set on the application
+ */
+ @NonNull
+ Map<String, Property> getProperties();
+
+ /**
* Whether or not the app requested explicitly resizeable Activities.
* A null value means nothing was explicitly requested.
*/
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 6196854..d6c895a 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -42,6 +42,7 @@
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.SigningDetails;
@@ -699,12 +700,19 @@
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
- ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
- pkg.getMetaData(), input);
- if (metaDataResult.isSuccess()) {
- pkg.setMetaData(metaDataResult.getResult());
+ ParseResult<Property> metaDataResult = parseMetaData(pkg, res, parser,
+ "<meta-data>", input);
+ if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
+ pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
}
return metaDataResult;
+ case "property":
+ ParseResult<Property> propertyResult = parseMetaData(pkg, res, parser,
+ "<property>", input);
+ if (propertyResult.isSuccess()) {
+ pkg.addProperty(propertyResult.getResult());
+ }
+ return propertyResult;
case "uses-static-library":
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
@@ -2085,13 +2093,19 @@
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
- ParseResult<Bundle> metaDataResult = parseMetaData(pkg, res, parser,
- pkg.getMetaData(), input);
- if (metaDataResult.isSuccess()) {
- pkg.setMetaData(metaDataResult.getResult());
+ final ParseResult<Property> metaDataResult = parseMetaData(pkg, res, parser,
+ "<meta-data>", input);
+ if (metaDataResult.isSuccess() && metaDataResult.getResult() != null) {
+ pkg.setMetaData(metaDataResult.getResult().toBundle(pkg.getMetaData()));
}
-
return metaDataResult;
+ case "property":
+ final ParseResult<Property> propertyResult = parseMetaData(pkg, res, parser,
+ "<property>", input);
+ if (propertyResult.isSuccess()) {
+ pkg.addProperty(propertyResult.getResult());
+ }
+ return propertyResult;
case "static-library":
return parseStaticLibrary(pkg, res, parser, input);
case "library":
@@ -2736,57 +2750,58 @@
: input.error("must have at least one '.' separator");
}
- public static ParseResult<Bundle> parseMetaData(ParsingPackage pkg, Resources res,
- XmlResourceParser parser, Bundle data, ParseInput input) {
+ /**
+ * Parse a meta data defined on the enclosing tag.
+ * <p>Meta data can be defined by either <meta-data> or <property> elements.
+ */
+ public static ParseResult<Property> parseMetaData(ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, String tagName, ParseInput input) {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestMetaData);
try {
- if (data == null) {
- data = new Bundle();
- }
-
- String name = TextUtils.safeIntern(
+ final Property property;
+ final String name = TextUtils.safeIntern(
nonConfigString(0, R.styleable.AndroidManifestMetaData_name, sa));
if (name == null) {
- return input.error("<meta-data> requires an android:name attribute");
+ return input.error(tagName + " requires an android:name attribute");
}
TypedValue v = sa.peekValue(R.styleable.AndroidManifestMetaData_resource);
if (v != null && v.resourceId != 0) {
- //Slog.i(TAG, "Meta data ref " + name + ": " + v);
- data.putInt(name, v.resourceId);
+ property = new Property(name, v.resourceId, true);
} else {
v = sa.peekValue(R.styleable.AndroidManifestMetaData_value);
- //Slog.i(TAG, "Meta data " + name + ": " + v);
if (v != null) {
if (v.type == TypedValue.TYPE_STRING) {
- CharSequence cs = v.coerceToString();
- data.putString(name, cs != null ? cs.toString() : null);
+ final CharSequence cs = v.coerceToString();
+ final String stringValue = cs != null ? cs.toString() : null;
+ property = new Property(name, stringValue);
} else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
- data.putBoolean(name, v.data != 0);
+ property = new Property(name, v.data != 0);
} else if (v.type >= TypedValue.TYPE_FIRST_INT
&& v.type <= TypedValue.TYPE_LAST_INT) {
- data.putInt(name, v.data);
+ property = new Property(name, v.data, false);
} else if (v.type == TypedValue.TYPE_FLOAT) {
- data.putFloat(name, v.getFloat());
+ property = new Property(name, v.getFloat());
} else {
if (!PackageParser.RIGID_PARSER) {
Slog.w(TAG,
- "<meta-data> only supports string, integer, float, color, "
+ tagName + " only supports string, integer, float, color, "
+ "boolean, and resource reference types: "
+ parser.getName() + " at "
+ pkg.getBaseApkPath() + " "
+ parser.getPositionDescription());
+ property = null;
} else {
- return input.error("<meta-data> only supports string, integer, float, "
+ return input.error(tagName + " only supports string, integer, float, "
+ "color, boolean, and resource reference types");
}
}
} else {
- return input.error("<meta-data> requires an android:value "
+ return input.error(tagName + " requires an android:value "
+ "or android:resource attribute");
}
}
- return input.success(data);
+ return input.success(property);
} finally {
sa.recycle();
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index 511ee5d..0d7198d 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -356,6 +356,8 @@
result = intentResult;
} else if (parser.getName().equals("meta-data")) {
result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
+ } else if (parser.getName().equals("property")) {
+ result = ParsedComponentUtils.addProperty(activity, pkg, resources, parser, input);
} else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponent.java b/core/java/android/content/pm/parsing/component/ParsedComponent.java
index 6323d69..4aed77a 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponent.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponent.java
@@ -18,10 +18,13 @@
import static android.content.pm.parsing.ParsingPackageImpl.sForInternedString;
+import static java.util.Collections.emptyMap;
+
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
+import android.content.pm.PackageManager.Property;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -35,6 +38,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
/** @hide */
public abstract class ParsedComponent implements Parcelable {
@@ -70,6 +74,8 @@
@Nullable
protected Bundle metaData;
+ private Map<String, Property> mProperties = emptyMap();
+
ParsedComponent() {
}
@@ -96,6 +102,11 @@
this.intents = CollectionUtils.add(this.intents, intent);
}
+ /** Add a property to the component */
+ public void addProperty(@NonNull Property property) {
+ this.mProperties = CollectionUtils.add(this.mProperties, property.getName(), property);
+ }
+
@NonNull
public List<ParsedIntentInfo> getIntents() {
return intents != null ? intents : Collections.emptyList();
@@ -142,6 +153,7 @@
sForInternedString.parcel(this.packageName, dest, flags);
sForIntentInfos.parcel(this.getIntents(), dest, flags);
dest.writeBundle(this.metaData);
+ dest.writeMap(this.mProperties);
}
protected ParsedComponent(Parcel in) {
@@ -160,6 +172,7 @@
this.packageName = sForInternedString.unparcel(in);
this.intents = sForIntentInfos.unparcel(in);
this.metaData = in.readBundle(boot);
+ this.mProperties = in.createTypedArrayMap(Property.CREATOR);
}
@NonNull
@@ -205,4 +218,9 @@
public Bundle getMetaData() {
return metaData;
}
+
+ @NonNull
+ public Map<String, Property> getProperties() {
+ return mProperties;
+ }
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
index dd2fb5b..1138dd3 100644
--- a/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedComponentUtils.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.Property;
import android.content.pm.parsing.ParsingPackage;
import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
@@ -97,13 +98,29 @@
static ParseResult<Bundle> addMetaData(ParsedComponent component, ParsingPackage pkg,
Resources resources, XmlResourceParser parser, ParseInput input) {
- ParseResult<Bundle> result = ParsingPackageUtils.parseMetaData(pkg, resources,
- parser, component.metaData, input);
+ ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, resources,
+ parser, "<meta-data>", input);
if (result.isError()) {
return input.error(result);
}
- Bundle bundle = result.getResult();
- component.metaData = bundle;
- return input.success(bundle);
+ final Property property = result.getResult();
+ if (property != null) {
+ component.metaData = property.toBundle(component.metaData);
+ }
+ return input.success(component.metaData);
+ }
+
+ static ParseResult<Property> addProperty(ParsedComponent component, ParsingPackage pkg,
+ Resources resources, XmlResourceParser parser, ParseInput input) {
+ ParseResult<Property> result = ParsingPackageUtils.parseMetaData(pkg, resources,
+ parser, "<property>", input);
+ if (result.isError()) {
+ return input.error(result);
+ }
+ final Property property = result.getResult();
+ if (property != null) {
+ component.addProperty(property);
+ }
+ return input.success(property);
}
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
index 37cbeca..90691f1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedProviderUtils.java
@@ -180,6 +180,9 @@
case "meta-data":
result = ParsedComponentUtils.addMetaData(provider, pkg, res, parser, input);
break;
+ case "property":
+ result = ParsedComponentUtils.addProperty(provider, pkg, res, parser, input);
+ break;
case "grant-uri-permission": {
result = parseGrantUriPermission(provider, pkg, res, parser, input);
break;
diff --git a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
index afe3c54..739bee2 100644
--- a/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedServiceUtils.java
@@ -145,6 +145,10 @@
case "meta-data":
parseResult = ParsedComponentUtils.addMetaData(service, pkg, res, parser, input);
break;
+ case "property":
+ parseResult =
+ ParsedComponentUtils.addProperty(service, pkg, res, parser, input);
+ break;
default:
parseResult = ParsingUtils.unknownTag(tag, pkg, parser, input);
break;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 1f72374..f2bb91c 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -103,6 +103,7 @@
":PackageParserTestApp1",
":PackageParserTestApp2",
":PackageParserTestApp3",
+ ":PackageParserTestApp4",
":apex.test",
],
resource_zips: [":FrameworksServicesTests_apks_as_resources"],
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 7694d09..90c2982 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -21,7 +21,9 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static java.lang.Boolean.TRUE;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.annotation.NonNull;
@@ -32,6 +34,7 @@
import android.content.pm.FeatureGroupInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.Property;
import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.ServiceInfo;
@@ -81,7 +84,9 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
@Presubmit
@@ -99,6 +104,8 @@
private static final String TEST_APP1_APK = "PackageParserTestApp1.apk";
private static final String TEST_APP2_APK = "PackageParserTestApp2.apk";
private static final String TEST_APP3_APK = "PackageParserTestApp3.apk";
+ private static final String TEST_APP4_APK = "PackageParserTestApp4.apk";
+ private static final String PACKAGE_NAME = "com.android.servicestests.apps.packageparserapp";
@Before
public void setUp() throws IOException {
@@ -270,6 +277,234 @@
}
}
+ private static final int PROPERTY_TYPE_BOOLEAN = 1;
+ private static final int PROPERTY_TYPE_FLOAT = 2;
+ private static final int PROPERTY_TYPE_INTEGER = 3;
+ private static final int PROPERTY_TYPE_RESOURCE = 4;
+ private static final int PROPERTY_TYPE_STRING = 5;
+ public void assertProperty(Map<String, Property> properties, String propertyName,
+ int propertyType, Object propertyValue) {
+ assertTrue(properties.containsKey(propertyName));
+
+ final Property testProperty = properties.get(propertyName);
+ assertEquals(propertyType, testProperty.getType());
+
+ if (propertyType == PROPERTY_TYPE_BOOLEAN) {
+ assertTrue(testProperty.isBoolean());
+ assertFalse(testProperty.isFloat());
+ assertFalse(testProperty.isInteger());
+ assertFalse(testProperty.isResourceId());
+ assertFalse(testProperty.isString());
+
+ // assert the property's type is set correctly
+ final Boolean boolValue = (Boolean) propertyValue;
+ if (boolValue.booleanValue()) {
+ assertTrue(testProperty.getBoolean());
+ } else {
+ assertFalse(testProperty.getBoolean());
+ }
+ // assert the other values have an appropriate default
+ assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+ assertEquals(0, testProperty.getInteger());
+ assertEquals(0, testProperty.getResourceId());
+ assertEquals(null, testProperty.getString());
+ } else if (propertyType == PROPERTY_TYPE_FLOAT) {
+ assertFalse(testProperty.isBoolean());
+ assertTrue(testProperty.isFloat());
+ assertFalse(testProperty.isInteger());
+ assertFalse(testProperty.isResourceId());
+ assertFalse(testProperty.isString());
+
+ // assert the property's type is set correctly
+ final Float floatValue = (Float) propertyValue;
+ assertEquals(floatValue.floatValue(), testProperty.getFloat(), 0.0f);
+ // assert the other values have an appropriate default
+ assertFalse(testProperty.getBoolean());
+ assertEquals(0, testProperty.getInteger());
+ assertEquals(0, testProperty.getResourceId());
+ assertEquals(null, testProperty.getString());
+ } else if (propertyType == PROPERTY_TYPE_INTEGER) {
+ assertFalse(testProperty.isBoolean());
+ assertFalse(testProperty.isFloat());
+ assertTrue(testProperty.isInteger());
+ assertFalse(testProperty.isResourceId());
+ assertFalse(testProperty.isString());
+
+ // assert the property's type is set correctly
+ final Integer integerValue = (Integer) propertyValue;
+ assertEquals(integerValue.intValue(), testProperty.getInteger());
+ // assert the other values have an appropriate default
+ assertFalse(testProperty.getBoolean());
+ assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+ assertEquals(0, testProperty.getResourceId());
+ assertEquals(null, testProperty.getString());
+ } else if (propertyType == PROPERTY_TYPE_RESOURCE) {
+ assertFalse(testProperty.isBoolean());
+ assertFalse(testProperty.isFloat());
+ assertFalse(testProperty.isInteger());
+ assertTrue(testProperty.isResourceId());
+ assertFalse(testProperty.isString());
+
+ // assert the property's type is set correctly
+ final Integer resourceValue = (Integer) propertyValue;
+ assertEquals(resourceValue.intValue(), testProperty.getResourceId());
+ // assert the other values have an appropriate default
+ assertFalse(testProperty.getBoolean());
+ assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+ assertEquals(0, testProperty.getInteger());
+ assertEquals(null, testProperty.getString());
+ } else if (propertyType == PROPERTY_TYPE_STRING) {
+ assertFalse(testProperty.isBoolean());
+ assertFalse(testProperty.isFloat());
+ assertFalse(testProperty.isInteger());
+ assertFalse(testProperty.isResourceId());
+ assertTrue(testProperty.isString());
+
+ // assert the property's type is set correctly
+ final String stringValue = (String) propertyValue;
+ assertEquals(stringValue, testProperty.getString());
+ // assert the other values have an appropriate default
+ assertFalse(testProperty.getBoolean());
+ assertEquals(0.0f, testProperty.getFloat(), 0.0f);
+ assertEquals(0, testProperty.getInteger());
+ assertEquals(0, testProperty.getResourceId());
+ } else {
+ fail("Unknown property type");
+ }
+ }
+
+ @Test
+ public void testParseApplicationProperties() throws Exception {
+ final File testFile = extractFile(TEST_APP4_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+ final Map<String, Property> properties = pkg.getProperties();
+ assertEquals(10, properties.size());
+ assertProperty(properties,
+ "android.cts.PROPERTY_RESOURCE_XML", PROPERTY_TYPE_RESOURCE, 0x7f060000);
+ assertProperty(properties,
+ "android.cts.PROPERTY_RESOURCE_INTEGER", PROPERTY_TYPE_RESOURCE, 0x7f040000);
+ assertProperty(properties,
+ "android.cts.PROPERTY_BOOLEAN", PROPERTY_TYPE_BOOLEAN, TRUE);
+ assertProperty(properties,
+ "android.cts.PROPERTY_BOOLEAN_VIA_RESOURCE", PROPERTY_TYPE_BOOLEAN, TRUE);
+ assertProperty(properties,
+ "android.cts.PROPERTY_FLOAT", PROPERTY_TYPE_FLOAT, 3.14f);
+ assertProperty(properties,
+ "android.cts.PROPERTY_FLOAT_VIA_RESOURCE", PROPERTY_TYPE_FLOAT, 2.718f);
+ assertProperty(properties,
+ "android.cts.PROPERTY_INTEGER", PROPERTY_TYPE_INTEGER, 42);
+ assertProperty(properties,
+ "android.cts.PROPERTY_INTEGER_VIA_RESOURCE", PROPERTY_TYPE_INTEGER, 123);
+ assertProperty(properties,
+ "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala");
+ assertProperty(properties,
+ "android.cts.PROPERTY_STRING_VIA_RESOURCE", PROPERTY_TYPE_STRING, "giraffe");
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ public void testParseActivityProperties() throws Exception {
+ final File testFile = extractFile(TEST_APP4_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+ final List<ParsedActivity> activities = pkg.getActivities();
+ for (ParsedActivity activity : activities) {
+ final Map<String, Property> properties = activity.getProperties();
+ if ((PACKAGE_NAME + ".MyActivityAlias").equals(activity.getName())) {
+ assertEquals(2, properties.size());
+ assertProperty(properties,
+ "android.cts.PROPERTY_ACTIVITY_ALIAS", PROPERTY_TYPE_INTEGER, 123);
+ assertProperty(properties,
+ "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_INTEGER, 123);
+ } else if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
+ assertEquals(3, properties.size());
+ assertProperty(properties,
+ "android.cts.PROPERTY_ACTIVITY", PROPERTY_TYPE_INTEGER, 123);
+ assertProperty(properties,
+ "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_INTEGER, 123);
+ assertProperty(properties,
+ "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala activity");
+ } else if ("android.app.AppDetailsActivity".equals(activity.getName())) {
+ // ignore default added activity
+ } else {
+ fail("Found unknown activity; name = " + activity.getName());
+ }
+ }
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ public void testParseProviderProperties() throws Exception {
+ final File testFile = extractFile(TEST_APP4_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+ final List<ParsedProvider> providers = pkg.getProviders();
+ for (ParsedProvider provider : providers) {
+ final Map<String, Property> properties = provider.getProperties();
+ if ((PACKAGE_NAME + ".MyProvider").equals(provider.getName())) {
+ assertEquals(1, properties.size());
+ assertProperty(properties,
+ "android.cts.PROPERTY_PROVIDER", PROPERTY_TYPE_INTEGER, 123);
+ } else {
+ fail("Found unknown provider; name = " + provider.getName());
+ }
+ }
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ public void testParseReceiverProperties() throws Exception {
+ final File testFile = extractFile(TEST_APP4_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+ final List<ParsedActivity> receivers = pkg.getReceivers();
+ for (ParsedActivity receiver : receivers) {
+ final Map<String, Property> properties = receiver.getProperties();
+ if ((PACKAGE_NAME + ".MyReceiver").equals(receiver.getName())) {
+ assertEquals(2, properties.size());
+ assertProperty(properties,
+ "android.cts.PROPERTY_RECEIVER", PROPERTY_TYPE_INTEGER, 123);
+ assertProperty(properties,
+ "android.cts.PROPERTY_STRING", PROPERTY_TYPE_STRING, "koala receiver");
+ } else {
+ fail("Found unknown receiver; name = " + receiver.getName());
+ }
+ }
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ @Test
+ public void testParseServiceProperties() throws Exception {
+ final File testFile = extractFile(TEST_APP4_APK);
+ try {
+ final ParsedPackage pkg = new TestPackageParser2().parsePackage(testFile, 0, false);
+ final List<ParsedService> services = pkg.getServices();
+ for (ParsedService service : services) {
+ final Map<String, Property> properties = service.getProperties();
+ if ((PACKAGE_NAME + ".MyService").equals(service.getName())) {
+ assertEquals(2, properties.size());
+ assertProperty(properties,
+ "android.cts.PROPERTY_SERVICE", PROPERTY_TYPE_INTEGER, 123);
+ assertProperty(properties,
+ "android.cts.PROPERTY_COMPONENT", PROPERTY_TYPE_RESOURCE, 0x7f040000);
+ } else {
+ fail("Found unknown service; name = " + service.getName());
+ }
+ }
+ } finally {
+ testFile.delete();
+ }
+ }
+
/**
* A trivial subclass of package parser that only caches the package name, and throws away
* all other information.
@@ -386,6 +621,13 @@
b.getInstrumentations().get(i));
}
+ assertEquals(a.getProperties().size(), b.getProperties().size());
+ final Iterator<String> iter = a.getProperties().keySet().iterator();
+ while (iter.hasNext()) {
+ final String key = iter.next();
+ assertEquals(a.getProperties().get(key), b.getProperties().get(key));
+ }
+
assertEquals(a.getRequestedPermissions(), b.getRequestedPermissions());
assertEquals(a.getProtectedBroadcasts(), b.getProtectedBroadcasts());
assertEquals(a.getLibraryNames(), b.getLibraryNames());
@@ -443,6 +685,13 @@
assertEquals(aIntent.getNonLocalizedLabel(), bIntent.getNonLocalizedLabel());
assertEquals(aIntent.getIcon(), bIntent.getIcon());
}
+
+ assertEquals(a.getProperties().size(), b.getProperties().size());
+ final Iterator<String> iter = a.getProperties().keySet().iterator();
+ while (iter.hasNext()) {
+ final String key = iter.next();
+ assertEquals(a.getProperties().get(key), b.getProperties().get(key));
+ }
}
private static void assertPermissionsEqual(ParsedPermission a, ParsedPermission b) {
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
index c409438..f69dfe9 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
+++ b/services/tests/servicestests/test-apps/PackageParserApp/Android.bp
@@ -51,3 +51,17 @@
resource_dirs: ["res"],
manifest: "AndroidManifestApp3.xml",
}
+
+android_test_helper_app {
+ name: "PackageParserTestApp4",
+ sdk_version: "current",
+ srcs: ["**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+ resource_dirs: ["res"],
+ manifest: "AndroidManifestApp4.xml",
+}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
new file mode 100644
index 0000000..299b9a0
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.packageparserapp" >
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <property android:name="android.cts.PROPERTY_RESOURCE_XML" android:resource="@xml/xml_property" />
+ <property android:name="android.cts.PROPERTY_RESOURCE_INTEGER" android:resource="@integer/integer_property" />
+ <property android:name="android.cts.PROPERTY_BOOLEAN" android:value="true" />
+ <property android:name="android.cts.PROPERTY_BOOLEAN_VIA_RESOURCE" android:value="@bool/boolean_property" />
+ <property android:name="android.cts.PROPERTY_FLOAT" android:value="3.14" />
+ <property android:name="android.cts.PROPERTY_FLOAT_VIA_RESOURCE" android:value="@dimen/float_property" />
+ <property android:name="android.cts.PROPERTY_INTEGER" android:value="42" />
+ <property android:name="android.cts.PROPERTY_INTEGER_VIA_RESOURCE" android:value="@integer/integer_property" />
+ <property android:name="android.cts.PROPERTY_STRING" android:value="koala" />
+ <property android:name="android.cts.PROPERTY_STRING_VIA_RESOURCE" android:value="@string/string_property" />
+
+ <activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
+ android:exported="true" >
+ <property android:name="android.cts.PROPERTY_ACTIVITY" android:value="@integer/integer_property" />
+ <property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
+ <property android:name="android.cts.PROPERTY_STRING" android:value="koala activity" />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity-alias android:name="com.android.servicestests.apps.packageparserapp.MyActivityAlias"
+ android:targetActivity="com.android.servicestests.apps.packageparserapp.MyActivity">
+ <property android:name="android.cts.PROPERTY_ACTIVITY_ALIAS" android:value="@integer/integer_property" />
+ <property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
+ </activity-alias>
+ <provider android:name="com.android.servicestests.apps.packageparserapp.MyProvider"
+ android:authorities="propertytest">
+ <property android:name="android.cts.PROPERTY_PROVIDER" android:value="@integer/integer_property" />
+ </provider>
+ <receiver android:name="com.android.servicestests.apps.packageparserapp.MyReceiver">
+ <property android:name="android.cts.PROPERTY_RECEIVER" android:value="@integer/integer_property" />
+ <property android:name="android.cts.PROPERTY_STRING" android:value="koala receiver" />
+ </receiver>
+ <service android:name="com.android.servicestests.apps.packageparserapp.MyService">
+ <property android:name="android.cts.PROPERTY_SERVICE" android:value="@integer/integer_property" />
+ <property android:name="android.cts.PROPERTY_COMPONENT" android:resource="@integer/integer_property" />
+ </service>
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.servicestests.apps.packageparserapp" />
+</manifest>
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
index 6a4cc65..67ecf66 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/res/values/values.xml
@@ -16,4 +16,10 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<bool name="config_isIsolated">true</bool>
-</resources>
\ No newline at end of file
+ <bool name="boolean_property">true</bool>
+ <color name="color_property">#00FF00</color>
+ <item name="float_property" format="float" type="dimen">2.718</item>
+ <dimen name="dimen_property">23dp</dimen>
+ <integer name="integer_property">123</integer>
+ <string name="string_property">giraffe</string>
+</resources>
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml b/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml
new file mode 100644
index 0000000..588db8d
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/res/xml/xml_property.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<paths>
+ <external-path path="Android/data/" name="files_root" />
+ <external-path path="." name="external_storage_root" />
+</paths>
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java
new file mode 100644
index 0000000..6627166
--- /dev/null
+++ b/services/tests/servicestests/test-apps/PackageParserApp/src/com/android/servicestests/apps/packageparserapp/MyProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.servicestests.apps.packageparserapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+public class MyProvider extends ContentProvider {
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ return null;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ return "text/plain";
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+}