Merge "Allow senders to specify the delivery group via matching filters."
This commit is contained in:
committed by
Android (Google) Code Review
commit
6fe293bbae
@@ -783,13 +783,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);
|
||||
@@ -3218,7 +3224,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +66,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.
|
||||
@@ -206,10 +207,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)}.
|
||||
@@ -217,6 +218,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.
|
||||
@@ -304,9 +311,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -733,7 +742,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
|
||||
@@ -745,22 +754,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -773,15 +852,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)
|
||||
@@ -837,8 +933,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) {
|
||||
@@ -849,6 +945,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