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/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;
+ }
+
+}