Add infinitely deferred broadcasts.
- Adds a flag that prevents a broadcast from being delivered to a cached app until that app is no longer cached. - As of this CL, this flag is only honored for unordered broadcasts to runtime receivers. Ordered broadcasts, alarm/interactive broadcasts, and manifest broadcasts are never deferred. - This CL prevents the modern queue from cold starting processes for manifest receivers that would not be eligible to receive a particular broadcast, as in the case of USER_PRESENT. - Unordered broadcasts with a completion callback (ie, resultTo broadcasts) are always marked as infinitely-deferred. resultTo broadcasts must still be dispatched only to runtime receivers or manifest receivers that disallow process starts in order to be deferred. - This CL marks various BATTERY_CHANGED-esque broadcasts as infinitely deferred. - This CL marks SCREEN_ON and SCREEN_OFF broadcasts as infinitely deferred. - This CL marks USER_PRESENT broadcasts are infinitely deferred. Test: atest BroadcastDeliveryGroupTest Test: atest BroadcastQueueModernImplTest Test: atest BroadcastQueueTest Bug: 261065790 Bug: 263141882 Bug: 263143790 Change-Id: I9a356c8701d73f5687831fbe5b699e4485f07dd5
This commit is contained in:
@@ -806,10 +806,12 @@ package android.app {
|
||||
method @Nullable public android.content.IntentFilter getDeliveryGroupMatchingFilter();
|
||||
method @Nullable public String getDeliveryGroupMatchingKey();
|
||||
method public int getDeliveryGroupPolicy();
|
||||
method public boolean isDeferUntilActive();
|
||||
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 @NonNull public android.app.BroadcastOptions setDeferUntilActive(boolean);
|
||||
method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingFilter(@NonNull android.content.IntentFilter);
|
||||
method @NonNull public android.app.BroadcastOptions setDeliveryGroupMatchingKey(@NonNull String, @NonNull String);
|
||||
method @NonNull public android.app.BroadcastOptions setDeliveryGroupPolicy(int);
|
||||
|
||||
@@ -63,6 +63,7 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
private long mRequireCompatChangeId = CHANGE_INVALID;
|
||||
private boolean mRequireCompatChangeEnabled = true;
|
||||
private boolean mIsAlarmBroadcast = false;
|
||||
private boolean mIsDeferUntilActive = false;
|
||||
private long mIdForResponseEvent;
|
||||
private @Nullable IntentFilter mRemoveMatchingFilter;
|
||||
private @DeliveryGroupPolicy int mDeliveryGroupPolicy;
|
||||
@@ -200,6 +201,12 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
private static final String KEY_REMOVE_MATCHING_FILTER =
|
||||
"android:broadcast.removeMatchingFilter";
|
||||
|
||||
/**
|
||||
* Corresponds to {@link #setDeferUntilActive(boolean)}.
|
||||
*/
|
||||
private static final String KEY_DEFER_UNTIL_ACTIVE =
|
||||
"android:broadcast.deferuntilactive";
|
||||
|
||||
/**
|
||||
* Corresponds to {@link #setDeliveryGroupPolicy(int)}.
|
||||
*/
|
||||
@@ -320,6 +327,7 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
BundleMerger.class);
|
||||
mDeliveryGroupMatchingFilter = opts.getParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER,
|
||||
IntentFilter.class);
|
||||
mIsDeferUntilActive = opts.getBoolean(KEY_DEFER_UNTIL_ACTIVE, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -699,6 +707,41 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
return mIdForResponseEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the broadcast should not run until the process is in an active process state
|
||||
* (ie, a process exists for the app and the app is not in a cached process state).
|
||||
*
|
||||
* Whether an app's process state is considered active is independent of its standby bucket.
|
||||
*
|
||||
* A broadcast that is deferred until the process is active will not execute until the process
|
||||
* is brought to an active state by some other action, like a job, alarm, or service binding. As
|
||||
* a result, the broadcast may be delayed indefinitely. This deferral only applies to runtime
|
||||
* registered receivers of a broadcast. Any manifest receivers will run immediately, similar to
|
||||
* how a manifest receiver would start a new process in order to run a broadcast receiver.
|
||||
*
|
||||
* Ordered broadcasts, alarm broadcasts, interactive broadcasts, and manifest broadcasts are
|
||||
* never deferred.
|
||||
*
|
||||
* Unordered broadcasts and unordered broadcasts with completion callbacks may be
|
||||
* deferred. Completion callbacks for broadcasts deferred until active are
|
||||
* best-effort. Completion callbacks will run when all eligible processes have finished
|
||||
* executing the broadcast. Processes in inactive process states that defer the broadcast are
|
||||
* not considered eligible and may not execute the broadcast prior to the completion callback.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public @NonNull BroadcastOptions setDeferUntilActive(boolean shouldDefer) {
|
||||
mIsDeferUntilActive = shouldDefer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@SystemApi
|
||||
public boolean isDeferUntilActive() {
|
||||
return mIsDeferUntilActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* When enqueuing this broadcast, remove all pending broadcasts previously
|
||||
* sent by this app which match the given filter.
|
||||
@@ -963,6 +1006,9 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
if (mDeliveryGroupMatchingFilter != null) {
|
||||
b.putParcelable(KEY_DELIVERY_GROUP_MATCHING_FILTER, mDeliveryGroupMatchingFilter);
|
||||
}
|
||||
if (mIsDeferUntilActive) {
|
||||
b.putBoolean(KEY_DEFER_UNTIL_ACTIVE, mIsDeferUntilActive);
|
||||
}
|
||||
return b.isEmpty() ? null : b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.AlarmManager;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.StatusBarManager;
|
||||
import android.app.WindowConfiguration;
|
||||
@@ -388,6 +389,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
| Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
|
||||
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
|
||||
|
||||
private static final Bundle USER_PRESENT_INTENT_OPTIONS =
|
||||
BroadcastOptions.makeBasic()
|
||||
.setDeferUntilActive(true)
|
||||
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
|
||||
.toBundle();
|
||||
|
||||
/**
|
||||
* {@link #setKeyguardEnabled} waits on this condition when it re-enables
|
||||
* the keyguard.
|
||||
@@ -2300,7 +2307,10 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
|
||||
Context.USER_SERVICE);
|
||||
mUiBgExecutor.execute(() -> {
|
||||
for (int profileId : um.getProfileIdsWithDisabled(currentUser.getIdentifier())) {
|
||||
mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, UserHandle.of(profileId));
|
||||
mContext.sendBroadcastAsUser(USER_PRESENT_INTENT,
|
||||
UserHandle.of(profileId),
|
||||
null,
|
||||
USER_PRESENT_INTENT_OPTIONS);
|
||||
}
|
||||
mLockPatternUtils.userPresent(currentUserId);
|
||||
});
|
||||
|
||||
@@ -189,15 +189,20 @@ public final class BatteryService extends SystemService {
|
||||
private long mLastBatteryLevelChangedSentMs;
|
||||
|
||||
private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).toBundle();
|
||||
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).setDeferUntilActive(true)
|
||||
.toBundle();
|
||||
private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).toBundle();
|
||||
new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).setDeferUntilActive(true)
|
||||
.toBundle();
|
||||
private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_POWER_CONNECTED)).toBundle();
|
||||
new IntentFilter(Intent.ACTION_POWER_CONNECTED)).setDeferUntilActive(true)
|
||||
.toBundle();
|
||||
private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_BATTERY_OKAY)).toBundle();
|
||||
new IntentFilter(Intent.ACTION_BATTERY_OKAY)).setDeferUntilActive(true)
|
||||
.toBundle();
|
||||
private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_BATTERY_LOW)).toBundle();
|
||||
new IntentFilter(Intent.ACTION_BATTERY_LOW)).setDeferUntilActive(true)
|
||||
.toBundle();
|
||||
|
||||
private MetricsLogger mMetricsLogger;
|
||||
|
||||
|
||||
@@ -14108,8 +14108,16 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
|
||||
(sticky ? "Broadcast sticky: ": "Broadcast: ") + intent
|
||||
+ " ordered=" + ordered + " userid=" + userId);
|
||||
if ((resultTo != null) && !ordered && !mEnableModernQueue) {
|
||||
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
|
||||
if ((resultTo != null) && !ordered) {
|
||||
if (!mEnableModernQueue) {
|
||||
Slog.w(TAG, "Broadcast " + intent + " not ordered but result callback requested!");
|
||||
}
|
||||
if (!UserHandle.isCore(callingUid)) {
|
||||
String msg = "Unauthorized unordered resultTo broadcast "
|
||||
+ intent + " sent from uid " + callingUid;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
|
||||
@@ -14190,6 +14198,18 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
// resultTo broadcasts are always infinitely deferrable.
|
||||
if ((resultTo != null) && !ordered && mEnableModernQueue) {
|
||||
if (brOptions == null) {
|
||||
brOptions = BroadcastOptions.makeBasic();
|
||||
}
|
||||
brOptions.setDeferUntilActive(true);
|
||||
}
|
||||
|
||||
if (ordered && brOptions != null && brOptions.isDeferUntilActive()) {
|
||||
throw new IllegalArgumentException("Ordered broadcasts can't be deferred until active");
|
||||
}
|
||||
|
||||
// Verify that protected broadcasts are only being sent by system code,
|
||||
// and that system code is only sending protected broadcasts.
|
||||
final boolean isProtectedBroadcast;
|
||||
|
||||
@@ -62,6 +62,7 @@ import java.util.Objects;
|
||||
*/
|
||||
// @NotThreadSafe
|
||||
class BroadcastProcessQueue {
|
||||
static final boolean VERBOSE = false;
|
||||
final @NonNull BroadcastConstants constants;
|
||||
final @NonNull String processName;
|
||||
final int uid;
|
||||
@@ -168,10 +169,14 @@ class BroadcastProcessQueue {
|
||||
/**
|
||||
* Count of pending broadcasts of these various flavors.
|
||||
*/
|
||||
private int mCountEnqueued;
|
||||
private int mCountDeferred;
|
||||
private int mCountForeground;
|
||||
private int mCountForegroundDeferred;
|
||||
private int mCountOrdered;
|
||||
private int mCountAlarm;
|
||||
private int mCountPrioritized;
|
||||
private int mCountPrioritizedDeferred;
|
||||
private int mCountInteractive;
|
||||
private int mCountResultTo;
|
||||
private int mCountInstrumented;
|
||||
@@ -226,10 +231,10 @@ class BroadcastProcessQueue {
|
||||
* used for ordered broadcasts and priority traunches.
|
||||
*/
|
||||
public void enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
|
||||
@NonNull BroadcastConsumer replacedBroadcastConsumer) {
|
||||
@NonNull BroadcastConsumer replacedBroadcastConsumer, boolean wouldBeSkipped) {
|
||||
if (record.isReplacePending()) {
|
||||
final boolean didReplace = replaceBroadcast(record, recordIndex,
|
||||
replacedBroadcastConsumer);
|
||||
replacedBroadcastConsumer, wouldBeSkipped);
|
||||
if (didReplace) {
|
||||
return;
|
||||
}
|
||||
@@ -240,13 +245,14 @@ class BroadcastProcessQueue {
|
||||
SomeArgs newBroadcastArgs = SomeArgs.obtain();
|
||||
newBroadcastArgs.arg1 = record;
|
||||
newBroadcastArgs.argi1 = recordIndex;
|
||||
newBroadcastArgs.argi2 = (wouldBeSkipped ? 1 : 0);
|
||||
|
||||
// Cross-broadcast prioritization policy: some broadcasts might warrant being
|
||||
// issued ahead of others that are already pending, for example if this new
|
||||
// broadcast is in a different delivery class or is tied to a direct user interaction
|
||||
// with implicit responsiveness expectations.
|
||||
getQueueForBroadcast(record).addLast(newBroadcastArgs);
|
||||
onBroadcastEnqueued(record, recordIndex);
|
||||
onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,11 +264,12 @@ class BroadcastProcessQueue {
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
private boolean replaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
|
||||
@NonNull BroadcastConsumer replacedBroadcastConsumer) {
|
||||
@NonNull BroadcastConsumer replacedBroadcastConsumer, boolean wouldBeSkipped) {
|
||||
final int count = mPendingQueues.size();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
final ArrayDeque<SomeArgs> queue = mPendingQueues.get(i);
|
||||
if (replaceBroadcastInQueue(queue, record, recordIndex, replacedBroadcastConsumer)) {
|
||||
if (replaceBroadcastInQueue(queue, record, recordIndex,
|
||||
replacedBroadcastConsumer, wouldBeSkipped)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -279,13 +286,15 @@ class BroadcastProcessQueue {
|
||||
*/
|
||||
private boolean replaceBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
|
||||
@NonNull BroadcastRecord record, int recordIndex,
|
||||
@NonNull BroadcastConsumer replacedBroadcastConsumer) {
|
||||
@NonNull BroadcastConsumer replacedBroadcastConsumer,
|
||||
boolean wouldBeSkipped) {
|
||||
final Iterator<SomeArgs> it = queue.descendingIterator();
|
||||
final Object receiver = record.receivers.get(recordIndex);
|
||||
while (it.hasNext()) {
|
||||
final SomeArgs args = it.next();
|
||||
final BroadcastRecord testRecord = (BroadcastRecord) args.arg1;
|
||||
final int testRecordIndex = args.argi1;
|
||||
final boolean testWouldBeSkipped = (args.argi2 == 1);
|
||||
final Object testReceiver = testRecord.receivers.get(testRecordIndex);
|
||||
if ((record.callingUid == testRecord.callingUid)
|
||||
&& (record.userId == testRecord.userId)
|
||||
@@ -295,9 +304,10 @@ class BroadcastProcessQueue {
|
||||
// Exact match found; perform in-place swap
|
||||
args.arg1 = record;
|
||||
args.argi1 = recordIndex;
|
||||
args.argi2 = (wouldBeSkipped ? 1 : 0);
|
||||
record.copyEnqueueTimeFrom(testRecord);
|
||||
onBroadcastDequeued(testRecord, testRecordIndex);
|
||||
onBroadcastEnqueued(record, recordIndex);
|
||||
onBroadcastDequeued(testRecord, testRecordIndex, testWouldBeSkipped);
|
||||
onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
|
||||
replacedBroadcastConsumer.accept(testRecord, testRecordIndex);
|
||||
return true;
|
||||
}
|
||||
@@ -352,12 +362,13 @@ class BroadcastProcessQueue {
|
||||
final SomeArgs args = it.next();
|
||||
final BroadcastRecord record = (BroadcastRecord) args.arg1;
|
||||
final int recordIndex = args.argi1;
|
||||
final boolean recordWouldBeSkipped = (args.argi2 == 1);
|
||||
if (predicate.test(record, recordIndex)) {
|
||||
consumer.accept(record, recordIndex);
|
||||
if (andRemove) {
|
||||
args.recycle();
|
||||
it.remove();
|
||||
onBroadcastDequeued(record, recordIndex);
|
||||
onBroadcastDequeued(record, recordIndex, recordWouldBeSkipped);
|
||||
}
|
||||
didSomething = true;
|
||||
}
|
||||
@@ -423,7 +434,7 @@ class BroadcastProcessQueue {
|
||||
}
|
||||
|
||||
public int getPreferredSchedulingGroupLocked() {
|
||||
if (mCountForeground > 0) {
|
||||
if (mCountForeground > mCountForegroundDeferred) {
|
||||
// We have a foreground broadcast somewhere down the queue, so
|
||||
// boost priority until we drain them all
|
||||
return ProcessList.SCHED_GROUP_DEFAULT;
|
||||
@@ -469,10 +480,11 @@ class BroadcastProcessQueue {
|
||||
final SomeArgs next = removeNextBroadcast();
|
||||
mActive = (BroadcastRecord) next.arg1;
|
||||
mActiveIndex = next.argi1;
|
||||
final boolean wouldBeSkipped = (next.argi2 == 1);
|
||||
mActiveCountSinceIdle++;
|
||||
mActiveViaColdStart = false;
|
||||
next.recycle();
|
||||
onBroadcastDequeued(mActive, mActiveIndex);
|
||||
onBroadcastDequeued(mActive, mActiveIndex, wouldBeSkipped);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -489,8 +501,16 @@ class BroadcastProcessQueue {
|
||||
/**
|
||||
* Update summary statistics when the given record has been enqueued.
|
||||
*/
|
||||
private void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex) {
|
||||
private void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex,
|
||||
boolean wouldBeSkipped) {
|
||||
mCountEnqueued++;
|
||||
if (record.deferUntilActive) {
|
||||
mCountDeferred++;
|
||||
}
|
||||
if (record.isForeground()) {
|
||||
if (record.deferUntilActive) {
|
||||
mCountForegroundDeferred++;
|
||||
}
|
||||
mCountForeground++;
|
||||
}
|
||||
if (record.ordered) {
|
||||
@@ -500,6 +520,9 @@ class BroadcastProcessQueue {
|
||||
mCountAlarm++;
|
||||
}
|
||||
if (record.prioritized) {
|
||||
if (record.deferUntilActive) {
|
||||
mCountPrioritizedDeferred++;
|
||||
}
|
||||
mCountPrioritized++;
|
||||
}
|
||||
if (record.interactive) {
|
||||
@@ -511,7 +534,8 @@ class BroadcastProcessQueue {
|
||||
if (record.callerInstrumented) {
|
||||
mCountInstrumented++;
|
||||
}
|
||||
if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
|
||||
if (!wouldBeSkipped
|
||||
&& (record.receivers.get(recordIndex) instanceof ResolveInfo)) {
|
||||
mCountManifest++;
|
||||
}
|
||||
invalidateRunnableAt();
|
||||
@@ -520,8 +544,16 @@ class BroadcastProcessQueue {
|
||||
/**
|
||||
* Update summary statistics when the given record has been dequeued.
|
||||
*/
|
||||
private void onBroadcastDequeued(@NonNull BroadcastRecord record, int recordIndex) {
|
||||
private void onBroadcastDequeued(@NonNull BroadcastRecord record, int recordIndex,
|
||||
boolean wouldBeSkipped) {
|
||||
mCountEnqueued--;
|
||||
if (record.deferUntilActive) {
|
||||
mCountDeferred--;
|
||||
}
|
||||
if (record.isForeground()) {
|
||||
if (record.deferUntilActive) {
|
||||
mCountForegroundDeferred--;
|
||||
}
|
||||
mCountForeground--;
|
||||
}
|
||||
if (record.ordered) {
|
||||
@@ -531,6 +563,9 @@ class BroadcastProcessQueue {
|
||||
mCountAlarm--;
|
||||
}
|
||||
if (record.prioritized) {
|
||||
if (record.deferUntilActive) {
|
||||
mCountPrioritizedDeferred--;
|
||||
}
|
||||
mCountPrioritized--;
|
||||
}
|
||||
if (record.interactive) {
|
||||
@@ -542,7 +577,8 @@ class BroadcastProcessQueue {
|
||||
if (record.callerInstrumented) {
|
||||
mCountInstrumented--;
|
||||
}
|
||||
if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
|
||||
if (!wouldBeSkipped
|
||||
&& (record.receivers.get(recordIndex) instanceof ResolveInfo)) {
|
||||
mCountManifest--;
|
||||
}
|
||||
invalidateRunnableAt();
|
||||
@@ -741,6 +777,15 @@ class BroadcastProcessQueue {
|
||||
return mRunnableAt != Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
public boolean isDeferredUntilActive() {
|
||||
if (mRunnableAtInvalidated) updateRunnableAt();
|
||||
return mRunnableAtReason == BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER;
|
||||
}
|
||||
|
||||
public boolean hasDeferredBroadcasts() {
|
||||
return (mCountDeferred > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return time at which this process is considered runnable. This is
|
||||
* typically the time at which the next pending broadcast was first
|
||||
@@ -776,6 +821,7 @@ class BroadcastProcessQueue {
|
||||
static final int REASON_INSTRUMENTED = 5;
|
||||
static final int REASON_PERSISTENT = 6;
|
||||
static final int REASON_FORCE_DELAYED = 7;
|
||||
static final int REASON_CACHED_INFINITE_DEFER = 8;
|
||||
static final int REASON_CONTAINS_FOREGROUND = 10;
|
||||
static final int REASON_CONTAINS_ORDERED = 11;
|
||||
static final int REASON_CONTAINS_ALARM = 12;
|
||||
@@ -794,6 +840,7 @@ class BroadcastProcessQueue {
|
||||
REASON_INSTRUMENTED,
|
||||
REASON_PERSISTENT,
|
||||
REASON_FORCE_DELAYED,
|
||||
REASON_CACHED_INFINITE_DEFER,
|
||||
REASON_CONTAINS_FOREGROUND,
|
||||
REASON_CONTAINS_ORDERED,
|
||||
REASON_CONTAINS_ALARM,
|
||||
@@ -816,6 +863,7 @@ class BroadcastProcessQueue {
|
||||
case REASON_INSTRUMENTED: return "INSTRUMENTED";
|
||||
case REASON_PERSISTENT: return "PERSISTENT";
|
||||
case REASON_FORCE_DELAYED: return "FORCE_DELAYED";
|
||||
case REASON_CACHED_INFINITE_DEFER: return "INFINITE_DEFER";
|
||||
case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND";
|
||||
case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED";
|
||||
case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM";
|
||||
@@ -831,9 +879,16 @@ class BroadcastProcessQueue {
|
||||
private boolean blockedOnOrderedDispatch(BroadcastRecord r, int index) {
|
||||
final int blockedUntilTerminalCount = r.blockedUntilTerminalCount[index];
|
||||
|
||||
int existingDeferredCount = 0;
|
||||
if (r.deferUntilActive) {
|
||||
for (int i = 0; i < index; i++) {
|
||||
if (r.deferredUntilActive[i]) existingDeferredCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// We might be blocked waiting for other receivers to finish,
|
||||
// typically for an ordered broadcast or priority traunches
|
||||
if (r.terminalCount < blockedUntilTerminalCount
|
||||
if ((r.terminalCount + existingDeferredCount) < blockedUntilTerminalCount
|
||||
&& !isDeliveryStateTerminal(r.getDeliveryState(index))) {
|
||||
return true;
|
||||
}
|
||||
@@ -862,7 +917,7 @@ class BroadcastProcessQueue {
|
||||
if (mForcedDelayedDurationMs > 0) {
|
||||
mRunnableAt = runnableAt + mForcedDelayedDurationMs;
|
||||
mRunnableAtReason = REASON_FORCE_DELAYED;
|
||||
} else if (mCountForeground > 0) {
|
||||
} else if (mCountForeground > mCountForegroundDeferred) {
|
||||
mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
|
||||
mRunnableAtReason = REASON_CONTAINS_FOREGROUND;
|
||||
} else if (mCountInteractive > 0) {
|
||||
@@ -880,12 +935,9 @@ class BroadcastProcessQueue {
|
||||
} else if (mCountAlarm > 0) {
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_CONTAINS_ALARM;
|
||||
} else if (mCountPrioritized > 0) {
|
||||
} else if (mCountPrioritized > mCountPrioritizedDeferred) {
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_CONTAINS_PRIORITIZED;
|
||||
} else if (mCountResultTo > 0) {
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
|
||||
} else if (mCountManifest > 0) {
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_CONTAINS_MANIFEST;
|
||||
@@ -893,8 +945,39 @@ class BroadcastProcessQueue {
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_PERSISTENT;
|
||||
} else if (mProcessCached) {
|
||||
mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
|
||||
mRunnableAtReason = REASON_CACHED;
|
||||
if (r.deferUntilActive) {
|
||||
// All enqueued broadcasts are deferrable, defer
|
||||
if (mCountDeferred == mCountEnqueued) {
|
||||
mRunnableAt = Long.MAX_VALUE;
|
||||
mRunnableAtReason = REASON_CACHED_INFINITE_DEFER;
|
||||
} else {
|
||||
// At least one enqueued broadcast isn't deferrable, repick time and reason
|
||||
// for this record. If a later record is not deferrable and is one of these
|
||||
// special cases, one of the cases above would have already caught that.
|
||||
if (r.isForeground()) {
|
||||
mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
|
||||
mRunnableAtReason = REASON_CONTAINS_FOREGROUND;
|
||||
} else if (r.prioritized) {
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_CONTAINS_PRIORITIZED;
|
||||
} else if (r.resultTo != null) {
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
|
||||
} else {
|
||||
mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
|
||||
mRunnableAtReason = REASON_CACHED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This record isn't deferrable
|
||||
mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
|
||||
mRunnableAtReason = REASON_CACHED;
|
||||
}
|
||||
} else if (mCountResultTo > 0) {
|
||||
// All resultTo broadcasts are infinitely deferrable, so if the app
|
||||
// is already cached, they'll be deferred on the line above
|
||||
mRunnableAt = runnableAt;
|
||||
mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
|
||||
} else {
|
||||
mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
|
||||
mRunnableAtReason = REASON_NORMAL;
|
||||
@@ -907,6 +990,15 @@ class BroadcastProcessQueue {
|
||||
mRunnableAt = Math.min(mRunnableAt, runnableAt);
|
||||
mRunnableAtReason = REASON_MAX_PENDING;
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, "BroadcastQueue",
|
||||
((app != null) ? app.processName : "(null)")
|
||||
+ ":" + r.intent.toString() + ":"
|
||||
+ r.deferUntilActive
|
||||
+ ":" + mRunnableAt + " " + reasonToString(mRunnableAtReason)
|
||||
+ ":" + ((app != null) ? app.isCached() : "false"));
|
||||
}
|
||||
} else {
|
||||
mRunnableAt = Long.MAX_VALUE;
|
||||
mRunnableAtReason = REASON_EMPTY;
|
||||
|
||||
@@ -385,6 +385,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
// If app isn't running, and there's nothing in the queue, clean up
|
||||
if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
|
||||
removeProcessQueue(queue.processName, queue.uid);
|
||||
} else {
|
||||
updateQueueDeferred(queue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -637,11 +639,34 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
final ArraySet<BroadcastRecord> replacedBroadcasts = new ArraySet<>();
|
||||
final BroadcastConsumer replacedBroadcastConsumer =
|
||||
(record, i) -> replacedBroadcasts.add(record);
|
||||
boolean enqueuedBroadcast = false;
|
||||
|
||||
for (int i = 0; i < r.receivers.size(); i++) {
|
||||
final Object receiver = r.receivers.get(i);
|
||||
final BroadcastProcessQueue queue = getOrCreateProcessQueue(
|
||||
getReceiverProcessName(receiver), getReceiverUid(receiver));
|
||||
queue.enqueueOrReplaceBroadcast(r, i, replacedBroadcastConsumer);
|
||||
|
||||
boolean wouldBeSkipped = false;
|
||||
if (receiver instanceof ResolveInfo) {
|
||||
// If the app is running but would not have been started if the process weren't
|
||||
// running, we're going to deliver the broadcast but mark that it's not a manifest
|
||||
// broadcast that would have started the app. This allows BroadcastProcessQueue to
|
||||
// defer the broadcast as though it were a normal runtime receiver.
|
||||
wouldBeSkipped = (mSkipPolicy.shouldSkipMessage(r, receiver) != null);
|
||||
if (wouldBeSkipped && queue.app == null && !queue.getActiveViaColdStart()) {
|
||||
// Skip receiver if there's no running app, the app is not being started, and
|
||||
// the app wouldn't be launched for this broadcast
|
||||
setDeliveryState(null, null, r, i, receiver, BroadcastRecord.DELIVERY_SKIPPED,
|
||||
"skipped by policy to avoid cold start");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
enqueuedBroadcast = true;
|
||||
queue.enqueueOrReplaceBroadcast(r, i, replacedBroadcastConsumer, wouldBeSkipped);
|
||||
if (r.isDeferUntilActive() && queue.isDeferredUntilActive()) {
|
||||
setDeliveryState(queue, null, r, i, receiver, BroadcastRecord.DELIVERY_DEFERRED,
|
||||
"deferred at enqueue time");
|
||||
}
|
||||
updateRunnableList(queue);
|
||||
enqueueUpdateRunningList();
|
||||
}
|
||||
@@ -651,7 +676,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
skipAndCancelReplacedBroadcasts(replacedBroadcasts);
|
||||
|
||||
// If nothing to dispatch, send any pending result immediately
|
||||
if (r.receivers.isEmpty()) {
|
||||
if (r.receivers.isEmpty() || !enqueuedBroadcast) {
|
||||
scheduleResultTo(r);
|
||||
notifyFinishBroadcast(r);
|
||||
}
|
||||
@@ -1195,11 +1220,19 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
@NonNull Object receiver, @DeliveryState int newDeliveryState, String reason) {
|
||||
final int cookie = traceBegin("setDeliveryState");
|
||||
final int oldDeliveryState = getDeliveryState(r, index);
|
||||
boolean checkFinished = false;
|
||||
|
||||
// Only apply state when we haven't already reached a terminal state;
|
||||
// this is how we ignore racing timeout messages
|
||||
if (!isDeliveryStateTerminal(oldDeliveryState)) {
|
||||
r.setDeliveryState(index, newDeliveryState);
|
||||
if (oldDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
|
||||
r.deferredCount--;
|
||||
} else if (newDeliveryState == BroadcastRecord.DELIVERY_DEFERRED) {
|
||||
// If we're deferring a broadcast, maybe that's enough to unblock the final callback
|
||||
r.deferredCount++;
|
||||
checkFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Emit any relevant tracing results when we're changing the delivery
|
||||
@@ -1217,7 +1250,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
// bookkeeping to update for ordered broadcasts
|
||||
if (!isDeliveryStateTerminal(oldDeliveryState)
|
||||
&& isDeliveryStateTerminal(newDeliveryState)) {
|
||||
if (newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
|
||||
if (DEBUG_BROADCAST
|
||||
&& newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
|
||||
logw("Delivery state of " + r + " to " + receiver
|
||||
+ " via " + app + " changed from "
|
||||
+ deliveryStateToString(oldDeliveryState) + " to "
|
||||
@@ -1226,9 +1260,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
|
||||
r.terminalCount++;
|
||||
notifyFinishReceiver(queue, r, index, receiver);
|
||||
|
||||
// When entire ordered broadcast finished, deliver final result
|
||||
final boolean recordFinished = (r.terminalCount == r.receivers.size());
|
||||
checkFinished = true;
|
||||
}
|
||||
// When entire ordered broadcast finished, deliver final result
|
||||
if (checkFinished) {
|
||||
final boolean recordFinished =
|
||||
((r.terminalCount + r.deferredCount) == r.receivers.size());
|
||||
if (recordFinished) {
|
||||
scheduleResultTo(r);
|
||||
}
|
||||
@@ -1329,6 +1366,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
r.resultExtras = null;
|
||||
};
|
||||
|
||||
private final BroadcastConsumer mBroadcastConsumerDefer = (r, i) -> {
|
||||
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_DEFERRED,
|
||||
"mBroadcastConsumerDefer");
|
||||
};
|
||||
|
||||
private final BroadcastConsumer mBroadcastConsumerUndoDefer = (r, i) -> {
|
||||
setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_PENDING,
|
||||
"mBroadcastConsumerUndoDefer");
|
||||
};
|
||||
|
||||
/**
|
||||
* Verify that all known {@link #mProcessQueues} are in the state tested by
|
||||
* the given {@link Predicate}.
|
||||
@@ -1392,6 +1439,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateQueueDeferred(
|
||||
@NonNull BroadcastProcessQueue leaf) {
|
||||
if (leaf.isDeferredUntilActive()) {
|
||||
leaf.forEachMatchingBroadcast((r, i) -> {
|
||||
return r.deferUntilActive && (r.getDeliveryState(i)
|
||||
== BroadcastRecord.DELIVERY_PENDING);
|
||||
}, mBroadcastConsumerDefer, false);
|
||||
} else if (leaf.hasDeferredBroadcasts()) {
|
||||
leaf.forEachMatchingBroadcast((r, i) -> {
|
||||
return r.deferUntilActive && (r.getDeliveryState(i)
|
||||
== BroadcastRecord.DELIVERY_DEFERRED);
|
||||
}, mBroadcastConsumerUndoDefer, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(@NonNull ContentResolver resolver) {
|
||||
mFgConstants.startObserving(mHandler, resolver);
|
||||
@@ -1404,6 +1466,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
BroadcastProcessQueue leaf = mProcessQueues.get(uid);
|
||||
while (leaf != null) {
|
||||
leaf.setProcessCached(cached);
|
||||
updateQueueDeferred(leaf);
|
||||
updateRunnableList(leaf);
|
||||
leaf = leaf.processNameNext;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ final class BroadcastRecord extends Binder {
|
||||
final boolean interactive; // originated from user interaction?
|
||||
final boolean initialSticky; // initial broadcast from register to sticky?
|
||||
final boolean prioritized; // contains more than one priority tranche
|
||||
final boolean deferUntilActive; // infinitely deferrable broadcast
|
||||
final int userId; // user id this broadcast was for
|
||||
final @Nullable String resolvedType; // the resolved data type
|
||||
final @Nullable String[] requiredPermissions; // permissions the caller has required
|
||||
@@ -97,6 +98,7 @@ final class BroadcastRecord extends Binder {
|
||||
final @Nullable BroadcastOptions options; // BroadcastOptions supplied by caller
|
||||
final @NonNull List<Object> receivers; // contains BroadcastFilter and ResolveInfo
|
||||
final @DeliveryState int[] delivery; // delivery state of each receiver
|
||||
final boolean[] deferredUntilActive; // whether each receiver is infinitely deferred
|
||||
final int[] blockedUntilTerminalCount; // blocked until count of each receiver
|
||||
@Nullable ProcessRecord resultToApp; // who receives final result if non-null
|
||||
@Nullable IIntentReceiver resultTo; // who receives final result if non-null
|
||||
@@ -130,6 +132,7 @@ final class BroadcastRecord extends Binder {
|
||||
int manifestCount; // number of manifest receivers dispatched.
|
||||
int manifestSkipCount; // number of manifest receivers skipped.
|
||||
int terminalCount; // number of receivers in terminal state.
|
||||
int deferredCount; // number of receivers in deferred state.
|
||||
@Nullable BroadcastQueue queue; // the outbound queue handling this broadcast
|
||||
|
||||
// if set to true, app's process will be temporarily allowed to start activities from background
|
||||
@@ -168,6 +171,8 @@ final class BroadcastRecord extends Binder {
|
||||
static final int DELIVERY_SCHEDULED = 4;
|
||||
/** Terminal state: failure to dispatch */
|
||||
static final int DELIVERY_FAILURE = 5;
|
||||
/** Intermediate state: currently deferred while app is cached */
|
||||
static final int DELIVERY_DEFERRED = 6;
|
||||
|
||||
@IntDef(flag = false, prefix = { "DELIVERY_" }, value = {
|
||||
DELIVERY_PENDING,
|
||||
@@ -176,6 +181,7 @@ final class BroadcastRecord extends Binder {
|
||||
DELIVERY_TIMEOUT,
|
||||
DELIVERY_SCHEDULED,
|
||||
DELIVERY_FAILURE,
|
||||
DELIVERY_DEFERRED,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface DeliveryState {}
|
||||
@@ -188,6 +194,7 @@ final class BroadcastRecord extends Binder {
|
||||
case DELIVERY_TIMEOUT: return "TIMEOUT";
|
||||
case DELIVERY_SCHEDULED: return "SCHEDULED";
|
||||
case DELIVERY_FAILURE: return "FAILURE";
|
||||
case DELIVERY_DEFERRED: return "DEFERRED";
|
||||
default: return Integer.toString(deliveryState);
|
||||
}
|
||||
}
|
||||
@@ -388,6 +395,8 @@ final class BroadcastRecord extends Binder {
|
||||
options = _options;
|
||||
receivers = (_receivers != null) ? _receivers : EMPTY_RECEIVERS;
|
||||
delivery = new int[_receivers != null ? _receivers.size() : 0];
|
||||
deferUntilActive = options != null ? options.isDeferUntilActive() : false;
|
||||
deferredUntilActive = new boolean[deferUntilActive ? delivery.length : 0];
|
||||
blockedUntilTerminalCount = calculateBlockedUntilTerminalCount(receivers, _serialized);
|
||||
scheduledTime = new long[delivery.length];
|
||||
terminalTime = new long[delivery.length];
|
||||
@@ -443,6 +452,8 @@ final class BroadcastRecord extends Binder {
|
||||
options = from.options;
|
||||
receivers = from.receivers;
|
||||
delivery = from.delivery;
|
||||
deferUntilActive = from.deferUntilActive;
|
||||
deferredUntilActive = from.deferredUntilActive;
|
||||
blockedUntilTerminalCount = from.blockedUntilTerminalCount;
|
||||
scheduledTime = from.scheduledTime;
|
||||
terminalTime = from.terminalTime;
|
||||
@@ -606,7 +617,7 @@ final class BroadcastRecord extends Binder {
|
||||
*/
|
||||
void setDeliveryState(int index, @DeliveryState int deliveryState) {
|
||||
delivery[index] = deliveryState;
|
||||
|
||||
if (deferUntilActive) deferredUntilActive[index] = false;
|
||||
switch (deliveryState) {
|
||||
case DELIVERY_DELIVERED:
|
||||
case DELIVERY_SKIPPED:
|
||||
@@ -617,6 +628,9 @@ final class BroadcastRecord extends Binder {
|
||||
case DELIVERY_SCHEDULED:
|
||||
scheduledTime[index] = SystemClock.uptimeMillis();
|
||||
break;
|
||||
case DELIVERY_DEFERRED:
|
||||
if (deferUntilActive) deferredUntilActive[index] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -647,6 +661,10 @@ final class BroadcastRecord extends Binder {
|
||||
return (intent.getFlags() & Intent.FLAG_RECEIVER_OFFLOAD) != 0;
|
||||
}
|
||||
|
||||
boolean isDeferUntilActive() {
|
||||
return deferUntilActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Core policy determination about this broadcast's delivery prioritization
|
||||
*/
|
||||
|
||||
@@ -229,12 +229,7 @@ public class BroadcastSkipPolicy {
|
||||
return "Background execution disabled: receiving "
|
||||
+ r.intent + " to "
|
||||
+ component.flattenToShortString();
|
||||
} else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|
||||
|| (r.intent.getComponent() == null
|
||||
&& r.intent.getPackage() == null
|
||||
&& ((r.intent.getFlags()
|
||||
& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
|
||||
&& !isSignaturePerm(r.requiredPermissions))) {
|
||||
} else if (disallowBackgroundStart(r)) {
|
||||
mService.addBackgroundCheckViolationLocked(r.intent.getAction(),
|
||||
component.getPackageName());
|
||||
return "Background execution not allowed: receiving "
|
||||
@@ -340,6 +335,18 @@ public class BroadcastSkipPolicy {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given {@link BroadcastRecord} is eligible to launch processes.
|
||||
*/
|
||||
public boolean disallowBackgroundStart(@NonNull BroadcastRecord r) {
|
||||
return ((r.intent.getFlags() & Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0)
|
||||
|| (r.intent.getComponent() == null
|
||||
&& r.intent.getPackage() == null
|
||||
&& ((r.intent.getFlags()
|
||||
& Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0)
|
||||
&& !isSignaturePerm(r.requiredPermissions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the given {@link BroadcastRecord} is eligible to be sent to
|
||||
* the given {@link BroadcastFilter}.
|
||||
@@ -624,7 +631,7 @@ public class BroadcastSkipPolicy {
|
||||
/**
|
||||
* Return true if all given permissions are signature-only perms.
|
||||
*/
|
||||
private boolean isSignaturePerm(String[] perms) {
|
||||
private static boolean isSignaturePerm(String[] perms) {
|
||||
if (perms == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_INTERA
|
||||
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_MANIFEST;
|
||||
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ORDERED;
|
||||
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_PRIORITIZED;
|
||||
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_RESULT_TO;
|
||||
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
|
||||
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
|
||||
import static com.android.server.am.BroadcastQueueTest.CLASS_BLUE;
|
||||
@@ -115,8 +114,29 @@ public class BroadcastQueueModernImplTest {
|
||||
mConstants.DELAY_NORMAL_MILLIS = 10_000;
|
||||
mConstants.DELAY_CACHED_MILLIS = 120_000;
|
||||
|
||||
final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
|
||||
public boolean shouldSkip(BroadcastRecord r, Object o) {
|
||||
// Ignored
|
||||
return false;
|
||||
}
|
||||
public String shouldSkipMessage(BroadcastRecord r, Object o) {
|
||||
// Ignored
|
||||
return null;
|
||||
}
|
||||
public boolean disallowBackgroundStart(BroadcastRecord r) {
|
||||
// Ignored
|
||||
return false;
|
||||
}
|
||||
};
|
||||
final BroadcastHistory emptyHistory = new BroadcastHistory(mConstants) {
|
||||
public void addBroadcastToHistoryLocked(BroadcastRecord original) {
|
||||
// Ignored
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
|
||||
mConstants, mConstants);
|
||||
mConstants, mConstants, emptySkipPolicy, emptyHistory);
|
||||
|
||||
doReturn(1L).when(mQueue1).getRunnableAt();
|
||||
doReturn(2L).when(mQueue2).getRunnableAt();
|
||||
@@ -200,7 +220,8 @@ public class BroadcastQueueModernImplTest {
|
||||
|
||||
private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
|
||||
BroadcastRecord record, int recordIndex, long enqueueTime) {
|
||||
queue.enqueueOrReplaceBroadcast(record, recordIndex, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(record, recordIndex,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
record.enqueueTime = enqueueTime;
|
||||
}
|
||||
|
||||
@@ -330,7 +351,8 @@ public class BroadcastQueueModernImplTest {
|
||||
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
||||
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
|
||||
List.of(makeMockRegisteredReceiver()));
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
|
||||
queue.setProcessCached(false);
|
||||
final long notCachedRunnableAt = queue.getRunnableAt();
|
||||
@@ -352,12 +374,14 @@ public class BroadcastQueueModernImplTest {
|
||||
// enqueue a bg-priority broadcast then a fg-priority one
|
||||
final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
|
||||
final BroadcastRecord timezoneRecord = makeBroadcastRecord(timezone);
|
||||
queue.enqueueOrReplaceBroadcast(timezoneRecord, 0, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(timezoneRecord, 0,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
|
||||
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
||||
airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
|
||||
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
|
||||
// verify that:
|
||||
// (a) the queue is immediately runnable by existence of a fg-priority broadcast
|
||||
@@ -388,7 +412,8 @@ public class BroadcastQueueModernImplTest {
|
||||
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, null,
|
||||
List.of(withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
|
||||
withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 0)), true);
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 1, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 1,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
|
||||
assertFalse(queue.isRunnable());
|
||||
assertEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason());
|
||||
@@ -411,7 +436,8 @@ public class BroadcastQueueModernImplTest {
|
||||
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
|
||||
final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
|
||||
List.of(makeMockRegisteredReceiver()));
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
|
||||
mConstants.MAX_PENDING_BROADCASTS = 128;
|
||||
queue.invalidateRunnableAt();
|
||||
@@ -437,11 +463,13 @@ public class BroadcastQueueModernImplTest {
|
||||
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
|
||||
List.of(makeMockRegisteredReceiver()));
|
||||
|
||||
queue.enqueueOrReplaceBroadcast(lazyRecord, 0, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(lazyRecord, 0,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
assertThat(queue.getRunnableAt()).isGreaterThan(lazyRecord.enqueueTime);
|
||||
assertThat(queue.getRunnableAtReason()).isNotEqualTo(testRunnableAtReason);
|
||||
|
||||
queue.enqueueOrReplaceBroadcast(testRecord, 0, null /* replacedBroadcastConsumer */);
|
||||
queue.enqueueOrReplaceBroadcast(testRecord, 0,
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
assertThat(queue.getRunnableAt()).isAtMost(testRecord.enqueueTime);
|
||||
assertThat(queue.getRunnableAtReason()).isEqualTo(testRunnableAtReason);
|
||||
}
|
||||
@@ -458,13 +486,6 @@ public class BroadcastQueueModernImplTest {
|
||||
List.of(makeMockRegisteredReceiver()), null, true), REASON_CONTAINS_ORDERED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunnableAt_Cached_ResultTo() {
|
||||
final IIntentReceiver resultTo = mock(IIntentReceiver.class);
|
||||
doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
|
||||
List.of(makeMockRegisteredReceiver()), resultTo, false), REASON_CONTAINS_RESULT_TO);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunnableAt_Cached_Foreground() {
|
||||
final Intent foregroundIntent = new Intent();
|
||||
@@ -511,25 +532,25 @@ public class BroadcastQueueModernImplTest {
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0,
|
||||
null /* replacedBroadcastConsumer */);
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0,
|
||||
null /* replacedBroadcastConsumer */);
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0,
|
||||
null /* replacedBroadcastConsumer */);
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_ALARM_CHANGED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0,
|
||||
null /* replacedBroadcastConsumer */);
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0,
|
||||
null /* replacedBroadcastConsumer */);
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
queue.enqueueOrReplaceBroadcast(
|
||||
makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED)
|
||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0,
|
||||
null /* replacedBroadcastConsumer */);
|
||||
null /* replacedBroadcastConsumer */, false);
|
||||
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_LOCKED_BOOT_COMPLETED, queue.getActive().intent.getAction());
|
||||
|
||||
@@ -357,6 +357,11 @@ public class BroadcastQueueTest {
|
||||
}
|
||||
receiversToSkip.add(o);
|
||||
}
|
||||
public boolean disallowBackgroundStart(BroadcastRecord r) {
|
||||
// Ignored
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TestInjector extends Injector {
|
||||
|
||||
Reference in New Issue
Block a user