BroadcastQueue: remove matching broadcasts.
There are common broadcasts that should remove any still-pending broadcasts of a particular flavor. For example, sending SCREEN_ON should remove any still-pending SCREEN_OFF broadcasts. This change adds a BroadcastOption to support this use-case, along with tests to validate, and starts wiring up the option to a handful of straightforward broadcasts. For this to work effectively, we need to actually remove skipped events from our per-process queues, which means we need a more robust way of finishing ordered broadcasts. This change factors out the "finishing" of each broadcast to a local setDeliveryState() method which can then be invoked when a broadcast is removed. Bug: 245771249 Test: atest FrameworksMockingServicesTests:BroadcastQueueTest Change-Id: I8c81893841026074ef78d88f9ecc82518164e50e
This commit is contained in:
@@ -57,6 +57,8 @@ import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_R
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManagerInternal;
|
||||
@@ -289,6 +291,7 @@ public class AlarmManagerService extends SystemService {
|
||||
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
|
||||
IBinder.DeathRecipient mListenerDeathRecipient;
|
||||
Intent mTimeTickIntent;
|
||||
Bundle mTimeTickOptions;
|
||||
IAlarmListener mTimeTickTrigger;
|
||||
PendingIntent mDateChangeSender;
|
||||
boolean mInteractive = true;
|
||||
@@ -1909,7 +1912,9 @@ public class AlarmManagerService extends SystemService {
|
||||
Intent.FLAG_RECEIVER_REGISTERED_ONLY
|
||||
| Intent.FLAG_RECEIVER_FOREGROUND
|
||||
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
|
||||
|
||||
mTimeTickOptions = BroadcastOptions
|
||||
.makeRemovingMatchingFilter(new IntentFilter(Intent.ACTION_TIME_TICK))
|
||||
.toBundle();
|
||||
mTimeTickTrigger = new IAlarmListener.Stub() {
|
||||
@Override
|
||||
public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException {
|
||||
@@ -1921,8 +1926,8 @@ public class AlarmManagerService extends SystemService {
|
||||
// takes care of this automatically, but we're using the direct internal
|
||||
// interface here rather than that client-side wrapper infrastructure.
|
||||
mHandler.post(() -> {
|
||||
getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL);
|
||||
|
||||
getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL, null,
|
||||
mTimeTickOptions);
|
||||
try {
|
||||
callback.alarmComplete(this);
|
||||
} catch (RemoteException e) { /* local method call */ }
|
||||
|
||||
@@ -4667,7 +4667,7 @@ public class ActivityManager {
|
||||
* @hide
|
||||
*/
|
||||
public static void broadcastStickyIntent(Intent intent, int userId) {
|
||||
broadcastStickyIntent(intent, AppOpsManager.OP_NONE, userId);
|
||||
broadcastStickyIntent(intent, AppOpsManager.OP_NONE, null, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4676,11 +4676,20 @@ public class ActivityManager {
|
||||
* @hide
|
||||
*/
|
||||
public static void broadcastStickyIntent(Intent intent, int appOp, int userId) {
|
||||
broadcastStickyIntent(intent, appOp, null, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience for sending a sticky broadcast. For internal use only.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static void broadcastStickyIntent(Intent intent, int appOp, Bundle options, int userId) {
|
||||
try {
|
||||
getService().broadcastIntentWithFeature(
|
||||
null, null, intent, null, null, Activity.RESULT_OK, null, null,
|
||||
null /*requiredPermissions*/, null /*excludedPermissions*/,
|
||||
null /*excludedPackages*/, appOp, null, false, true, userId);
|
||||
null /*excludedPackages*/, appOp, options, false, true, userId);
|
||||
} catch (RemoteException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,15 @@ import android.compat.annotation.ChangeId;
|
||||
import android.compat.annotation.Disabled;
|
||||
import android.compat.annotation.EnabledSince;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.os.PowerExemptionManager.ReasonCode;
|
||||
import android.os.PowerExemptionManager.TempAllowListType;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Helper class for building an options Bundle that can be used with
|
||||
* {@link android.content.Context#sendBroadcast(android.content.Intent)
|
||||
@@ -55,6 +58,7 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
private boolean mRequireCompatChangeEnabled = true;
|
||||
private boolean mIsAlarmBroadcast = false;
|
||||
private long mIdForResponseEvent;
|
||||
private @Nullable IntentFilter mRemoveMatchingFilter;
|
||||
|
||||
/**
|
||||
* Change ID which is invalid.
|
||||
@@ -180,11 +184,25 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
private static final String KEY_ID_FOR_RESPONSE_EVENT =
|
||||
"android:broadcast.idForResponseEvent";
|
||||
|
||||
/**
|
||||
* Corresponds to {@link #setRemoveMatchingFilter}.
|
||||
*/
|
||||
private static final String KEY_REMOVE_MATCHING_FILTER =
|
||||
"android:broadcast.removeMatchingFilter";
|
||||
|
||||
public static BroadcastOptions makeBasic() {
|
||||
BroadcastOptions opts = new BroadcastOptions();
|
||||
return opts;
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
public static @NonNull BroadcastOptions makeRemovingMatchingFilter(
|
||||
@NonNull IntentFilter removeMatchingFilter) {
|
||||
BroadcastOptions opts = new BroadcastOptions();
|
||||
opts.setRemoveMatchingFilter(removeMatchingFilter);
|
||||
return opts;
|
||||
}
|
||||
|
||||
private BroadcastOptions() {
|
||||
super();
|
||||
resetTemporaryAppAllowlist();
|
||||
@@ -216,6 +234,8 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true);
|
||||
mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
|
||||
mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false);
|
||||
mRemoveMatchingFilter = opts.getParcelable(KEY_REMOVE_MATCHING_FILTER,
|
||||
IntentFilter.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -595,6 +615,29 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
return mIdForResponseEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* When enqueuing this broadcast, remove all pending broadcasts previously
|
||||
* sent by this app which match the given filter.
|
||||
* <p>
|
||||
* For example, sending {@link Intent#ACTION_SCREEN_ON} would typically want
|
||||
* to remove any pending {@link Intent#ACTION_SCREEN_OFF} broadcasts.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void setRemoveMatchingFilter(@NonNull IntentFilter removeMatchingFilter) {
|
||||
mRemoveMatchingFilter = Objects.requireNonNull(removeMatchingFilter);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void clearRemoveMatchingFilter() {
|
||||
mRemoveMatchingFilter = null;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public @Nullable IntentFilter getRemoveMatchingFilter() {
|
||||
return mRemoveMatchingFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the created options as a Bundle, which can be passed to
|
||||
* {@link android.content.Context#sendBroadcast(android.content.Intent)
|
||||
@@ -640,6 +683,9 @@ public class BroadcastOptions extends ComponentOptions {
|
||||
if (mIdForResponseEvent != 0) {
|
||||
b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent);
|
||||
}
|
||||
if (mRemoveMatchingFilter != null) {
|
||||
b.putParcelable(KEY_REMOVE_MATCHING_FILTER, mRemoveMatchingFilter);
|
||||
}
|
||||
return b.isEmpty() ? null : b;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,12 @@ import static com.android.server.health.Utils.copyV1Battery;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.database.ContentObserver;
|
||||
import android.hardware.health.HealthInfo;
|
||||
import android.hardware.health.V2_1.BatteryCapacityLevel;
|
||||
@@ -185,6 +188,17 @@ public final class BatteryService extends SystemService {
|
||||
private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
|
||||
private long mLastBatteryLevelChangedSentMs;
|
||||
|
||||
private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).toBundle();
|
||||
private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).toBundle();
|
||||
private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_POWER_CONNECTED)).toBundle();
|
||||
private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_BATTERY_OKAY)).toBundle();
|
||||
private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_BATTERY_LOW)).toBundle();
|
||||
|
||||
private MetricsLogger mMetricsLogger;
|
||||
|
||||
public BatteryService(Context context) {
|
||||
@@ -606,7 +620,8 @@ public final class BatteryService extends SystemService {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
|
||||
mPowerConnectedOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -617,7 +632,8 @@ public final class BatteryService extends SystemService {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
|
||||
mPowerDisconnectedOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -630,7 +646,8 @@ public final class BatteryService extends SystemService {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
|
||||
mBatteryLowOptions);
|
||||
}
|
||||
});
|
||||
} else if (mSentLowBatteryBroadcast &&
|
||||
@@ -642,7 +659,8 @@ public final class BatteryService extends SystemService {
|
||||
mHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
|
||||
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
|
||||
mBatteryOkayOptions);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -712,7 +730,8 @@ public final class BatteryService extends SystemService {
|
||||
+ ", info:" + mHealthInfo.toString());
|
||||
}
|
||||
|
||||
mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL));
|
||||
mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, AppOpsManager.OP_NONE,
|
||||
mBatteryChangedOptions, UserHandle.USER_ALL));
|
||||
}
|
||||
|
||||
private void sendBatteryLevelChangedIntentLocked() {
|
||||
|
||||
@@ -17354,6 +17354,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(),
|
||||
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
|
||||
PowerExemptionManager.REASON_LOCALE_CHANGED, "");
|
||||
bOptions.setRemoveMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
|
||||
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
|
||||
null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID,
|
||||
SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(),
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.server.am;
|
||||
|
||||
import static com.android.server.am.BroadcastQueue.checkState;
|
||||
|
||||
import android.annotation.DurationMillisLong;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.UptimeMillisLong;
|
||||
@@ -28,12 +27,10 @@ import android.util.IndentingPrintWriter;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.server.am.BroadcastRecord.DeliveryState;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
/**
|
||||
* Queue of pending {@link BroadcastRecord} entries intended for delivery to a
|
||||
@@ -46,7 +43,11 @@ import java.util.function.BiPredicate;
|
||||
* Internally each queue consists of a pending broadcasts which are waiting to
|
||||
* be dispatched, and a single active broadcast which is currently being
|
||||
* dispatched.
|
||||
* <p>
|
||||
* This entire class is marked as {@code NotThreadSafe} since it's the
|
||||
* responsibility of the caller to always interact with a relevant lock held.
|
||||
*/
|
||||
// @NotThreadSafe
|
||||
class BroadcastProcessQueue {
|
||||
final @NonNull BroadcastConstants constants;
|
||||
final @NonNull String processName;
|
||||
@@ -155,24 +156,42 @@ class BroadcastProcessQueue {
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip any broadcasts matching the given predicate, marking them as
|
||||
* {@link BroadcastRecord#DELIVERY_SKIPPED}. Typically used when a package
|
||||
* or components have been disabled.
|
||||
* <p>
|
||||
* Note that we carefully preserve the broadcast in our queue to ensure that
|
||||
* we follow our normal flow for "finishing" a broadcast, which is where we
|
||||
* handle things like ordered broadcasts.
|
||||
* Functional interface that tests a {@link BroadcastRecord} that has been
|
||||
* previously enqueued in {@link BroadcastProcessQueue}.
|
||||
*/
|
||||
public boolean skipMatchingBroadcasts(@NonNull BiPredicate<BroadcastRecord, Object> predicate) {
|
||||
@FunctionalInterface
|
||||
public interface BroadcastPredicate {
|
||||
public boolean test(@NonNull BroadcastRecord r, int index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Functional interface that consumes a {@link BroadcastRecord} that has
|
||||
* been previously enqueued in {@link BroadcastProcessQueue}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BroadcastConsumer {
|
||||
public void accept(@NonNull BroadcastRecord r, int index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any broadcasts matching the given predicate.
|
||||
* <p>
|
||||
* Predicates that choose to remove a broadcast <em>must</em> finish
|
||||
* delivery of the matched broadcast, to ensure that situations like ordered
|
||||
* broadcasts are handled consistently.
|
||||
*/
|
||||
public boolean removeMatchingBroadcasts(@NonNull BroadcastPredicate predicate,
|
||||
@NonNull BroadcastConsumer consumer) {
|
||||
boolean didSomething = false;
|
||||
final Iterator<SomeArgs> it = mPending.iterator();
|
||||
while (it.hasNext()) {
|
||||
final SomeArgs args = it.next();
|
||||
final BroadcastRecord record = (BroadcastRecord) args.arg1;
|
||||
final int index = args.argi1;
|
||||
final Object receiver = record.receivers.get(index);
|
||||
if (predicate.test(record, receiver)) {
|
||||
record.setDeliveryState(index, BroadcastRecord.DELIVERY_SKIPPED);
|
||||
if (predicate.test(record, index)) {
|
||||
consumer.accept(record, index);
|
||||
args.recycle();
|
||||
it.remove();
|
||||
didSomething = true;
|
||||
}
|
||||
}
|
||||
@@ -263,78 +282,48 @@ class BroadcastProcessQueue {
|
||||
mActiveViaColdStart = false;
|
||||
}
|
||||
|
||||
public void traceStartingBegin() {
|
||||
public void traceProcessStartingBegin() {
|
||||
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
traceTrackName, toShortString() + " starting", hashCode());
|
||||
}
|
||||
|
||||
public void traceRunningBegin() {
|
||||
public void traceProcessRunningBegin() {
|
||||
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
traceTrackName, toShortString() + " running", hashCode());
|
||||
}
|
||||
|
||||
public void traceEnd() {
|
||||
public void traceProcessEnd() {
|
||||
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
traceTrackName, hashCode());
|
||||
}
|
||||
|
||||
public @DeliveryState int getActiveDeliveryState() {
|
||||
checkState(isActive(), "isActive");
|
||||
return mActive.delivery[mActiveIndex];
|
||||
}
|
||||
|
||||
public void setActiveDeliveryState(@DeliveryState int deliveryState) {
|
||||
checkState(isActive(), "isActive");
|
||||
|
||||
// Emit tracing events for the broadcast we're dispatching; the cookie
|
||||
// here is unique within the track
|
||||
public void traceActiveBegin() {
|
||||
final int cookie = mActive.receivers.get(mActiveIndex).hashCode();
|
||||
switch (deliveryState) {
|
||||
case BroadcastRecord.DELIVERY_SCHEDULED:
|
||||
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
traceTrackName, mActive.toShortString() + " scheduled", cookie);
|
||||
break;
|
||||
case BroadcastRecord.DELIVERY_DELIVERED:
|
||||
case BroadcastRecord.DELIVERY_SKIPPED:
|
||||
case BroadcastRecord.DELIVERY_TIMEOUT:
|
||||
case BroadcastRecord.DELIVERY_FAILURE:
|
||||
// Only end trace events previously started by us
|
||||
if (getActiveDeliveryState() == BroadcastRecord.DELIVERY_SCHEDULED) {
|
||||
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
traceTrackName, cookie);
|
||||
}
|
||||
break;
|
||||
}
|
||||
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
traceTrackName, mActive.toShortString() + " scheduled", cookie);
|
||||
}
|
||||
|
||||
mActive.setDeliveryState(mActiveIndex, deliveryState);
|
||||
public void traceActiveEnd() {
|
||||
final int cookie = mActive.receivers.get(mActiveIndex).hashCode();
|
||||
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
|
||||
traceTrackName, cookie);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the delay between the currently active broadcast being enqueued
|
||||
* and being scheduled to run for this process.
|
||||
* Return the broadcast being actively dispatched in this process.
|
||||
*/
|
||||
public @DurationMillisLong long getActiveScheduledDelay() {
|
||||
checkState(isActive(), "isActive");
|
||||
return mActive.scheduledTime[mActiveIndex] - mActive.enqueueTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the delay between the currently active broadcast being scheduled
|
||||
* to run and reaching a terminal state for this process.
|
||||
*/
|
||||
public @DurationMillisLong long getActiveDeliveredDelay() {
|
||||
checkState(isActive(), "isActive");
|
||||
return mActive.duration[mActiveIndex];
|
||||
}
|
||||
|
||||
public @NonNull BroadcastRecord getActive() {
|
||||
checkState(isActive(), "isActive");
|
||||
return mActive;
|
||||
}
|
||||
|
||||
public @NonNull Object getActiveReceiver() {
|
||||
/**
|
||||
* Return the index into {@link BroadcastRecord#receivers} of the receiver
|
||||
* being actively dispatched in this process.
|
||||
*/
|
||||
public int getActiveIndex() {
|
||||
checkState(isActive(), "isActive");
|
||||
return mActive.receivers.get(mActiveIndex);
|
||||
return mActiveIndex;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
|
||||
@@ -21,14 +21,18 @@ import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
|
||||
|
||||
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
|
||||
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
|
||||
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_UNKNOWN;
|
||||
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
|
||||
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
|
||||
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME;
|
||||
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
|
||||
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
|
||||
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
|
||||
import static com.android.server.am.BroadcastRecord.deliveryStateToString;
|
||||
import static com.android.server.am.BroadcastRecord.getReceiverPackageName;
|
||||
import static com.android.server.am.BroadcastRecord.getReceiverProcessName;
|
||||
import static com.android.server.am.BroadcastRecord.getReceiverUid;
|
||||
import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal;
|
||||
import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER;
|
||||
import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_START_RECEIVER;
|
||||
|
||||
@@ -42,6 +46,8 @@ import android.app.usage.UsageEvents.Event;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
@@ -64,6 +70,8 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.internal.os.TimeoutRecord;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.server.am.BroadcastProcessQueue.BroadcastConsumer;
|
||||
import com.android.server.am.BroadcastProcessQueue.BroadcastPredicate;
|
||||
import com.android.server.am.BroadcastRecord.DeliveryState;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
@@ -73,7 +81,6 @@ import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -345,15 +352,15 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
+ " from runnable to running; process is " + queue.app);
|
||||
|
||||
// Allocate this available permit and start running!
|
||||
final int index = getRunningIndexOf(null);
|
||||
mRunning[index] = queue;
|
||||
final int queueIndex = getRunningIndexOf(null);
|
||||
mRunning[queueIndex] = queue;
|
||||
avail--;
|
||||
|
||||
// Remove ourselves from linked list of runnable things
|
||||
mRunnableHead = removeFromRunnableList(mRunnableHead, queue);
|
||||
|
||||
// Emit all trace events for this process into a consistent track
|
||||
queue.traceTrackName = TAG + ".mRunning[" + index + "]";
|
||||
queue.traceTrackName = TAG + ".mRunning[" + queueIndex + "]";
|
||||
|
||||
// If we're already warm, boost OOM adjust now; if cold we'll boost
|
||||
// it after the app has been started
|
||||
@@ -365,10 +372,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
// otherwise we'll wait for the cold start to circle back around
|
||||
queue.makeActiveNextPending();
|
||||
if (processWarm) {
|
||||
queue.traceRunningBegin();
|
||||
queue.traceProcessRunningBegin();
|
||||
scheduleReceiverWarmLocked(queue);
|
||||
} else {
|
||||
queue.traceStartingBegin();
|
||||
queue.traceProcessStartingBegin();
|
||||
scheduleReceiverColdLocked(queue);
|
||||
}
|
||||
|
||||
@@ -401,8 +408,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
final BroadcastProcessQueue queue = mRunningColdStart;
|
||||
mRunningColdStart = null;
|
||||
|
||||
queue.traceEnd();
|
||||
queue.traceRunningBegin();
|
||||
queue.traceProcessEnd();
|
||||
queue.traceProcessRunningBegin();
|
||||
scheduleReceiverWarmLocked(queue);
|
||||
|
||||
// We might be willing to kick off another cold start
|
||||
@@ -468,6 +475,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
// TODO: handle empty receivers to deliver result immediately
|
||||
if (r.receivers == null) return;
|
||||
|
||||
final IntentFilter removeMatchingFilter = (r.options != null)
|
||||
? r.options.getRemoveMatchingFilter() : null;
|
||||
if (removeMatchingFilter != null) {
|
||||
final Predicate<Intent> removeMatching = removeMatchingFilter.asPredicate();
|
||||
skipMatchingBroadcasts(QUEUE_PREDICATE_ANY, (testRecord, testReceiver) -> {
|
||||
// We only allow caller to clear broadcasts they enqueued
|
||||
return (testRecord.callingUid == r.callingUid)
|
||||
&& removeMatching.test(testRecord.intent);
|
||||
});
|
||||
}
|
||||
|
||||
r.enqueueTime = SystemClock.uptimeMillis();
|
||||
r.enqueueRealTime = SystemClock.elapsedRealtime();
|
||||
r.enqueueClockTime = System.currentTimeMillis();
|
||||
@@ -494,7 +512,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
queue.setActiveViaColdStart(true);
|
||||
|
||||
final BroadcastRecord r = queue.getActive();
|
||||
final Object receiver = queue.getActiveReceiver();
|
||||
final int index = queue.getActiveIndex();
|
||||
final Object receiver = r.receivers.get(index);
|
||||
|
||||
final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
|
||||
final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();
|
||||
@@ -534,15 +553,17 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
|
||||
final ProcessRecord app = queue.app;
|
||||
final BroadcastRecord r = queue.getActive();
|
||||
final Object receiver = queue.getActiveReceiver();
|
||||
final int index = queue.getActiveIndex();
|
||||
final Object receiver = r.receivers.get(index);
|
||||
|
||||
// If someone already skipped us, finish immediately; typically due to a
|
||||
// component being disabled
|
||||
if (queue.getActiveDeliveryState() == BroadcastRecord.DELIVERY_SKIPPED) {
|
||||
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
|
||||
// If someone already finished this broadcast, finish immediately
|
||||
final int oldDeliveryState = getDeliveryState(r, index);
|
||||
if (isDeliveryStateTerminal(oldDeliveryState)) {
|
||||
finishReceiverLocked(queue, oldDeliveryState);
|
||||
return;
|
||||
}
|
||||
|
||||
// Consider additional cases where we'd want fo finish immediately
|
||||
if (app.isInFullBackup()) {
|
||||
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
|
||||
return;
|
||||
@@ -583,13 +604,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
}
|
||||
|
||||
if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
|
||||
queue.setActiveDeliveryState(BroadcastRecord.DELIVERY_SCHEDULED);
|
||||
setDeliveryState(queue, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);
|
||||
|
||||
final IApplicationThread thread = app.getThread();
|
||||
if (thread != null) {
|
||||
try {
|
||||
if (receiver instanceof BroadcastFilter) {
|
||||
notifyScheduleRegisteredReceiver(app, r);
|
||||
notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
|
||||
thread.scheduleRegisteredReceiver(
|
||||
((BroadcastFilter) receiver).receiverList.receiver, receiverIntent,
|
||||
r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky,
|
||||
@@ -601,7 +622,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
finishReceiverLocked(queue, BroadcastRecord.DELIVERY_DELIVERED);
|
||||
}
|
||||
} else {
|
||||
notifyScheduleReceiver(app, r, receiverIntent);
|
||||
notifyScheduleReceiver(app, r, (ResolveInfo) receiver);
|
||||
thread.scheduleReceiver(receiverIntent, ((ResolveInfo) receiver).activityInfo,
|
||||
null, r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
|
||||
app.mState.getReportedProcState());
|
||||
@@ -653,7 +674,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
// receivers as skipped
|
||||
if (r.ordered && r.resultAbort) {
|
||||
for (int i = r.finishedCount + 1; i < r.receivers.size(); i++) {
|
||||
r.setDeliveryState(i, BroadcastRecord.DELIVERY_SKIPPED);
|
||||
setDeliveryState(null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -666,44 +687,20 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
|
||||
final ProcessRecord app = queue.app;
|
||||
final BroadcastRecord r = queue.getActive();
|
||||
final int index = queue.getActiveIndex();
|
||||
final Object receiver = r.receivers.get(index);
|
||||
|
||||
queue.setActiveDeliveryState(deliveryState);
|
||||
|
||||
if (deliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
|
||||
Slog.w(TAG, "Delivery state of " + queue.getActive() + " to " + queue + " changed to "
|
||||
+ BroadcastRecord.deliveryStateToString(deliveryState));
|
||||
}
|
||||
setDeliveryState(queue, r, index, receiver, deliveryState);
|
||||
|
||||
if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
|
||||
if (app != null && !app.isDebugging()) {
|
||||
mService.appNotResponding(queue.app, TimeoutRecord
|
||||
.forBroadcastReceiver("Broadcast of " + queue.getActive().toShortString()));
|
||||
.forBroadcastReceiver("Broadcast of " + r.toShortString()));
|
||||
}
|
||||
} else {
|
||||
mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT, queue);
|
||||
}
|
||||
|
||||
// We carefully only increment this here once we've confidently finished
|
||||
// a given receiver; everyone must come through this path to ensure all
|
||||
// bookkeeping is handled consistently
|
||||
r.finishedCount++;
|
||||
notifyFinishReceiver(queue);
|
||||
|
||||
if (r.ordered) {
|
||||
if (r.finishedCount < r.receivers.size()) {
|
||||
// We just finished an ordered receiver, which means the next
|
||||
// receiver might now be runnable
|
||||
final Object nextReceiver = r.receivers.get(r.finishedCount);
|
||||
final BroadcastProcessQueue nextQueue = getProcessQueue(
|
||||
getReceiverProcessName(nextReceiver), getReceiverUid(nextReceiver));
|
||||
nextQueue.invalidateRunnableAt();
|
||||
updateRunnableList(nextQueue);
|
||||
} else {
|
||||
// Everything finished, so deliver final result
|
||||
scheduleResultTo(r);
|
||||
}
|
||||
}
|
||||
|
||||
// Even if we have more broadcasts, if we've made reasonable progress
|
||||
// and someone else is waiting, retire ourselves to avoid starvation
|
||||
final boolean shouldRetire = (mRunnableHead != null)
|
||||
@@ -717,10 +714,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
} else {
|
||||
// We've drained running broadcasts; maybe move back to runnable
|
||||
queue.makeActiveIdle();
|
||||
queue.traceEnd();
|
||||
queue.traceProcessEnd();
|
||||
|
||||
final int index = getRunningIndexOf(queue);
|
||||
mRunning[index] = null;
|
||||
final int queueIndex = getRunningIndexOf(queue);
|
||||
mRunning[queueIndex] = null;
|
||||
updateRunnableList(queue);
|
||||
enqueueUpdateRunningList();
|
||||
|
||||
@@ -731,17 +728,67 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Predicate<BroadcastProcessQueue> QUEUE_PREDICATE_ANY =
|
||||
(q) -> true;
|
||||
private static final BiPredicate<BroadcastRecord, Object> BROADCAST_PREDICATE_ANY =
|
||||
(r, o) -> true;
|
||||
/**
|
||||
* Set the delivery state on the given broadcast, then apply any additional
|
||||
* bookkeeping related to ordered broadcasts.
|
||||
*/
|
||||
private void setDeliveryState(@Nullable BroadcastProcessQueue queue,
|
||||
@NonNull BroadcastRecord r, int index, @NonNull Object receiver,
|
||||
@DeliveryState int newDeliveryState) {
|
||||
final int oldDeliveryState = getDeliveryState(r, index);
|
||||
|
||||
if (newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
|
||||
Slog.w(TAG, "Delivery state of " + r + " to " + receiver + " changed from "
|
||||
+ deliveryStateToString(oldDeliveryState) + " to "
|
||||
+ deliveryStateToString(newDeliveryState));
|
||||
}
|
||||
|
||||
r.setDeliveryState(index, newDeliveryState);
|
||||
|
||||
// Emit any relevant tracing results when we're changing the delivery
|
||||
// state as part of running from a queue
|
||||
if (queue != null) {
|
||||
if (newDeliveryState == BroadcastRecord.DELIVERY_SCHEDULED) {
|
||||
queue.traceActiveBegin();
|
||||
} else if ((oldDeliveryState == BroadcastRecord.DELIVERY_SCHEDULED)
|
||||
&& isDeliveryStateTerminal(newDeliveryState)) {
|
||||
queue.traceActiveEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// If we're moving into a terminal state, we might have internal
|
||||
// bookkeeping to update for ordered broadcasts
|
||||
if (!isDeliveryStateTerminal(oldDeliveryState)
|
||||
&& isDeliveryStateTerminal(newDeliveryState)) {
|
||||
r.finishedCount++;
|
||||
notifyFinishReceiver(queue, r, index, receiver);
|
||||
|
||||
if (r.ordered) {
|
||||
if (r.finishedCount < r.receivers.size()) {
|
||||
// We just finished an ordered receiver, which means the
|
||||
// next receiver might now be runnable
|
||||
final Object nextReceiver = r.receivers.get(r.finishedCount);
|
||||
final BroadcastProcessQueue nextQueue = getProcessQueue(
|
||||
getReceiverProcessName(nextReceiver), getReceiverUid(nextReceiver));
|
||||
nextQueue.invalidateRunnableAt();
|
||||
updateRunnableList(nextQueue);
|
||||
} else {
|
||||
// Everything finished, so deliver final result
|
||||
scheduleResultTo(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @DeliveryState int getDeliveryState(@NonNull BroadcastRecord r, int index) {
|
||||
return r.getDeliveryState(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cleanupDisabledPackageReceiversLocked(@Nullable String packageName,
|
||||
@Nullable Set<String> filterByClasses, int userId) {
|
||||
final Predicate<BroadcastProcessQueue> queuePredicate;
|
||||
final BiPredicate<BroadcastRecord, Object> broadcastPredicate;
|
||||
// TODO: apply packageName filter inside broadcastPredicate
|
||||
final BroadcastPredicate broadcastPredicate;
|
||||
if (packageName != null) {
|
||||
// Caller provided a package and user ID, so we're focused on queues
|
||||
// belonging to a specific UID
|
||||
@@ -754,15 +801,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
// If caller provided a set of classes, filter to skip only those;
|
||||
// otherwise we skip all broadcasts
|
||||
if (filterByClasses != null) {
|
||||
broadcastPredicate = (r, o) -> {
|
||||
if (o instanceof ResolveInfo) {
|
||||
return filterByClasses.contains(((ResolveInfo) o).activityInfo.name);
|
||||
broadcastPredicate = (r, i) -> {
|
||||
final Object receiver = r.receivers.get(i);
|
||||
if (receiver instanceof ResolveInfo) {
|
||||
final ActivityInfo info = ((ResolveInfo) receiver).activityInfo;
|
||||
return packageName.equals(info.packageName)
|
||||
&& filterByClasses.contains(info.name);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
broadcastPredicate = BROADCAST_PREDICATE_ANY;
|
||||
broadcastPredicate = (r, i) -> {
|
||||
final Object receiver = r.receivers.get(i);
|
||||
return packageName.equals(getReceiverPackageName(receiver));
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Caller is cleaning up an entire user ID; skip all broadcasts
|
||||
@@ -771,7 +824,25 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
};
|
||||
broadcastPredicate = BROADCAST_PREDICATE_ANY;
|
||||
}
|
||||
return skipMatchingBroadcasts(queuePredicate, broadcastPredicate);
|
||||
}
|
||||
|
||||
private static final Predicate<BroadcastProcessQueue> QUEUE_PREDICATE_ANY =
|
||||
(q) -> true;
|
||||
private static final BroadcastPredicate BROADCAST_PREDICATE_ANY =
|
||||
(r, i) -> true;
|
||||
|
||||
/**
|
||||
* Typical consumer that will skip the given broadcast, usually as a result
|
||||
* of it matching a predicate.
|
||||
*/
|
||||
private final BroadcastConsumer mBroadcastConsumerSkip = (r, i) -> {
|
||||
setDeliveryState(null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
|
||||
};
|
||||
|
||||
private boolean skipMatchingBroadcasts(
|
||||
@NonNull Predicate<BroadcastProcessQueue> queuePredicate,
|
||||
@NonNull BroadcastPredicate broadcastPredicate) {
|
||||
// Note that we carefully preserve any "skipped" broadcasts in their
|
||||
// queues so that we follow our normal flow for "finishing" a broadcast,
|
||||
// which is where we handle things like ordered broadcasts.
|
||||
@@ -780,7 +851,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
|
||||
while (leaf != null) {
|
||||
if (queuePredicate.test(leaf)) {
|
||||
didSomething |= leaf.skipMatchingBroadcasts(broadcastPredicate);
|
||||
didSomething |= leaf.removeMatchingBroadcasts(broadcastPredicate,
|
||||
mBroadcastConsumerSkip);
|
||||
}
|
||||
leaf = leaf.processNameNext;
|
||||
}
|
||||
@@ -909,7 +981,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
* a registered receiver, typically for internal bookkeeping.
|
||||
*/
|
||||
private void notifyScheduleRegisteredReceiver(@NonNull ProcessRecord app,
|
||||
@NonNull BroadcastRecord r) {
|
||||
@NonNull BroadcastRecord r, @NonNull BroadcastFilter receiver) {
|
||||
reportUsageStatsBroadcastDispatched(app, r);
|
||||
}
|
||||
|
||||
@@ -918,22 +990,25 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
* a manifest receiver, typically for internal bookkeeping.
|
||||
*/
|
||||
private void notifyScheduleReceiver(@NonNull ProcessRecord app,
|
||||
@NonNull BroadcastRecord r, @NonNull Intent receiverIntent) {
|
||||
@NonNull BroadcastRecord r, @NonNull ResolveInfo receiver) {
|
||||
reportUsageStatsBroadcastDispatched(app, r);
|
||||
|
||||
final String receiverPackageName = receiver.activityInfo.packageName;
|
||||
app.addPackage(receiverPackageName,
|
||||
receiver.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
|
||||
|
||||
final boolean targetedBroadcast = r.intent.getComponent() != null;
|
||||
final boolean targetedSelf = Objects.equals(r.callerPackage,
|
||||
receiverIntent.getComponent().getPackageName());
|
||||
final boolean targetedSelf = Objects.equals(r.callerPackage, receiverPackageName);
|
||||
if (targetedBroadcast && !targetedSelf) {
|
||||
mService.mUsageStatsService.reportEvent(receiverIntent.getComponent().getPackageName(),
|
||||
mService.mUsageStatsService.reportEvent(receiverPackageName,
|
||||
r.userId, Event.APP_COMPONENT_USED);
|
||||
}
|
||||
|
||||
mService.notifyPackageUse(receiverIntent.getComponent().getPackageName(),
|
||||
mService.notifyPackageUse(receiverPackageName,
|
||||
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
|
||||
|
||||
mService.mPackageManagerInt.setPackageStoppedState(
|
||||
receiverIntent.getComponent().getPackageName(), false, r.userId);
|
||||
receiverPackageName, false, r.userId);
|
||||
}
|
||||
|
||||
private void reportUsageStatsBroadcastDispatched(@NonNull ProcessRecord app,
|
||||
@@ -961,26 +1036,30 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
|
||||
* Inform other parts of OS that the given broadcast was just finished,
|
||||
* typically for internal bookkeeping.
|
||||
*/
|
||||
private void notifyFinishReceiver(@NonNull BroadcastProcessQueue queue) {
|
||||
final BroadcastRecord r = queue.getActive();
|
||||
|
||||
private void notifyFinishReceiver(@Nullable BroadcastProcessQueue queue,
|
||||
@NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
|
||||
// Report statistics for each individual receiver
|
||||
final int uid = queue.uid;
|
||||
final int uid = getReceiverUid(receiver);
|
||||
final int senderUid = (r.callingUid == -1) ? Process.SYSTEM_UID : r.callingUid;
|
||||
final String actionName = ActivityManagerService.getShortAction(r.intent.getAction());
|
||||
final int receiverType = (queue.getActiveReceiver() instanceof BroadcastFilter)
|
||||
final int receiverType = (receiver instanceof BroadcastFilter)
|
||||
? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
|
||||
: BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
|
||||
final int procStartType = queue.getActiveViaColdStart()
|
||||
? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD
|
||||
: BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
|
||||
final int type;
|
||||
if (queue == null) {
|
||||
type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_UNKNOWN;
|
||||
} else if (queue.getActiveViaColdStart()) {
|
||||
type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
|
||||
} else {
|
||||
type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
|
||||
}
|
||||
// With the new per-process queues, there's no delay between being
|
||||
// "dispatched" and "scheduled", so we report no "receive delay"
|
||||
final long dispatchDelay = queue.getActiveScheduledDelay();
|
||||
final long dispatchDelay = r.scheduledTime[index] - r.enqueueTime;
|
||||
final long receiveDelay = 0;
|
||||
final long finishDelay = queue.getActiveDeliveredDelay();
|
||||
final long finishDelay = r.duration[index];
|
||||
FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, uid, senderUid, actionName,
|
||||
receiverType, procStartType, dispatchDelay, receiveDelay, finishDelay);
|
||||
receiverType, type, dispatchDelay, receiveDelay, finishDelay);
|
||||
|
||||
final boolean recordFinished = (r.finishedCount == r.receivers.size());
|
||||
if (recordFinished) {
|
||||
|
||||
@@ -23,7 +23,9 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID;
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
|
||||
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
|
||||
import static com.android.server.am.BroadcastQueue.checkState;
|
||||
|
||||
import android.annotation.DurationMillisLong;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -168,6 +170,22 @@ final class BroadcastRecord extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given delivery state is "terminal", where no additional
|
||||
* delivery state changes will be made.
|
||||
*/
|
||||
static boolean isDeliveryStateTerminal(@DeliveryState int deliveryState) {
|
||||
switch (deliveryState) {
|
||||
case DELIVERY_DELIVERED:
|
||||
case DELIVERY_SKIPPED:
|
||||
case DELIVERY_TIMEOUT:
|
||||
case DELIVERY_FAILURE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ProcessRecord curApp; // hosting application of current receiver.
|
||||
ComponentName curComponent; // the receiver class that is currently running.
|
||||
ActivityInfo curReceiver; // the manifest receiver that is currently running.
|
||||
@@ -546,6 +564,10 @@ final class BroadcastRecord extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
@DeliveryState int getDeliveryState(int index) {
|
||||
return delivery[index];
|
||||
}
|
||||
|
||||
boolean isForeground() {
|
||||
return (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
|
||||
}
|
||||
@@ -611,7 +633,7 @@ final class BroadcastRecord extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
static String getReceiverProcessName(@NonNull Object receiver) {
|
||||
static @NonNull String getReceiverProcessName(@NonNull Object receiver) {
|
||||
if (receiver instanceof BroadcastFilter) {
|
||||
return ((BroadcastFilter) receiver).receiverList.app.processName;
|
||||
} else /* if (receiver instanceof ResolveInfo) */ {
|
||||
@@ -619,6 +641,14 @@ final class BroadcastRecord extends Binder {
|
||||
}
|
||||
}
|
||||
|
||||
static @NonNull String getReceiverPackageName(@NonNull Object receiver) {
|
||||
if (receiver instanceof BroadcastFilter) {
|
||||
return ((BroadcastFilter) receiver).receiverList.app.info.packageName;
|
||||
} else /* if (receiver instanceof ResolveInfo) */ {
|
||||
return ((ResolveInfo) receiver).activityInfo.packageName;
|
||||
}
|
||||
}
|
||||
|
||||
public BroadcastRecord maybeStripForHistory() {
|
||||
if (!intent.canStripForHistory()) {
|
||||
return this;
|
||||
|
||||
@@ -20,10 +20,12 @@ import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.app.trust.TrustManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.hardware.input.InputManagerInternal;
|
||||
import android.media.AudioManager;
|
||||
@@ -32,6 +34,7 @@ import android.media.RingtoneManager;
|
||||
import android.metrics.LogMaker;
|
||||
import android.net.Uri;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IWakeLockCallback;
|
||||
import android.os.Looper;
|
||||
@@ -137,7 +140,9 @@ public class Notifier {
|
||||
private final NotifierHandler mHandler;
|
||||
private final Executor mBackgroundExecutor;
|
||||
private final Intent mScreenOnIntent;
|
||||
private final Bundle mScreenOnOptions;
|
||||
private final Intent mScreenOffIntent;
|
||||
private final Bundle mScreenOffOptions;
|
||||
|
||||
// True if the device should suspend when the screen is off due to proximity.
|
||||
private final boolean mSuspendWhenScreenOffDueToProximityConfig;
|
||||
@@ -199,10 +204,14 @@ public class Notifier {
|
||||
mScreenOnIntent.addFlags(
|
||||
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
|
||||
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
|
||||
mScreenOnOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_SCREEN_OFF)).toBundle();
|
||||
mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
|
||||
mScreenOffIntent.addFlags(
|
||||
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
|
||||
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
|
||||
mScreenOffOptions = BroadcastOptions.makeRemovingMatchingFilter(
|
||||
new IntentFilter(Intent.ACTION_SCREEN_ON)).toBundle();
|
||||
|
||||
mSuspendWhenScreenOffDueToProximityConfig = context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);
|
||||
@@ -788,7 +797,8 @@ public class Notifier {
|
||||
|
||||
if (mActivityManagerInternal.isSystemReady()) {
|
||||
mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
|
||||
mWakeUpBroadcastDone, mHandler, 0, null, null);
|
||||
AppOpsManager.OP_NONE, mScreenOnOptions, mWakeUpBroadcastDone, mHandler,
|
||||
0, null, null);
|
||||
} else {
|
||||
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
|
||||
sendNextBroadcast();
|
||||
@@ -811,7 +821,8 @@ public class Notifier {
|
||||
|
||||
if (mActivityManagerInternal.isSystemReady()) {
|
||||
mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
|
||||
mGoToSleepBroadcastDone, mHandler, 0, null, null);
|
||||
AppOpsManager.OP_NONE, mScreenOffOptions, mGoToSleepBroadcastDone, mHandler,
|
||||
0, null, null);
|
||||
} else {
|
||||
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
|
||||
sendNextBroadcast();
|
||||
|
||||
@@ -37,12 +37,14 @@ import android.app.Activity;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.BroadcastOptions;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -88,6 +90,11 @@ public class BroadcastQueueModernImplTest {
|
||||
doReturn(4L).when(mQueue4).getRunnableAt();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
mHandlerThread.quit();
|
||||
}
|
||||
|
||||
private static void assertOrphan(BroadcastProcessQueue queue) {
|
||||
assertNull(queue.runnableAtNext);
|
||||
assertNull(queue.runnableAtPrev);
|
||||
@@ -291,4 +298,35 @@ public class BroadcastQueueModernImplTest {
|
||||
assertTrue(queue.isRunnable());
|
||||
assertEquals(airplaneRecord.enqueueTime, queue.getRunnableAt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that sending a broadcast that removes any matching pending
|
||||
* broadcasts is applied as expected.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveMatchingFilter() {
|
||||
final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
|
||||
final BroadcastOptions optionsOn = BroadcastOptions.makeBasic();
|
||||
optionsOn.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_OFF));
|
||||
|
||||
final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF);
|
||||
final BroadcastOptions optionsOff = BroadcastOptions.makeBasic();
|
||||
optionsOff.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_ON));
|
||||
|
||||
// Halt all processing so that we get a consistent view
|
||||
mHandlerThread.getLooper().getQueue().postSyncBarrier();
|
||||
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn));
|
||||
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff));
|
||||
|
||||
// Marching through the queue we should only have one SCREEN_OFF
|
||||
// broadcast, since that's the last state we dispatched
|
||||
final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN,
|
||||
getUidForPackage(PACKAGE_GREEN));
|
||||
queue.makeActiveNextPending();
|
||||
assertEquals(Intent.ACTION_SCREEN_OFF, queue.getActive().intent.getAction());
|
||||
assertTrue(queue.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,37 +654,28 @@ public class BroadcastQueueTest {
|
||||
receiverApp.mState.getReportedProcState());
|
||||
verify(mAms, times(2)).enqueueOomAdjTargetLocked(eq(receiverApp));
|
||||
|
||||
// Confirm that app was thawed
|
||||
if ((mImpl == Impl.DEFAULT) && (receiverApp == receiverBlueApp)) {
|
||||
// Nuance: the default implementation doesn't ask for manifest
|
||||
// cold-started apps to be thawed, but the modern stack does
|
||||
} else {
|
||||
// Confirm that app was thawed
|
||||
verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(receiverApp),
|
||||
eq(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER));
|
||||
|
||||
// Confirm that we added package to process
|
||||
verify(receiverApp, atLeastOnce()).addPackage(eq(receiverApp.info.packageName),
|
||||
anyLong(), any());
|
||||
}
|
||||
|
||||
// Confirm that we've reported package as being used
|
||||
verify(mAms, atLeastOnce()).notifyPackageUse(eq(receiverApp.info.packageName),
|
||||
eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
|
||||
|
||||
// Confirm that we unstopped manifest receivers
|
||||
verify(mAms.mPackageManagerInt, atLeastOnce()).setPackageStoppedState(
|
||||
eq(receiverApp.info.packageName), eq(false), eq(UserHandle.USER_SYSTEM));
|
||||
}
|
||||
|
||||
// Confirm that we've reported relevant packages as being used, but
|
||||
// only called for manifest receivers
|
||||
verify(mAms, never()).notifyPackageUse(eq(PACKAGE_RED),
|
||||
eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
|
||||
verify(mAms, atLeastOnce()).notifyPackageUse(eq(PACKAGE_GREEN),
|
||||
eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
|
||||
verify(mAms, atLeastOnce()).notifyPackageUse(eq(PACKAGE_BLUE),
|
||||
eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
|
||||
verify(mAms, atLeastOnce()).notifyPackageUse(eq(PACKAGE_YELLOW),
|
||||
eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
|
||||
|
||||
// Confirm that we unstopped manifest receivers
|
||||
verify(mAms.mPackageManagerInt, never()).setPackageStoppedState(eq(PACKAGE_RED),
|
||||
eq(false), eq(UserHandle.USER_SYSTEM));
|
||||
verify(mAms.mPackageManagerInt, atLeastOnce()).setPackageStoppedState(eq(PACKAGE_GREEN),
|
||||
eq(false), eq(UserHandle.USER_SYSTEM));
|
||||
verify(mAms.mPackageManagerInt, atLeastOnce()).setPackageStoppedState(eq(PACKAGE_BLUE),
|
||||
eq(false), eq(UserHandle.USER_SYSTEM));
|
||||
verify(mAms.mPackageManagerInt, atLeastOnce()).setPackageStoppedState(eq(PACKAGE_YELLOW),
|
||||
eq(false), eq(UserHandle.USER_SYSTEM));
|
||||
|
||||
// Confirm that we've reported expected usage events
|
||||
verify(mAms.mUsageStatsService).reportBroadcastDispatched(eq(callerApp.uid),
|
||||
eq(PACKAGE_YELLOW), eq(UserHandle.SYSTEM), eq(42L), anyLong(), anyInt());
|
||||
|
||||
Reference in New Issue
Block a user