diff options
| -rw-r--r-- | core/api/current.txt | 17 | ||||
| -rw-r--r-- | core/java/android/content/pm/PackageManager.java | 210 | ||||
| -rw-r--r-- | core/res/res/values/attrs_manifest.xml | 32 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/content/pm/PackageManagerPropertyTests.java | 169 |
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) { + } + } +} |