summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt17
-rw-r--r--core/java/android/content/pm/PackageManager.java210
-rw-r--r--core/res/res/values/attrs_manifest.xml32
-rw-r--r--core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java169
4 files changed, 428 insertions, 0 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index dfe80c7108ba..4f9d57477b04 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -12343,6 +12343,23 @@ package android.content.pm {
ctor public PackageManager.NameNotFoundException(String);
}
+ public static final class PackageManager.Property implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getBoolean();
+ method public float getFloat();
+ method public int getInteger();
+ method @NonNull public String getName();
+ method public int getResourceId();
+ method @Nullable public String getString();
+ method public boolean isBoolean();
+ method public boolean isFloat();
+ method public boolean isInteger();
+ method public boolean isResourceId();
+ method public boolean isString();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PackageManager.Property> CREATOR;
+ }
+
@Deprecated public class PackageStats implements android.os.Parcelable {
ctor @Deprecated public PackageStats(String);
ctor @Deprecated public PackageStats(android.os.Parcel);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index cc484deeee49..7d50dd22d0ba 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -64,6 +64,8 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -75,6 +77,7 @@ import android.permission.PermissionManager;
import android.util.AndroidException;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import dalvik.system.VMRuntime;
@@ -116,6 +119,213 @@ public abstract class PackageManager {
}
/**
+ * A property value set within the manifest.
+ * <p>
+ * The value of a property will only have a single type, as defined by
+ * the property itself.
+ */
+ public static final class Property implements Parcelable {
+ private static final int TYPE_BOOLEAN = 1;
+ private static final int TYPE_FLOAT = 2;
+ private static final int TYPE_INTEGER = 3;
+ private static final int TYPE_RESOURCE = 4;
+ private static final int TYPE_STRING = 5;
+ private final String mName;
+ private boolean mBooleanValue;
+ private float mFloatValue;
+ private int mIntegerValue;
+ private String mStringValue;
+ private final int mType;
+
+ /** @hide */
+ @VisibleForTesting
+ public Property(@NonNull String name, int type) {
+ assert name != null;
+ assert type >= TYPE_BOOLEAN && type <= TYPE_STRING;
+ this.mName = name;
+ this.mType = type;
+ }
+ /** @hide */
+ public Property(@NonNull String name, boolean value) {
+ this(name, TYPE_BOOLEAN);
+ mBooleanValue = value;
+ }
+ /** @hide */
+ public Property(@NonNull String name, float value) {
+ this(name, TYPE_FLOAT);
+ mFloatValue = value;
+ }
+ /** @hide */
+ public Property(@NonNull String name, int value, boolean isResource) {
+ this(name, isResource ? TYPE_RESOURCE : TYPE_INTEGER);
+ mIntegerValue = value;
+ }
+ /** @hide */
+ public Property(@NonNull String name, String value) {
+ this(name, TYPE_STRING);
+ mStringValue = value;
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the name of the property.
+ */
+ @NonNull public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the boolean value set for the property.
+ * <p>If the property is not of a boolean type, returns {@code false}.
+ */
+ public boolean getBoolean() {
+ return mBooleanValue;
+ }
+
+ /**
+ * Returns {@code true} if the property is a boolean type. Otherwise {@code false}.
+ */
+ public boolean isBoolean() {
+ return mType == TYPE_BOOLEAN;
+ }
+
+ /**
+ * Returns the float value set for the property.
+ * <p>If the property is not of a float type, returns {@code 0.0}.
+ */
+ public float getFloat() {
+ return mFloatValue;
+ }
+
+ /**
+ * Returns {@code true} if the property is a float type. Otherwise {@code false}.
+ */
+ public boolean isFloat() {
+ return mType == TYPE_FLOAT;
+ }
+
+ /**
+ * Returns the integer value set for the property.
+ * <p>If the property is not of an integer type, returns {@code 0}.
+ */
+ public int getInteger() {
+ return mType == TYPE_INTEGER ? mIntegerValue : 0;
+ }
+
+ /**
+ * Returns {@code true} if the property is an integer type. Otherwise {@code false}.
+ */
+ public boolean isInteger() {
+ return mType == TYPE_INTEGER;
+ }
+
+ /**
+ * Returns the a resource id set for the property.
+ * <p>If the property is not of a resource id type, returns {@code 0}.
+ */
+ public int getResourceId() {
+ return mType == TYPE_RESOURCE ? mIntegerValue : 0;
+ }
+
+ /**
+ * Returns {@code true} if the property is a resource id type. Otherwise {@code false}.
+ */
+ public boolean isResourceId() {
+ return mType == TYPE_RESOURCE;
+ }
+
+ /**
+ * Returns the a String value set for the property.
+ * <p>If the property is not a String type, returns {@code null}.
+ */
+ @Nullable public String getString() {
+ return mStringValue;
+ }
+
+ /**
+ * Returns {@code true} if the property is a String type. Otherwise {@code false}.
+ */
+ public boolean isString() {
+ return mType == TYPE_STRING;
+ }
+
+ /**
+ * Adds a mapping from the given key to this property's value in the provided
+ * {@link android.os.Bundle}. If the provided {@link android.os.Bundle} is
+ * {@code null}, creates a new {@link android.os.Bundle}.
+ * @hide
+ */
+ public Bundle toBundle(Bundle outBundle) {
+ final Bundle b = outBundle == null ? new Bundle() : outBundle;
+ if (mType == TYPE_BOOLEAN) {
+ b.putBoolean(mName, mBooleanValue);
+ } else if (mType == TYPE_FLOAT) {
+ b.putFloat(mName, mFloatValue);
+ } else if (mType == TYPE_INTEGER) {
+ b.putInt(mName, mIntegerValue);
+ } else if (mType == TYPE_RESOURCE) {
+ b.putInt(mName, mIntegerValue);
+ } else if (mType == TYPE_STRING) {
+ b.putString(mName, mStringValue);
+ }
+ return b;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeInt(mType);
+ if (mType == TYPE_BOOLEAN) {
+ dest.writeBoolean(mBooleanValue);
+ } else if (mType == TYPE_FLOAT) {
+ dest.writeFloat(mFloatValue);
+ } else if (mType == TYPE_INTEGER) {
+ dest.writeInt(mIntegerValue);
+ } else if (mType == TYPE_RESOURCE) {
+ dest.writeInt(mIntegerValue);
+ } else if (mType == TYPE_STRING) {
+ dest.writeString(mStringValue);
+ }
+ }
+
+ @NonNull
+ public static final Creator<Property> CREATOR = new Creator<Property>() {
+ @Override
+ public Property createFromParcel(@NonNull Parcel source) {
+ final String name = source.readString();
+ final int type = source.readInt();
+ if (type == TYPE_BOOLEAN) {
+ return new Property(name, source.readBoolean());
+ } else if (type == TYPE_FLOAT) {
+ return new Property(name, source.readFloat());
+ } else if (type == TYPE_INTEGER) {
+ return new Property(name, source.readInt(), false);
+ } else if (type == TYPE_RESOURCE) {
+ return new Property(name, source.readInt(), true);
+ } else if (type == TYPE_STRING) {
+ return new Property(name, source.readString());
+ }
+ return null;
+ }
+
+ @Override
+ public Property[] newArray(int size) {
+ return new Property[size];
+ }
+ };
+ }
+
+ /**
* Listener for changes in permissions granted to a UID.
*
* @hide
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index fc9f670f31a7..c32e8dc94486 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2834,6 +2834,38 @@
<attr name="resource" format="reference" />
</declare-styleable>
+ <!-- The <code>property</code> tag is used to attach additional data that can
+ be supplied to the parent component. A component element can contain any
+ number of <code>property</code> subelements. Valid names are any of the
+ <code>PROPERTY_</code> constants defined in the
+ {@link android.content.pm.PackageManager PackageManager} class. Values
+ are obtained using the appropriate method on the
+ {@link android.content.pm.PackageManager.Property PackageManager.Property} class.
+ <p>Ordinary values are specified through the value attribute. Resource IDs are
+ specified through the resource attribute.
+ <p>It is invalid to specify both a value and resource attributes. -->
+ <declare-styleable name="AndroidManifestProperty"
+ parent="AndroidManifestApplication
+ AndroidManifestActivity
+ AndroidManifestReceiver
+ AndroidManifestProvider
+ AndroidManifestService">
+ <attr name="name" />
+ <!-- Concrete value to assign to this property.
+ The data can later be retrieved from the property object
+ through
+ {@link android.content.pm.PackageManager.Property#getString Property.getString},
+ {@link android.content.pm.PackageManager.Property#getInteger Property.getInteger},
+ {@link android.content.pm.PackageManager.Property#getBoolean Property.getBoolean},
+ or {@link android.content.pm.PackageManager.Property#getFloat Property.getFloat}
+ depending on the type used here. -->
+ <attr name="value" />
+ <!-- The resource identifier to assign to this property.
+ The resource identifier can later be retrieved from the property object through
+ {@link android.content.pm.PackageManager.Property#getResourceId Property.getResourceId}. -->
+ <attr name="resource" />
+ </declare-styleable>
+
<!-- The <code>intent-filter</code> tag is used to construct an
{@link android.content.IntentFilter} object that will be used
to determine which component can handle a particular
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
new file mode 100644
index 000000000000..7effa561b873
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java
@@ -0,0 +1,169 @@
+/*
+ * 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 android.content.pm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.pm.PackageManager.Property;
+import android.os.Bundle;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageManagerPropertyTests {
+
+ @Test
+ public void testBooleanProperty() throws Exception {
+ final Property p = new Property("booleanProperty", true);
+ assertTrue(p.isBoolean());
+ assertFalse(p.isFloat());
+ assertFalse(p.isInteger());
+ assertFalse(p.isResourceId());
+ assertFalse(p.isString());
+ assertTrue(p.getBoolean());
+ assertEquals(0.0f, p.getFloat(), 0.0f);
+ assertEquals(0, p.getInteger());
+ assertEquals(0, p.getResourceId());
+ assertEquals(null, p.getString());
+ }
+
+ @Test
+ public void testBooleanPropertyToBundle() throws Exception {
+ final Bundle b = new Property("booleanProperty", true).toBundle(null);
+ assertTrue(b.getBoolean("booleanProperty"));
+ }
+
+ @Test
+ public void testFloatProperty() throws Exception {
+ final Property p = new Property("floatProperty", 3.14f);
+ assertFalse(p.isBoolean());
+ assertTrue(p.isFloat());
+ assertFalse(p.isInteger());
+ assertFalse(p.isResourceId());
+ assertFalse(p.isString());
+ assertFalse(p.getBoolean());
+ assertEquals(3.14f, p.getFloat(), 0.0f);
+ assertEquals(0, p.getInteger());
+ assertEquals(0, p.getResourceId());
+ assertEquals(null, p.getString());
+ }
+
+ @Test
+ public void testFloatPropertyToBundle() throws Exception {
+ final Bundle b = new Property("floatProperty", 3.14f).toBundle(null);
+ assertEquals(3.14f, b.getFloat("floatProperty"), 0.0f);
+ }
+
+ @Test
+ public void testIntegerProperty() throws Exception {
+ final Property p = new Property("integerProperty", 42, false);
+ assertFalse(p.isBoolean());
+ assertFalse(p.isFloat());
+ assertTrue(p.isInteger());
+ assertFalse(p.isResourceId());
+ assertFalse(p.isString());
+ assertFalse(p.getBoolean());
+ assertEquals(0.0f, p.getFloat(), 0.0f);
+ assertEquals(42, p.getInteger());
+ assertEquals(0, p.getResourceId());
+ assertEquals(null, p.getString());
+ }
+
+ @Test
+ public void testIntegerPropertyToBundle() throws Exception {
+ final Bundle b = new Property("integerProperty", 42, false).toBundle(null);
+ assertEquals(42, b.getInt("integerProperty"));
+ }
+
+ @Test
+ public void testResourceProperty() throws Exception {
+ final Property p = new Property("resourceProperty", 0x7f010001, true);
+ assertFalse(p.isBoolean());
+ assertFalse(p.isFloat());
+ assertFalse(p.isInteger());
+ assertTrue(p.isResourceId());
+ assertFalse(p.isString());
+ assertFalse(p.getBoolean());
+ assertEquals(0.0f, p.getFloat(), 0.0f);
+ assertEquals(0, p.getInteger());
+ assertEquals(0x7f010001, p.getResourceId());
+ assertEquals(null, p.getString());
+ }
+
+ @Test
+ public void testResourcePropertyToBundle() throws Exception {
+ final Bundle b = new Property("resourceProperty", 0x7f010001, true).toBundle(null);
+ assertEquals(0x7f010001, b.getInt("resourceProperty"));
+ }
+
+ @Test
+ public void testStringProperty() throws Exception {
+ final Property p = new Property("stringProperty", "koala");
+ assertFalse(p.isBoolean());
+ assertFalse(p.isFloat());
+ assertFalse(p.isInteger());
+ assertFalse(p.isResourceId());
+ assertTrue(p.isString());
+ assertFalse(p.getBoolean());
+ assertEquals(0.0f, p.getFloat(), 0.0f);
+ assertEquals(0, p.getInteger());
+ assertEquals(0, p.getResourceId());
+ assertEquals("koala", p.getString());
+ }
+
+ @Test
+ public void testStringPropertyToBundle() throws Exception {
+ final Bundle b = new Property("stringProperty", "koala").toBundle(null);
+ assertEquals("koala", b.getString("stringProperty"));
+ }
+
+ @Test
+ public void testProperty_invalidName() throws Exception {
+ try {
+ final Property p = new Property(null, 1);
+ fail("expected assertion error");
+ } catch (AssertionError expected) {
+ }
+ }
+
+ @Test
+ public void testProperty_invalidType() throws Exception {
+ try {
+ final Property p = new Property(null, 0);
+ fail("expected assertion error");
+ } catch (AssertionError expected) {
+ }
+
+ try {
+ final Property p = new Property(null, 6);
+ fail("expected assertion error");
+ } catch (AssertionError expected) {
+ }
+
+ try {
+ final Property p = new Property(null, -1);
+ fail("expected assertion error");
+ } catch (AssertionError expected) {
+ }
+ }
+}