Allow senders to specify the delivery group via matching filters.
Senders can specify an IntentFilter object that should match against another broadcast intent to be considered in the same delivery group. Bug: 249160234 Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java Test: atest services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java Test: atest tests/app/src/android/app/cts/BroadcastOptionsTest.java Test: atest tests/tests/content/src/android/content/cts/IntentFilterTest.java Change-Id: I36f1e3ffab8e30371a9968cb23c7b345c3301682
This commit is contained in:
@@ -779,13 +779,19 @@ package android.app {
|
||||
}
|
||||
|
||||
public class BroadcastOptions {
|
||||
method public void clearDeliveryGroupMatchingFilter();
|
||||
method public void clearDeliveryGroupMatchingKey();
|
||||
method public void clearDeliveryGroupPolicy();
|
||||
method public void clearRequireCompatChange();
|
||||
method @Nullable public android.content.IntentFilter getDeliveryGroupMatchingFilter();
|
||||
method @Nullable public String getDeliveryGroupMatchingKey();
|
||||
method public int getDeliveryGroupPolicy();
|
||||
method public boolean isPendingIntentBackgroundActivityLaunchAllowed();
|
||||
method public static android.app.BroadcastOptions makeBasic();
|
||||
method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long);
|
||||
method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean);
|
||||
method public void setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
|
||||
method public void setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
|
||||
method public void setDeliveryGroupPolicy(int);
|
||||
method public void setDontSendToRestrictedApps(boolean);
|
||||
method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean);
|
||||
@@ -3170,7 +3176,9 @@ package android.content {
|
||||
}
|
||||
|
||||
public class IntentFilter implements android.os.Parcelable {
|
||||
method @NonNull public final android.os.PersistableBundle getExtras();
|
||||
method public final int getOrder();
|
||||
method public final void setExtras(@NonNull android.os.PersistableBundle);
|
||||
method public final void setOrder(int);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,9 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
private long mIdForResponseEvent;
|
||||
private @Nullable IntentFilter mRemoveMatchingFilter;
|
||||
private @DeliveryGroupPolicy int mDeliveryGroupPolicy;
|
||||
private @Nullable String mDeliveryGroupKey;
|
||||
private @Nullable String mDeliveryGroupMatchingKey;
|
||||
private @Nullable BundleMerger mDeliveryGroupExtrasMerger;
|
||||
private @Nullable IntentFilter mDeliveryGroupMatchingFilter;
|
||||
|
||||
/**
|
||||
* Change ID which is invalid.
|
||||
@@ -214,10 +215,10 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
"android:broadcast.deliveryGroupPolicy";
|
||||
|
||||
/**
|
||||
* Corresponds to {@link #setDeliveryGroupKey(String, String)}.
|
||||
* Corresponds to {@link #setDeliveryGroupMatchingKey(String, String)}.
|
||||
*/
|
||||
private static final String KEY_DELIVERY_GROUP_KEY =
|
||||
"android:broadcast.deliveryGroupKey";
|
||||
"android:broadcast.deliveryGroupMatchingKey";
|
||||
|
||||
/**
|
||||
* Corresponds to {@link #setDeliveryGroupExtrasMerger(BundleMerger)}.
|
||||
@@ -225,6 +226,12 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
private static final String KEY_DELIVERY_GROUP_EXTRAS_MERGER =
|
||||
"android:broadcast.deliveryGroupExtrasMerger";
|
||||
|
||||
/**
|
||||
* Corresponds to {@link #setDeliveryGroupMatchingFilter(IntentFilter)}.
|
||||
*/
|
||||
private static final String KEY_DELIVERY_GROUP_MATCHING_FILTER =
|
||||
"android:broadcast.deliveryGroupMatchingFilter";
|
||||
|
||||
/**
|
||||
* The list of delivery group policies which specify how multiple broadcasts belonging to
|
||||
* the same delivery group has to be handled.
|
||||
@@ -313,9 +320,11 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
IntentFilter.class);
|
||||
mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY,
|
||||
DELIVERY_GROUP_POLICY_ALL);
|
||||
mDeliveryGroupKey = opts.getString(KEY_DELIVERY_GROUP_KEY);
|
||||
mDeliveryGroupMatchingKey = opts.getString(KEY_DELIVERY_GROUP_KEY);
|
||||
mDeliveryGroupExtrasMerger = opts.getParcelable(KEY_DELIVERY_GROUP_EXTRAS_MERGER,
|
||||
BundleMerger.class);
|
||||
mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER,
|
||||
IntentFilter.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -764,7 +773,7 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
|
||||
/**
|
||||
* Clears any previously set delivery group policies using
|
||||
* {@link #setDeliveryGroupKey(String, String)} and resets the delivery group policy to
|
||||
* {@link #setDeliveryGroupMatchingKey(String, String)} and resets the delivery group policy to
|
||||
* the default value ({@link #DELIVERY_GROUP_POLICY_ALL}).
|
||||
*
|
||||
* @hide
|
||||
@@ -776,22 +785,92 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
|
||||
/**
|
||||
* Set namespace and key to identify the delivery group that this broadcast belongs to.
|
||||
* If no namespace and key is set, then by default {@link Intent#filterEquals(Intent)} will be
|
||||
* used to identify the delivery group.
|
||||
*
|
||||
* <p> If {@code namespace} and {@code key} are specified, then another broadcast will be
|
||||
* considered to be in the same delivery group as this iff it has the same {@code namespace}
|
||||
* and {@code key}.
|
||||
*
|
||||
* <p> If neither matching key using this API nor matching filter using
|
||||
* {@link #setDeliveryGroupMatchingFilter(IntentFilter)} is specified, then by default
|
||||
* {@link Intent#filterEquals(Intent)} will be used to identify the delivery group.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setDeliveryGroupKey(@NonNull String namespace, @NonNull String key) {
|
||||
@SystemApi
|
||||
public void setDeliveryGroupMatchingKey(@NonNull String namespace, @NonNull String key) {
|
||||
Preconditions.checkArgument(!namespace.contains("/"),
|
||||
"namespace should not contain '/'");
|
||||
Preconditions.checkArgument(!key.contains("/"),
|
||||
"key should not contain '/'");
|
||||
mDeliveryGroupKey = namespace + "/" + key;
|
||||
mDeliveryGroupMatchingKey = namespace + "/" + key;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public String getDeliveryGroupKey() {
|
||||
return mDeliveryGroupKey;
|
||||
/**
|
||||
* Return the namespace and key that is used to identify the delivery group that this
|
||||
* broadcast belongs to.
|
||||
*
|
||||
* @return the delivery group namespace and key that was previously set using
|
||||
* {@link #setDeliveryGroupMatchingKey(String, String)}, concatenated with a {@code /}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public String getDeliveryGroupMatchingKey() {
|
||||
return mDeliveryGroupMatchingKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the namespace and key that was previously set using
|
||||
* {@link #setDeliveryGroupMatchingKey(String, String)}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void clearDeliveryGroupMatchingKey() {
|
||||
mDeliveryGroupMatchingKey = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link IntentFilter} object to identify the delivery group that this broadcast
|
||||
* belongs to.
|
||||
*
|
||||
* <p> If a {@code matchingFilter} is specified, then another broadcast will be considered
|
||||
* to be in the same delivery group as this iff the {@code matchingFilter} matches it's intent.
|
||||
*
|
||||
* <p> If neither matching key using {@link #setDeliveryGroupMatchingKey(String, String)} nor
|
||||
* matching filter using this API is specified, then by default
|
||||
* {@link Intent#filterEquals(Intent)} will be used to identify the delivery group.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void setDeliveryGroupMatchingFilter(@NonNull IntentFilter matchingFilter) {
|
||||
mDeliveryGroupMatchingFilter = Objects.requireNonNull(matchingFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link IntentFilter} object that is used to identify the delivery group
|
||||
* that this broadcast belongs to.
|
||||
*
|
||||
* @return the {@link IntentFilter} object that was previously set using
|
||||
* {@link #setDeliveryGroupMatchingFilter(IntentFilter)}.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Nullable
|
||||
public IntentFilter getDeliveryGroupMatchingFilter() {
|
||||
return mDeliveryGroupMatchingFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the {@link IntentFilter} object that was previously set using
|
||||
* {@link #setDeliveryGroupMatchingFilter(IntentFilter)}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public void clearDeliveryGroupMatchingFilter() {
|
||||
mDeliveryGroupMatchingFilter = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -804,15 +883,32 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
* @hide
|
||||
*/
|
||||
public void setDeliveryGroupExtrasMerger(@NonNull BundleMerger extrasMerger) {
|
||||
Preconditions.checkNotNull(extrasMerger);
|
||||
mDeliveryGroupExtrasMerger = extrasMerger;
|
||||
mDeliveryGroupExtrasMerger = Objects.requireNonNull(extrasMerger);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public @Nullable BundleMerger getDeliveryGroupExtrasMerger() {
|
||||
/**
|
||||
* Return the {@link BundleMerger} that specifies how to merge the extras data from
|
||||
* broadcasts in a delivery group.
|
||||
*
|
||||
* @return the {@link BundleMerger} object that was previously set using
|
||||
* {@link #setDeliveryGroupExtrasMerger(BundleMerger)}.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public BundleMerger getDeliveryGroupExtrasMerger() {
|
||||
return mDeliveryGroupExtrasMerger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the {@link BundleMerger} object that was previously set using
|
||||
* {@link #setDeliveryGroupExtrasMerger(BundleMerger)}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void clearDeliveryGroupExtrasMerger() {
|
||||
mDeliveryGroupExtrasMerger = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the created options as a Bundle, which can be passed to
|
||||
* {@link android.content.Context#sendBroadcast(android.content.Intent)
|
||||
@@ -871,8 +967,8 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) {
|
||||
b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy);
|
||||
}
|
||||
if (mDeliveryGroupKey != null) {
|
||||
b.putString(KEY_DELIVERY_GROUP_KEY, mDeliveryGroupKey);
|
||||
if (mDeliveryGroupMatchingKey != null) {
|
||||
b.putString(KEY_DELIVERY_GROUP_KEY, mDeliveryGroupMatchingKey);
|
||||
}
|
||||
if (mDeliveryGroupPolicy == DELIVERY_GROUP_POLICY_MERGED) {
|
||||
if (mDeliveryGroupExtrasMerger != null) {
|
||||
@@ -883,6 +979,9 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
+ "when delivery group policy is 'MERGED'");
|
||||
}
|
||||
}
|
||||
if (mDeliveryGroupMatchingFilter != null) {
|
||||
b.putParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER, mDeliveryGroupMatchingFilter);
|
||||
}
|
||||
return b.isEmpty() ? null : b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,11 @@ import android.annotation.SystemApi;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PatternMatcher;
|
||||
import android.os.PersistableBundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AndroidException;
|
||||
import android.util.ArraySet;
|
||||
@@ -171,6 +173,13 @@ public class IntentFilter implements Parcelable {
|
||||
private static final String NAME_STR = "name";
|
||||
private static final String ACTION_STR = "action";
|
||||
private static final String AUTO_VERIFY_STR = "autoVerify";
|
||||
private static final String EXTRAS_STR = "extras";
|
||||
|
||||
private static final int[] EMPTY_INT_ARRAY = new int[0];
|
||||
private static final long[] EMPTY_LONG_ARRAY = new long[0];
|
||||
private static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
|
||||
|
||||
/**
|
||||
* The filter {@link #setPriority} value at which system high-priority
|
||||
@@ -267,6 +276,11 @@ public class IntentFilter implements Parcelable {
|
||||
* that were not in the Intent.
|
||||
*/
|
||||
public static final int NO_MATCH_CATEGORY = -4;
|
||||
/**
|
||||
* That filter didn't match due to different extras data.
|
||||
* @hide
|
||||
*/
|
||||
public static final int NO_MATCH_EXTRAS = -5;
|
||||
|
||||
/**
|
||||
* HTTP scheme.
|
||||
@@ -314,6 +328,7 @@ public class IntentFilter implements Parcelable {
|
||||
private ArrayList<String> mMimeGroups = null;
|
||||
private boolean mHasStaticPartialTypes = false;
|
||||
private boolean mHasDynamicPartialTypes = false;
|
||||
private PersistableBundle mExtras = null;
|
||||
|
||||
private static final int STATE_VERIFY_AUTO = 0x00000001;
|
||||
private static final int STATE_NEED_VERIFY = 0x00000010;
|
||||
@@ -507,6 +522,9 @@ public class IntentFilter implements Parcelable {
|
||||
if (o.mMimeGroups != null) {
|
||||
mMimeGroups = new ArrayList<String>(o.mMimeGroups);
|
||||
}
|
||||
if (o.mExtras != null) {
|
||||
mExtras = new PersistableBundle(o.mExtras);
|
||||
}
|
||||
mHasStaticPartialTypes = o.mHasStaticPartialTypes;
|
||||
mHasDynamicPartialTypes = o.mHasDynamicPartialTypes;
|
||||
mVerifyState = o.mVerifyState;
|
||||
@@ -1767,6 +1785,423 @@ public class IntentFilter implements Parcelable {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match this filter against an Intent's extras. An intent must
|
||||
* have all the extras specified by the filter with the same values,
|
||||
* for the match to succeed.
|
||||
*
|
||||
* <p> An intent con have other extras in addition to those specified
|
||||
* by the filter and it would not affect whether the match succeeds or not.
|
||||
*
|
||||
* @param extras The extras included in the intent, as returned by
|
||||
* Intent.getExtras().
|
||||
*
|
||||
* @return If all extras match (success), null; else the name of the
|
||||
* first extra that didn't match.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
private String matchExtras(@Nullable Bundle extras) {
|
||||
if (mExtras == null) {
|
||||
return null;
|
||||
}
|
||||
final Set<String> keys = mExtras.keySet();
|
||||
for (String key : keys) {
|
||||
if (extras == null) {
|
||||
return key;
|
||||
}
|
||||
final Object value = mExtras.get(key);
|
||||
final Object otherValue = extras.get(key);
|
||||
if (otherValue == null || value.getClass() != otherValue.getClass()
|
||||
|| !Objects.deepEquals(value, otherValue)) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, int value) {
|
||||
Objects.requireNonNull(name);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putInt(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, int)} or
|
||||
* {@code 0} if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
public final int getIntExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? 0 : mExtras.getInt(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, @NonNull int[] value) {
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(value);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putIntArray(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, int[])} or
|
||||
* an empty int array if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final int[] getIntArrayExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? EMPTY_INT_ARRAY : mExtras.getIntArray(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, long value) {
|
||||
Objects.requireNonNull(name);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putLong(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, long)} or
|
||||
* {@code 0L} if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
public final long getLongExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? 0L : mExtras.getLong(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, @NonNull long[] value) {
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(value);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putLongArray(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, long[])} or
|
||||
* an empty long array if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final long[] getLongArrayExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? EMPTY_LONG_ARRAY : mExtras.getLongArray(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, double value) {
|
||||
Objects.requireNonNull(name);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putDouble(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, double)} or
|
||||
* {@code 0.0} if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final double getDoubleExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? 0.0 : mExtras.getDouble(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, @NonNull double[] value) {
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(value);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putDoubleArray(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, double[])} or
|
||||
* an empty double array if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final double[] getDoubleArrayExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? EMPTY_DOUBLE_ARRAY : mExtras.getDoubleArray(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, @NonNull String value) {
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(value);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putString(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, String)} or a
|
||||
* {@code null} if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public final String getStringExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? null : mExtras.getString(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, @NonNull String[] value) {
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(value);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putStringArray(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, String[])} or
|
||||
* an empty string array if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final String[] getStringArrayExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? EMPTY_STRING_ARRAY : mExtras.getStringArray(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, boolean value) {
|
||||
Objects.requireNonNull(name);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putBoolean(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, boolean)} or
|
||||
* {@code false} if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
public final boolean getBooleanExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? false : mExtras.getBoolean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new extra name and value to match against. If an extra is included in the filter,
|
||||
* then an Intent must have an extra with the same {@code name} and {@code value} for it to
|
||||
* match.
|
||||
*
|
||||
* <p> Subsequent calls to this method with the same {@code name} will override any previously
|
||||
* set {@code value}.
|
||||
*
|
||||
* @param name the name of the extra to match against.
|
||||
* @param value the value of the extra to match against.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final void addExtra(@NonNull String name, @NonNull boolean[] value) {
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(value);
|
||||
if (mExtras == null) {
|
||||
mExtras = new PersistableBundle();
|
||||
}
|
||||
mExtras.putBooleanArray(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the extra with {@code name} included in the filter.
|
||||
*
|
||||
* @return the value that was previously set using {@link #addExtra(String, boolean[])} or
|
||||
* an empty boolean array if no value has been set.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final boolean[] getBooleanArrayExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? EMPTY_BOOLEAN_ARRAY : mExtras.getBooleanArray(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not an extra with {@code name} is included in the filter.
|
||||
*
|
||||
* @return {@code true} if an extra with {@code name} is included in the filter.
|
||||
* Otherwise {@code false}.
|
||||
* @hide
|
||||
*/
|
||||
public final boolean hasExtra(@NonNull String name) {
|
||||
Objects.requireNonNull(name);
|
||||
return mExtras == null ? false : mExtras.containsKey(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the intent extras to match against. For this filter to match an
|
||||
* intent, it must contain all the {@code extras} set.
|
||||
*
|
||||
* <p> Subsequent calls to this method will override any previously set extras.
|
||||
*
|
||||
* @param extras The intent extras to match against.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final void setExtras(@NonNull PersistableBundle extras) {
|
||||
mExtras = extras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the intent extras included in this filter.
|
||||
*
|
||||
* @return the extras that were previously set using {@link #setExtras(PersistableBundle)} or
|
||||
* an empty {@link PersistableBundle} object if no extras were set.
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@NonNull
|
||||
public final PersistableBundle getExtras() {
|
||||
return mExtras == null ? new PersistableBundle() : mExtras;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Predicate} which tests whether this filter matches the
|
||||
* given <var>intent</var>.
|
||||
@@ -1818,7 +2253,9 @@ public class IntentFilter implements Parcelable {
|
||||
boolean resolve, String logTag) {
|
||||
String type = resolve ? intent.resolveType(resolver) : intent.getType();
|
||||
return match(intent.getAction(), type, intent.getScheme(),
|
||||
intent.getData(), intent.getCategories(), logTag);
|
||||
intent.getData(), intent.getCategories(), logTag,
|
||||
false /* supportWildcards */, null /* ignoreActions */,
|
||||
intent.getExtras());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1868,6 +2305,19 @@ public class IntentFilter implements Parcelable {
|
||||
public final int match(String action, String type, String scheme,
|
||||
Uri data, Set<String> categories, String logTag, boolean supportWildcards,
|
||||
@Nullable Collection<String> ignoreActions) {
|
||||
return match(action, type, scheme, data, categories, logTag, supportWildcards,
|
||||
ignoreActions, null /* extras */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #match(String, String, String, Uri, Set, String, boolean, Collection)}
|
||||
* that supports matching the extra values in the intent.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final int match(String action, String type, String scheme,
|
||||
Uri data, Set<String> categories, String logTag, boolean supportWildcards,
|
||||
@Nullable Collection<String> ignoreActions, @Nullable Bundle extras) {
|
||||
if (action != null && !matchAction(action, supportWildcards, ignoreActions)) {
|
||||
if (false) Log.v(
|
||||
logTag, "No matching action " + action + " for " + this);
|
||||
@@ -1897,6 +2347,14 @@ public class IntentFilter implements Parcelable {
|
||||
return NO_MATCH_CATEGORY;
|
||||
}
|
||||
|
||||
String extraMismatch = matchExtras(extras);
|
||||
if (extraMismatch != null) {
|
||||
if (false) {
|
||||
Log.v(logTag, "Mismatch for extra key " + extraMismatch + " for " + this);
|
||||
}
|
||||
return NO_MATCH_EXTRAS;
|
||||
}
|
||||
|
||||
// It would be nice to treat container activities as more
|
||||
// important than ones that can be embedded, but this is not the way...
|
||||
if (false) {
|
||||
@@ -1998,6 +2456,15 @@ public class IntentFilter implements Parcelable {
|
||||
}
|
||||
serializer.endTag(null, PATH_STR);
|
||||
}
|
||||
if (mExtras != null) {
|
||||
serializer.startTag(null, EXTRAS_STR);
|
||||
try {
|
||||
mExtras.saveToXml(serializer);
|
||||
} catch (XmlPullParserException e) {
|
||||
throw new IllegalStateException("Failed to write extras: " + mExtras.toString(), e);
|
||||
}
|
||||
serializer.endTag(null, EXTRAS_STR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2124,6 +2591,8 @@ public class IntentFilter implements Parcelable {
|
||||
} else if ((path=parser.getAttributeValue(null, SUFFIX_STR)) != null) {
|
||||
addDataPath(path, PatternMatcher.PATTERN_SUFFIX);
|
||||
}
|
||||
} else if (tagName.equals(EXTRAS_STR)) {
|
||||
mExtras = PersistableBundle.restoreFromXml(parser);
|
||||
} else {
|
||||
Log.w("IntentFilter", "Unknown tag parsing IntentFilter: " + tagName);
|
||||
}
|
||||
@@ -2187,6 +2656,9 @@ public class IntentFilter implements Parcelable {
|
||||
proto.write(IntentFilterProto.HAS_PARTIAL_TYPES, hasPartialTypes());
|
||||
}
|
||||
proto.write(IntentFilterProto.GET_AUTO_VERIFY, getAutoVerify());
|
||||
if (mExtras != null) {
|
||||
mExtras.dumpDebug(proto, IntentFilterProto.EXTRAS);
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
@@ -2297,6 +2769,11 @@ public class IntentFilter implements Parcelable {
|
||||
sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
|
||||
du.println(sb.toString());
|
||||
}
|
||||
if (mExtras != null) {
|
||||
sb.setLength(0);
|
||||
sb.append(prefix); sb.append("mExtras="); sb.append(mExtras.toString());
|
||||
du.println(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Parcelable.Creator<IntentFilter> CREATOR
|
||||
@@ -2379,6 +2856,12 @@ public class IntentFilter implements Parcelable {
|
||||
dest.writeInt(getAutoVerify() ? 1 : 0);
|
||||
dest.writeInt(mInstantAppVisibility);
|
||||
dest.writeInt(mOrder);
|
||||
if (mExtras != null) {
|
||||
dest.writeInt(1);
|
||||
mExtras.writeToParcel(dest, flags);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2459,6 +2942,9 @@ public class IntentFilter implements Parcelable {
|
||||
setAutoVerify(source.readInt() > 0);
|
||||
setVisibilityToInstantApp(source.readInt());
|
||||
mOrder = source.readInt();
|
||||
if (source.readInt() != 0) {
|
||||
mExtras = PersistableBundle.CREATOR.createFromParcel(source);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPartialTypes() {
|
||||
|
||||
@@ -21,6 +21,7 @@ option java_multiple_files = true;
|
||||
|
||||
import "frameworks/base/core/proto/android/content/component_name.proto";
|
||||
import "frameworks/base/core/proto/android/os/patternmatcher.proto";
|
||||
import "frameworks/base/core/proto/android/os/persistablebundle.proto";
|
||||
import "frameworks/base/core/proto/android/privacy.proto";
|
||||
|
||||
// Next Tag: 14
|
||||
@@ -87,6 +88,7 @@ message IntentFilterProto {
|
||||
optional bool has_partial_types = 9;
|
||||
optional bool get_auto_verify = 10;
|
||||
repeated string mime_groups = 11;
|
||||
optional android.os.PersistableBundleProto extras = 12;
|
||||
}
|
||||
|
||||
message AuthorityEntryProto {
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.app.compat.CompatChanges;
|
||||
import android.content.ComponentName;
|
||||
import android.content.IIntentReceiver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Binder;
|
||||
@@ -870,14 +871,34 @@ final class BroadcastRecord extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matchesDeliveryGroup(@NonNull BroadcastRecord other) {
|
||||
final String key = (options != null) ? options.getDeliveryGroupKey() : null;
|
||||
final String otherKey = (other.options != null)
|
||||
? other.options.getDeliveryGroupKey() : null;
|
||||
if (key == null && otherKey == null) {
|
||||
return intent.filterEquals(other.intent);
|
||||
boolean matchesDeliveryGroup(@NonNull BroadcastRecord other) {
|
||||
return matchesDeliveryGroup(this, other);
|
||||
}
|
||||
|
||||
private static boolean matchesDeliveryGroup(@NonNull BroadcastRecord newRecord,
|
||||
@NonNull BroadcastRecord oldRecord) {
|
||||
final String newMatchingKey = getDeliveryGroupMatchingKey(newRecord);
|
||||
final String oldMatchingKey = getDeliveryGroupMatchingKey(oldRecord);
|
||||
final IntentFilter newMatchingFilter = getDeliveryGroupMatchingFilter(newRecord);
|
||||
// If neither delivery group key nor matching filter is specified, then use
|
||||
// Intent.filterEquals() to identify the delivery group.
|
||||
if (newMatchingKey == null && oldMatchingKey == null && newMatchingFilter == null) {
|
||||
return newRecord.intent.filterEquals(oldRecord.intent);
|
||||
}
|
||||
return Objects.equals(key, otherKey);
|
||||
if (newMatchingFilter != null && !newMatchingFilter.asPredicate().test(oldRecord.intent)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(newMatchingKey, oldMatchingKey);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getDeliveryGroupMatchingKey(@NonNull BroadcastRecord record) {
|
||||
return record.options == null ? null : record.options.getDeliveryGroupMatchingKey();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static IntentFilter getDeliveryGroupMatchingFilter(@NonNull BroadcastRecord record) {
|
||||
return record.options == null ? null : record.options.getDeliveryGroupMatchingFilter();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -586,7 +586,7 @@ public class BroadcastQueueModernImplTest {
|
||||
final BroadcastOptions optionsMusicVolumeChanged = BroadcastOptions.makeBasic();
|
||||
optionsMusicVolumeChanged.setDeliveryGroupPolicy(
|
||||
BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
|
||||
optionsMusicVolumeChanged.setDeliveryGroupKey("audio",
|
||||
optionsMusicVolumeChanged.setDeliveryGroupMatchingKey("audio",
|
||||
String.valueOf(AudioManager.STREAM_MUSIC));
|
||||
|
||||
final Intent alarmVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
|
||||
@@ -595,7 +595,7 @@ public class BroadcastQueueModernImplTest {
|
||||
final BroadcastOptions optionsAlarmVolumeChanged = BroadcastOptions.makeBasic();
|
||||
optionsAlarmVolumeChanged.setDeliveryGroupPolicy(
|
||||
BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
|
||||
optionsAlarmVolumeChanged.setDeliveryGroupKey("audio",
|
||||
optionsAlarmVolumeChanged.setDeliveryGroupMatchingKey("audio",
|
||||
String.valueOf(AudioManager.STREAM_ALARM));
|
||||
|
||||
// Halt all processing so that we get a consistent view
|
||||
@@ -651,7 +651,8 @@ public class BroadcastQueueModernImplTest {
|
||||
final BroadcastOptions optionsPackageChangedForUid = BroadcastOptions.makeBasic();
|
||||
optionsPackageChangedForUid.setDeliveryGroupPolicy(
|
||||
BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED);
|
||||
optionsPackageChangedForUid.setDeliveryGroupKey("package", String.valueOf(TEST_UID));
|
||||
optionsPackageChangedForUid.setDeliveryGroupMatchingKey("package",
|
||||
String.valueOf(TEST_UID));
|
||||
optionsPackageChangedForUid.setDeliveryGroupExtrasMerger(extrasMerger);
|
||||
|
||||
final Intent secondPackageChangedForUid = createPackageChangedIntent(TEST_UID,
|
||||
@@ -662,7 +663,8 @@ public class BroadcastQueueModernImplTest {
|
||||
final BroadcastOptions optionsPackageChangedForUid2 = BroadcastOptions.makeBasic();
|
||||
optionsPackageChangedForUid.setDeliveryGroupPolicy(
|
||||
BroadcastOptions.DELIVERY_GROUP_POLICY_MERGED);
|
||||
optionsPackageChangedForUid.setDeliveryGroupKey("package", String.valueOf(TEST_UID2));
|
||||
optionsPackageChangedForUid.setDeliveryGroupMatchingKey("package",
|
||||
String.valueOf(TEST_UID2));
|
||||
optionsPackageChangedForUid.setDeliveryGroupExtrasMerger(extrasMerger);
|
||||
|
||||
// Halt all processing so that we get a consistent view
|
||||
@@ -685,6 +687,75 @@ public class BroadcastQueueModernImplTest {
|
||||
verifyPendingRecords(queue, List.of(packageChangedForUid2, expectedPackageChangedForUid));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeliveryGroupPolicy_matchingFilter() {
|
||||
final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
|
||||
final BroadcastOptions optionsTimeTick = BroadcastOptions.makeBasic();
|
||||
optionsTimeTick.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
|
||||
|
||||
final Intent musicVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
|
||||
musicVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
|
||||
AudioManager.STREAM_MUSIC);
|
||||
final IntentFilter filterMusicVolumeChanged = new IntentFilter(
|
||||
AudioManager.VOLUME_CHANGED_ACTION);
|
||||
filterMusicVolumeChanged.addExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
|
||||
AudioManager.STREAM_MUSIC);
|
||||
final BroadcastOptions optionsMusicVolumeChanged = BroadcastOptions.makeBasic();
|
||||
optionsMusicVolumeChanged.setDeliveryGroupPolicy(
|
||||
BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
|
||||
optionsMusicVolumeChanged.setDeliveryGroupMatchingFilter(filterMusicVolumeChanged);
|
||||
|
||||
final Intent alarmVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
|
||||
alarmVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
|
||||
AudioManager.STREAM_ALARM);
|
||||
final IntentFilter filterAlarmVolumeChanged = new IntentFilter(
|
||||
AudioManager.VOLUME_CHANGED_ACTION);
|
||||
filterAlarmVolumeChanged.addExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
|
||||
AudioManager.STREAM_ALARM);
|
||||
final BroadcastOptions optionsAlarmVolumeChanged = BroadcastOptions.makeBasic();
|
||||
optionsAlarmVolumeChanged.setDeliveryGroupPolicy(
|
||||
BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
|
||||
optionsAlarmVolumeChanged.setDeliveryGroupMatchingFilter(filterAlarmVolumeChanged);
|
||||
|
||||
// Halt all processing so that we get a consistent view
|
||||
mHandlerThread.getLooper().getQueue().postSyncBarrier();
|
||||
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
|
||||
optionsMusicVolumeChanged));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged,
|
||||
optionsAlarmVolumeChanged));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
|
||||
optionsMusicVolumeChanged));
|
||||
|
||||
final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN,
|
||||
getUidForPackage(PACKAGE_GREEN));
|
||||
// Verify that the older musicVolumeChanged has been removed.
|
||||
verifyPendingRecords(queue,
|
||||
List.of(timeTick, alarmVolumeChanged, musicVolumeChanged));
|
||||
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged,
|
||||
optionsAlarmVolumeChanged));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
|
||||
optionsMusicVolumeChanged));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged,
|
||||
optionsAlarmVolumeChanged));
|
||||
// Verify that the older alarmVolumeChanged has been removed.
|
||||
verifyPendingRecords(queue,
|
||||
List.of(timeTick, musicVolumeChanged, alarmVolumeChanged));
|
||||
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
|
||||
optionsMusicVolumeChanged));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(alarmVolumeChanged,
|
||||
optionsAlarmVolumeChanged));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
|
||||
// Verify that the older timeTick has been removed.
|
||||
verifyPendingRecords(queue,
|
||||
List.of(musicVolumeChanged, alarmVolumeChanged, timeTick));
|
||||
}
|
||||
|
||||
private Intent createPackageChangedIntent(int uid, List<String> componentNameList) {
|
||||
final Intent packageChangedIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED);
|
||||
packageChangedIntent.putExtra(Intent.EXTRA_UID, uid);
|
||||
|
||||
@@ -36,13 +36,17 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -176,9 +180,8 @@ public class BroadcastRecordTest {
|
||||
intent.putExtra(Intent.EXTRA_INDEX, 42);
|
||||
final BroadcastRecord r = createBroadcastRecord(
|
||||
List.of(createResolveInfo(PACKAGE1, getAppId(1))), UserHandle.USER_ALL, intent,
|
||||
(uid, extras) -> {
|
||||
return Bundle.EMPTY;
|
||||
});
|
||||
(uid, extras) -> Bundle.EMPTY,
|
||||
null /* options */);
|
||||
final Intent actual = r.getReceiverIntent(r.receivers.get(0));
|
||||
assertEquals(PACKAGE1, actual.getComponent().getPackageName());
|
||||
assertEquals(-1, actual.getIntExtra(Intent.EXTRA_INDEX, -1));
|
||||
@@ -192,9 +195,8 @@ public class BroadcastRecordTest {
|
||||
intent.putExtra(Intent.EXTRA_INDEX, 42);
|
||||
final BroadcastRecord r = createBroadcastRecord(
|
||||
List.of(createResolveInfo(PACKAGE1, getAppId(1))), UserHandle.USER_ALL, intent,
|
||||
(uid, extras) -> {
|
||||
return null;
|
||||
});
|
||||
(uid, extras) -> null,
|
||||
null /* options */);
|
||||
final Intent actual = r.getReceiverIntent(r.receivers.get(0));
|
||||
assertNull(actual);
|
||||
assertNull(r.intent.getComponent());
|
||||
@@ -290,6 +292,122 @@ public class BroadcastRecordTest {
|
||||
assertEquals(origReceiversSize, br.receivers.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesDeliveryGroup() {
|
||||
final List<ResolveInfo> receivers = List.of(createResolveInfo(PACKAGE1, getAppId(1)));
|
||||
|
||||
final Intent intent1 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent1.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
intent1.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
|
||||
final BroadcastOptions options1 = BroadcastOptions.makeBasic();
|
||||
final BroadcastRecord record1 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent1, options1);
|
||||
|
||||
final Intent intent2 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent2.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
intent2.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
|
||||
final BroadcastOptions options2 = BroadcastOptions.makeBasic();
|
||||
final BroadcastRecord record2 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent2, options2);
|
||||
|
||||
assertTrue(record2.matchesDeliveryGroup(record1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesDeliveryGroup_withMatchingKey() {
|
||||
final List<ResolveInfo> receivers = List.of(createResolveInfo(PACKAGE1, getAppId(1)));
|
||||
|
||||
final Intent intent1 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent1.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
intent1.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
|
||||
final BroadcastOptions options1 = BroadcastOptions.makeBasic();
|
||||
options1.setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE, "key1");
|
||||
final BroadcastRecord record1 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent1, options1);
|
||||
|
||||
final Intent intent2 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent2.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
intent2.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
|
||||
final BroadcastOptions options2 = BroadcastOptions.makeBasic();
|
||||
options2.setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE, "key2");
|
||||
final BroadcastRecord record2 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent2, options2);
|
||||
|
||||
final Intent intent3 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent3.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 1);
|
||||
intent3.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 3);
|
||||
final BroadcastOptions options3 = BroadcastOptions.makeBasic();
|
||||
options3.setDeliveryGroupMatchingKey(Intent.ACTION_SERVICE_STATE, "key1");
|
||||
final BroadcastRecord record3 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent3, options3);
|
||||
|
||||
// record2 and record1 have different matching keys, so their delivery groups
|
||||
// shouldn't match
|
||||
assertFalse(record2.matchesDeliveryGroup(record1));
|
||||
// record3 and record2 have different matching keys, so their delivery groups
|
||||
// shouldn't match
|
||||
assertFalse(record3.matchesDeliveryGroup(record2));
|
||||
// record3 and record1 have same matching keys, so their delivery groups should match even
|
||||
// if the intent has different extras.
|
||||
assertTrue(record3.matchesDeliveryGroup(record1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatchesDeliveryGroup_withMatchingFilter() {
|
||||
final List<ResolveInfo> receivers = List.of(createResolveInfo(PACKAGE1, getAppId(1)));
|
||||
|
||||
final Intent intent1 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent1.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
intent1.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
|
||||
intent1.putExtra(Intent.EXTRA_REASON, "reason1");
|
||||
final IntentFilter filter1 = new IntentFilter(Intent.ACTION_SERVICE_STATE);
|
||||
final PersistableBundle bundle1 = new PersistableBundle();
|
||||
bundle1.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
bundle1.putInt(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
|
||||
filter1.setExtras(bundle1);
|
||||
final BroadcastOptions options1 = BroadcastOptions.makeBasic();
|
||||
options1.setDeliveryGroupMatchingFilter(filter1);
|
||||
final BroadcastRecord record1 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent1, options1);
|
||||
|
||||
final Intent intent2 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent2.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
intent2.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
|
||||
intent2.putExtra(Intent.EXTRA_REASON, "reason2");
|
||||
final IntentFilter filter2 = new IntentFilter(Intent.ACTION_SERVICE_STATE);
|
||||
final PersistableBundle bundle2 = new PersistableBundle();
|
||||
bundle2.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
bundle2.putInt(SubscriptionManager.EXTRA_SLOT_INDEX, 2);
|
||||
filter2.setExtras(bundle2);
|
||||
final BroadcastOptions options2 = BroadcastOptions.makeBasic();
|
||||
options2.setDeliveryGroupMatchingFilter(filter2);
|
||||
final BroadcastRecord record2 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent2, options2);
|
||||
|
||||
final Intent intent3 = new Intent(Intent.ACTION_SERVICE_STATE);
|
||||
intent3.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 1);
|
||||
intent3.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 3);
|
||||
intent3.putExtra(Intent.EXTRA_REASON, "reason3");
|
||||
final IntentFilter filter3 = new IntentFilter(Intent.ACTION_SERVICE_STATE);
|
||||
final PersistableBundle bundle3 = new PersistableBundle();
|
||||
bundle3.putInt(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, 0);
|
||||
bundle3.putInt(SubscriptionManager.EXTRA_SLOT_INDEX, 1);
|
||||
filter3.setExtras(bundle3);
|
||||
final BroadcastOptions options3 = BroadcastOptions.makeBasic();
|
||||
options3.setDeliveryGroupMatchingFilter(filter3);
|
||||
final BroadcastRecord record3 = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
intent3, options3);
|
||||
|
||||
// record2's matchingFilter doesn't match record1's intent, so their delivery groups
|
||||
// shouldn't match
|
||||
assertFalse(record2.matchesDeliveryGroup(record1));
|
||||
// record3's matchingFilter doesn't match record2's intent, so their delivery groups
|
||||
// shouldn't match
|
||||
assertFalse(record3.matchesDeliveryGroup(record2));
|
||||
// record3's matchingFilter matches record1's intent, so their delivery groups should match.
|
||||
assertTrue(record3.matchesDeliveryGroup(record1));
|
||||
}
|
||||
|
||||
private BroadcastRecord createBootCompletedBroadcastRecord(String action) {
|
||||
final List<ResolveInfo> receivers = createReceiverInfos(PACKAGE_LIST, USER_LIST);
|
||||
final BroadcastRecord br = createBroadcastRecord(receivers, UserHandle.USER_ALL,
|
||||
@@ -539,11 +657,19 @@ public class BroadcastRecordTest {
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
|
||||
Intent intent) {
|
||||
return createBroadcastRecord(receivers, userId, intent, null);
|
||||
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
|
||||
null /* options */);
|
||||
}
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
|
||||
Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
|
||||
Intent intent, BroadcastOptions options) {
|
||||
return createBroadcastRecord(receivers, userId, intent, null /* filterExtrasForReceiver */,
|
||||
options);
|
||||
}
|
||||
|
||||
private BroadcastRecord createBroadcastRecord(List<ResolveInfo> receivers, int userId,
|
||||
Intent intent, BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver,
|
||||
BroadcastOptions options) {
|
||||
return new BroadcastRecord(
|
||||
mQueue /* queue */,
|
||||
intent,
|
||||
@@ -558,7 +684,7 @@ public class BroadcastRecordTest {
|
||||
null /* excludedPermissions */,
|
||||
null /* excludedPackages */,
|
||||
0 /* appOp */,
|
||||
null /* options */,
|
||||
options,
|
||||
new ArrayList<>(receivers), // Make a copy to not affect the original list.
|
||||
null /* resultToApp */,
|
||||
null /* resultTo */,
|
||||
|
||||
Reference in New Issue
Block a user