blob: e389821595a0015ca5ddcee1d81d7375cef19b8c [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.am;
import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER;
import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
import static android.text.TextUtils.formatSimple;
import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED;
import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
import static com.android.internal.util.FrameworkStatsLog.BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
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_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.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_MU;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ApplicationExitInfo;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.pm.UserJourneyLogger;
import com.android.server.pm.UserManagerInternal;
import dalvik.annotation.optimization.NeverCompile;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Set;
import java.util.function.BooleanSupplier;
/**
* BROADCASTS
*
* We keep three broadcast queues and associated bookkeeping, one for those at
* foreground priority, and one for normal (background-priority) broadcasts, and one to
* offload special broadcasts that we know take a long time, such as BOOT_COMPLETED.
*/
public class BroadcastQueueImpl extends BroadcastQueue {
private static final String TAG_MU = TAG + POSTFIX_MU;
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
final BroadcastConstants mConstants;
/**
* If true, we can delay broadcasts while waiting services to finish in the previous
* receiver's process.
*/
final boolean mDelayBehindServices;
final int mSchedGroup;
/**
* Lists of all active broadcasts that are to be executed immediately
* (without waiting for another broadcast to finish). Currently this only
* contains broadcasts to registered receivers, to avoid spinning up
* a bunch of processes to execute IntentReceiver components. Background-
* and foreground-priority broadcasts are queued separately.
*/
final ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();
/**
* Tracking of the ordered broadcast queue, including deferral policy and alarm
* prioritization.
*/
final BroadcastDispatcher mDispatcher;
/**
* Refcounting for completion callbacks of split/deferred broadcasts. The key
* is an opaque integer token assigned lazily when a broadcast is first split
* into multiple BroadcastRecord objects.
*/
final SparseIntArray mSplitRefcounts = new SparseIntArray();
private int mNextToken = 0;
/**
* Set when we current have a BROADCAST_INTENT_MSG in flight.
*/
boolean mBroadcastsScheduled = false;
/**
* True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler.
*/
boolean mPendingBroadcastTimeoutMessage;
/**
* Intent broadcasts that we have tried to start, but are
* waiting for the application's process to be created. We only
* need one per scheduling class (instead of a list) because we always
* process broadcasts one at a time, so no others can be started while
* waiting for this one.
*/
BroadcastRecord mPendingBroadcast = null;
/**
* The receiver index that is pending, to restart the broadcast if needed.
*/
int mPendingBroadcastRecvIndex;
static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
// log latency metrics for ordered broadcasts during BOOT_COMPLETED processing
boolean mLogLatencyMetrics = true;
final BroadcastHandler mHandler;
private final class BroadcastHandler extends Handler {
public BroadcastHandler(Looper looper) {
super(looper, null);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
+ mQueueName + "]");
processNextBroadcast(true);
} break;
case BROADCAST_TIMEOUT_MSG: {
synchronized (mService) {
broadcastTimeoutLocked(true);
}
} break;
}
}
}
BroadcastQueueImpl(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, boolean allowDelayBehindServices,
int schedGroup) {
this(service, handler, name, constants, new BroadcastSkipPolicy(service),
new BroadcastHistory(constants), allowDelayBehindServices, schedGroup);
}
BroadcastQueueImpl(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, BroadcastSkipPolicy skipPolicy,
BroadcastHistory history, boolean allowDelayBehindServices, int schedGroup) {
super(service, handler, name, skipPolicy, history);
mHandler = new BroadcastHandler(handler.getLooper());
mConstants = constants;
mDelayBehindServices = allowDelayBehindServices;
mSchedGroup = schedGroup;
mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
}
public void start(ContentResolver resolver) {
mDispatcher.start();
mConstants.startObserving(mHandler, resolver);
}
public boolean isDelayBehindServices() {
return mDelayBehindServices;
}
public BroadcastRecord getPendingBroadcastLocked() {
return mPendingBroadcast;
}
public BroadcastRecord getActiveBroadcastLocked() {
return mDispatcher.getActiveBroadcastLocked();
}
public int getPreferredSchedulingGroupLocked(ProcessRecord app) {
final BroadcastRecord active = getActiveBroadcastLocked();
if (active != null && active.curApp == app) {
return mSchedGroup;
}
final BroadcastRecord pending = getPendingBroadcastLocked();
if (pending != null && pending.curApp == app) {
return mSchedGroup;
}
return ProcessList.SCHED_GROUP_UNDEFINED;
}
public void enqueueBroadcastLocked(BroadcastRecord r) {
r.applySingletonPolicy(mService);
final boolean replacePending = (r.intent.getFlags()
& Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
// Ordered broadcasts obviously need to be dispatched in serial order,
// but this implementation expects all manifest receivers to also be
// dispatched in a serial fashion
boolean serialDispatch = r.ordered;
if (!serialDispatch) {
final int N = (r.receivers != null) ? r.receivers.size() : 0;
for (int i = 0; i < N; i++) {
if (r.receivers.get(i) instanceof ResolveInfo) {
serialDispatch = true;
break;
}
}
}
if (serialDispatch) {
final BroadcastRecord oldRecord =
replacePending ? replaceOrderedBroadcastLocked(r) : null;
if (oldRecord != null) {
// Replaced, fire the result-to receiver.
if (oldRecord.resultTo != null) {
try {
oldRecord.mIsReceiverAppRunning = true;
performReceiveLocked(oldRecord.resultToApp, oldRecord.resultTo,
oldRecord.intent,
Activity.RESULT_CANCELED, null, null,
false, false, oldRecord.shareIdentity, oldRecord.userId,
oldRecord.callingUid, r.callingUid, r.callerPackage,
SystemClock.uptimeMillis() - oldRecord.enqueueTime, 0);
} catch (RemoteException e) {
Slog.w(TAG, "Failure ["
+ mQueueName + "] sending broadcast result of "
+ oldRecord.intent, e);
}
}
} else {
enqueueOrderedBroadcastLocked(r);
scheduleBroadcastsLocked();
}
} else {
final boolean replaced = replacePending
&& (replaceParallelBroadcastLocked(r) != null);
// Note: We assume resultTo is null for non-ordered broadcasts.
if (!replaced) {
enqueueParallelBroadcastLocked(r);
scheduleBroadcastsLocked();
}
}
}
public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
r.enqueueClockTime = System.currentTimeMillis();
r.enqueueTime = SystemClock.uptimeMillis();
r.enqueueRealTime = SystemClock.elapsedRealtime();
mParallelBroadcasts.add(r);
enqueueBroadcastHelper(r);
}
public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
r.enqueueClockTime = System.currentTimeMillis();
r.enqueueTime = SystemClock.uptimeMillis();
r.enqueueRealTime = SystemClock.elapsedRealtime();
mDispatcher.enqueueOrderedBroadcastLocked(r);
enqueueBroadcastHelper(r);
}
/**
* Don't call this method directly; call enqueueParallelBroadcastLocked or
* enqueueOrderedBroadcastLocked.
*/
private void enqueueBroadcastHelper(BroadcastRecord r) {
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
System.identityHashCode(r));
}
}
/**
* Find the same intent from queued parallel broadcast, replace with a new one and return
* the old one.
*/
public final BroadcastRecord replaceParallelBroadcastLocked(BroadcastRecord r) {
return replaceBroadcastLocked(mParallelBroadcasts, r, "PARALLEL");
}
/**
* Find the same intent from queued ordered broadcast, replace with a new one and return
* the old one.
*/
public final BroadcastRecord replaceOrderedBroadcastLocked(BroadcastRecord r) {
return mDispatcher.replaceBroadcastLocked(r, "ORDERED");
}
private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> queue,
BroadcastRecord r, String typeForLogging) {
final Intent intent = r.intent;
for (int i = queue.size() - 1; i > 0; i--) {
final BroadcastRecord old = queue.get(i);
if (old.userId == r.userId && intent.filterEquals(old.intent)) {
if (DEBUG_BROADCAST) {
Slog.v(TAG_BROADCAST, "***** DROPPING "
+ typeForLogging + " [" + mQueueName + "]: " + intent);
}
queue.set(i, r);
return old;
}
}
return null;
}
private final void processCurBroadcastLocked(BroadcastRecord r,
ProcessRecord app) throws RemoteException {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " for app " + app);
final IApplicationThread thread = app.getThread();
if (thread == null) {
throw new RemoteException();
}
if (app.isInFullBackup()) {
skipReceiverLocked(r);
return;
}
r.curApp = app;
final ProcessReceiverRecord prr = app.mReceivers;
prr.addCurReceiver(r);
app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
// Don't bump its LRU position if it's in the background restricted.
if (mService.mInternal.getRestrictionLevel(app.info.packageName, app.userId)
< RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
mService.updateLruProcessLocked(app, false, null);
}
// Make sure the oom adj score is updated before delivering the broadcast.
// Force an update, even if there are other pending requests, overall it still saves time,
// because time(updateOomAdj(N apps)) <= N * time(updateOomAdj(1 app)).
mService.enqueueOomAdjTargetLocked(app);
mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
// Tell the application to launch this receiver.
maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
r.intent.setComponent(r.curComponent);
// See if we need to delay the freezer based on BroadcastOptions
if (r.options != null
&& r.options.getTemporaryAppAllowlistDuration() > 0
&& r.options.getTemporaryAppAllowlistType()
== TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER,
r.options.getTemporaryAppAllowlistDuration());
}
boolean started = false;
try {
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Delivering to component " + r.curComponent
+ ": " + r);
mService.notifyPackageUse(r.intent.getComponent().getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
final boolean assumeDelivered = false;
thread.scheduleReceiver(
prepareReceiverIntent(r.intent, r.curFilteredExtras),
r.curReceiver, null /* compatInfo (unused but need to keep method signature) */,
r.resultCode, r.resultData, r.resultExtras, r.ordered, assumeDelivered,
r.userId, r.shareIdentity ? r.callingUid : Process.INVALID_UID,
app.mState.getReportedProcState(),
r.shareIdentity ? r.callerPackage : null);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + " DELIVERED for app " + app);
started = true;
} finally {
if (!started) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Process cur broadcast " + r + ": NOT STARTED!");
r.curApp = null;
prr.removeCurReceiver(r);
}
}
// if something bad happens here, launch the app and try again
if (app.isKilled()) {
throw new RemoteException("app gets killed during broadcasting");
}
}
/**
* Called by ActivityManagerService to notify that the uid has process started, if there is any
* deferred BOOT_COMPLETED broadcast, the BroadcastDispatcher can dispatch the broadcast now.
* @param uid
*/
public void updateUidReadyForBootCompletedBroadcastLocked(int uid) {
mDispatcher.updateUidReadyForBootCompletedBroadcastLocked(uid);
scheduleBroadcastsLocked();
}
public boolean onApplicationAttachedLocked(ProcessRecord app) {
updateUidReadyForBootCompletedBroadcastLocked(app.uid);
if (mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
return sendPendingBroadcastsLocked(app);
} else {
return false;
}
}
public void onApplicationTimeoutLocked(ProcessRecord app) {
skipCurrentOrPendingReceiverLocked(app);
}
public void onApplicationProblemLocked(ProcessRecord app) {
skipCurrentOrPendingReceiverLocked(app);
}
public void onApplicationCleanupLocked(ProcessRecord app) {
skipCurrentOrPendingReceiverLocked(app);
}
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
boolean didSomething = false;
final BroadcastRecord br = mPendingBroadcast;
if (br != null && br.curApp.getPid() > 0 && br.curApp.getPid() == app.getPid()) {
if (br.curApp != app) {
Slog.e(TAG, "App mismatch when sending pending broadcast to "
+ app.processName + ", intended target is " + br.curApp.processName);
return false;
}
try {
mPendingBroadcast = null;
br.mIsReceiverAppRunning = false;
processCurBroadcastLocked(br, app);
didSomething = true;
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting receiver "
+ br.curComponent.flattenToShortString(), e);
logBroadcastReceiverDiscardLocked(br);
finishReceiverLocked(br, br.resultCode, br.resultData,
br.resultExtras, br.resultAbort, false);
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
br.state = BroadcastRecord.IDLE;
throw new RuntimeException(e.getMessage());
}
}
return didSomething;
}
// Skip the current receiver, if any, that is in flight to the given process
public boolean skipCurrentOrPendingReceiverLocked(ProcessRecord app) {
BroadcastRecord r = null;
final BroadcastRecord curActive = mDispatcher.getActiveBroadcastLocked();
if (curActive != null && curActive.curApp == app) {
// confirmed: the current active broadcast is to the given app
r = curActive;
}
// If the current active broadcast isn't this BUT we're waiting for
// mPendingBroadcast to spin up the target app, that's what we use.
if (r == null && mPendingBroadcast != null && mPendingBroadcast.curApp == app) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"[" + mQueueName + "] skip & discard pending app " + r);
r = mPendingBroadcast;
}
if (r != null) {
skipReceiverLocked(r);
return true;
} else {
return false;
}
}
private void skipReceiverLocked(BroadcastRecord r) {
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
}
public void scheduleBroadcastsLocked() {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
+ mQueueName + "]: current="
+ mBroadcastsScheduled);
if (mBroadcastsScheduled) {
return;
}
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
mBroadcastsScheduled = true;
}
public BroadcastRecord getMatchingOrderedReceiver(ProcessRecord app) {
BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
if (br == null) {
Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
+ "] no active broadcast");
return null;
}
if (br.curApp != app) {
Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName
+ "] active broadcast " + br.curApp + " doesn't match " + app);
return null;
}
return br;
}
// > 0 only, no worry about "eventual" recycling
private int nextSplitTokenLocked() {
int next = mNextToken + 1;
if (next <= 0) {
next = 1;
}
mNextToken = next;
return next;
}
private void postActivityStartTokenRemoval(ProcessRecord app, BroadcastRecord r) {
// the receiver had run for less than allowed bg activity start timeout,
// so allow the process to still start activities from bg for some more time
String msgToken = (app.toShortString() + r.toString()).intern();
// first, if there exists a past scheduled request to remove this token, drop
// that request - we don't want the token to be swept from under our feet...
mHandler.removeCallbacksAndMessages(msgToken);
// ...then schedule the removal of the token after the extended timeout
mHandler.postAtTime(() -> {
synchronized (mService) {
app.removeBackgroundStartPrivileges(r);
}
}, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT));
}
public boolean finishReceiverLocked(ProcessRecord app, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
final BroadcastRecord r = getMatchingOrderedReceiver(app);
if (r != null) {
return finishReceiverLocked(r, resultCode,
resultData, resultExtras, resultAbort, waitForServices);
} else {
return false;
}
}
public boolean finishReceiverLocked(BroadcastRecord r, int resultCode,
String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) {
final int state = r.state;
final ActivityInfo receiver = r.curReceiver;
final long finishTime = SystemClock.uptimeMillis();
final long elapsed = finishTime - r.receiverTime;
r.state = BroadcastRecord.IDLE;
final int curIndex = r.nextReceiver - 1;
final int packageState = r.mWasReceiverAppStopped
? SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED
: SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
if (curIndex >= 0 && curIndex < r.receivers.size() && r.curApp != null) {
final Object curReceiver = r.receivers.get(curIndex);
FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, r.curApp.uid,
r.callingUid == -1 ? Process.SYSTEM_UID : r.callingUid,
r.intent.getAction(),
curReceiver instanceof BroadcastFilter
? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
: BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST,
r.mIsReceiverAppRunning
? BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM
: BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD,
r.dispatchTime - r.enqueueTime,
r.receiverTime - r.dispatchTime,
finishTime - r.receiverTime,
packageState,
r.curApp.info.packageName,
r.callerPackage);
}
if (state == BroadcastRecord.IDLE) {
Slog.w(TAG_BROADCAST, "finishReceiver [" + mQueueName + "] called but state is IDLE");
}
if (r.mBackgroundStartPrivileges.allowsAny() && r.curApp != null) {
if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) {
// if the receiver has run for more than allowed bg activity start timeout,
// just remove the token for this process now and we're done
r.curApp.removeBackgroundStartPrivileges(r);
} else {
// It gets more time; post the removal to happen at the appropriate moment
postActivityStartTokenRemoval(r.curApp, r);
}
}
// If we're abandoning this broadcast before any receivers were actually spun up,
// nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply.
if (r.nextReceiver > 0) {
r.terminalTime[r.nextReceiver - 1] = finishTime;
}
// if this receiver was slow, impose deferral policy on the app. This will kick in
// when processNextBroadcastLocked() next finds this uid as a receiver identity.
if (!r.timeoutExempt) {
// r.curApp can be null if finish has raced with process death - benign
// edge case, and we just ignore it because we're already cleaning up
// as expected.
if (r.curApp != null
&& mConstants.SLOW_TIME > 0 && elapsed > mConstants.SLOW_TIME) {
// Core system packages are exempt from deferral policy
if (!UserHandle.isCore(r.curApp.uid)) {
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Broadcast receiver " + (r.nextReceiver - 1)
+ " was slow: " + receiver + " br=" + r);
}
mDispatcher.startDeferring(r.curApp.uid);
} else {
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Core uid " + r.curApp.uid
+ " receiver was slow but not deferring: "
+ receiver + " br=" + r);
}
}
}
} else {
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Finished broadcast " + r.intent.getAction()
+ " is exempt from deferral policy");
}
}
r.intent.setComponent(null);
if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) {
r.curApp.mReceivers.removeCurReceiver(r);
mService.enqueueOomAdjTargetLocked(r.curApp);
}
if (r.curFilter != null) {
r.curFilter.receiverList.curBroadcast = null;
}
r.curFilter = null;
r.curReceiver = null;
r.curApp = null;
r.curFilteredExtras = null;
r.mWasReceiverAppStopped = false;
mPendingBroadcast = null;
r.resultCode = resultCode;
r.resultData = resultData;
r.resultExtras = resultExtras;
if (resultAbort && (r.intent.getFlags()&Intent.FLAG_RECEIVER_NO_ABORT) == 0) {
r.resultAbort = resultAbort;
} else {
r.resultAbort = false;
}
// If we want to wait behind services *AND* we're finishing the head/
// active broadcast on its queue
if (waitForServices && r.curComponent != null && r.queue.isDelayBehindServices()
&& ((BroadcastQueueImpl) r.queue).getActiveBroadcastLocked() == r) {
ActivityInfo nextReceiver;
if (r.nextReceiver < r.receivers.size()) {
Object obj = r.receivers.get(r.nextReceiver);
nextReceiver = (obj instanceof ActivityInfo) ? (ActivityInfo)obj : null;
} else {
nextReceiver = null;
}
// Don't do this if the next receive is in the same process as the current one.
if (receiver == null || nextReceiver == null
|| receiver.applicationInfo.uid != nextReceiver.applicationInfo.uid
|| !receiver.processName.equals(nextReceiver.processName)) {
// In this case, we are ready to process the next receiver for the current broadcast,
// but are on a queue that would like to wait for services to finish before moving
// on. If there are background services currently starting, then we will go into a
// special state where we hold off on continuing this broadcast until they are done.
if (mService.mServices.hasBackgroundServicesLocked(r.userId)) {
Slog.i(TAG, "Delay finish: " + r.curComponent.flattenToShortString());
r.state = BroadcastRecord.WAITING_SERVICES;
return false;
}
}
}
r.curComponent = null;
// We will process the next receiver right now if this is finishing
// an app receiver (which is always asynchronous) or after we have
// come back from calling a receiver.
final boolean doNext = (state == BroadcastRecord.APP_RECEIVE)
|| (state == BroadcastRecord.CALL_DONE_RECEIVE);
if (doNext) {
processNextBroadcastLocked(/* fromMsg= */ false, /* skipOomAdj= */ true);
}
return doNext;
}
public void backgroundServicesFinishedLocked(int userId) {
BroadcastRecord br = mDispatcher.getActiveBroadcastLocked();
if (br != null) {
if (br.userId == userId && br.state == BroadcastRecord.WAITING_SERVICES) {
Slog.i(TAG, "Resuming delayed broadcast");
br.curComponent = null;
br.state = BroadcastRecord.IDLE;
processNextBroadcastLocked(false, false);
}
}
}
public void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, boolean shareIdentity, int sendingUser,
int receiverUid, int callingUid, String callingPackage,
long dispatchDelay, long receiveDelay) throws RemoteException {
// If the broadcaster opted-in to sharing their identity, then expose package visibility for
// the receiver.
if (shareIdentity) {
mService.mPackageManagerInt.grantImplicitAccess(sendingUser, intent,
UserHandle.getAppId(receiverUid), callingUid, true);
}
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {
final IApplicationThread thread = app.getThread();
if (thread != null) {
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
final boolean assumeDelivered = !ordered;
thread.scheduleRegisteredReceiver(
receiver, intent, resultCode,
data, extras, ordered, sticky, assumeDelivered, sendingUser,
app.mState.getReportedProcState(),
shareIdentity ? callingUid : Process.INVALID_UID,
shareIdentity ? callingPackage : null);
} catch (RemoteException ex) {
// Failed to call into the process. It's either dying or wedged. Kill it gently.
synchronized (mService) {
final String msg = "Failed to schedule " + intent + " to " + receiver
+ " via " + app + ": " + ex;
Slog.w(TAG, msg);
app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
}
throw ex;
}
} else {
// Application has died. Receiver doesn't exist.
throw new RemoteException("app.thread must not be null");
}
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);
}
if (!ordered) {
FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED,
receiverUid == -1 ? Process.SYSTEM_UID : receiverUid,
callingUid == -1 ? Process.SYSTEM_UID : callingUid,
intent.getAction(),
BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME,
BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM,
dispatchDelay, receiveDelay, 0 /* finish_delay */,
SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL,
app != null ? app.info.packageName : null, callingPackage);
}
}
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
boolean skip = mSkipPolicy.shouldSkip(r, filter);
// Filter packages in the intent extras, skipping delivery if none of the packages is
// visible to the receiver.
Bundle filteredExtras = null;
if (!skip && r.filterExtrasForReceiver != null) {
final Bundle extras = r.intent.getExtras();
if (extras != null) {
filteredExtras = r.filterExtrasForReceiver.apply(filter.receiverList.uid, extras);
if (filteredExtras == null) {
if (DEBUG_BROADCAST) {
Slog.v(TAG, "Skipping delivery to "
+ filter.receiverList.app
+ " : receiver is filtered by the package visibility");
}
skip = true;
}
}
}
if (skip) {
r.delivery[index] = BroadcastRecord.DELIVERY_SKIPPED;
return;
}
r.delivery[index] = BroadcastRecord.DELIVERY_DELIVERED;
// If this is not being sent as an ordered broadcast, then we
// don't want to touch the fields that keep track of the current
// state of ordered broadcasts.
if (ordered) {
r.curFilter = filter;
filter.receiverList.curBroadcast = r;
r.state = BroadcastRecord.CALL_IN_RECEIVE;
if (filter.receiverList.app != null) {
// Bump hosting application to no longer be in background
// scheduling class. Note that we can't do that if there
// isn't an app... but we can only be in that case for
// things that directly call the IActivityManager API, which
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
filter.receiverList.app.mReceivers.addCurReceiver(r);
mService.enqueueOomAdjTargetLocked(r.curApp);
mService.updateOomAdjPendingTargetsLocked(
OOM_ADJ_REASON_START_RECEIVER);
}
} else if (filter.receiverList.app != null) {
mService.mOomAdjuster.unfreezeTemporarily(filter.receiverList.app,
CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
}
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
final boolean isInFullBackup = (filter.receiverList.app != null)
&& filter.receiverList.app.isInFullBackup();
final boolean isKilled = (filter.receiverList.app != null)
&& filter.receiverList.app.isKilled();
if (isInFullBackup || isKilled) {
// Skip delivery if full backup in progress
// If it's an ordered broadcast, we need to continue to the next receiver.
if (ordered) {
skipReceiverLocked(r);
}
} else {
r.receiverTime = SystemClock.uptimeMillis();
r.scheduledTime[index] = r.receiverTime;
maybeAddBackgroundStartPrivileges(filter.receiverList.app, r);
maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
prepareReceiverIntent(r.intent, filteredExtras), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.shareIdentity, r.userId,
filter.receiverList.uid, r.callingUid, r.callerPackage,
r.dispatchTime - r.enqueueTime,
r.receiverTime - r.dispatchTime);
// parallel broadcasts are fire-and-forget, not bookended by a call to
// finishReceiverLocked(), so we manage their activity-start token here
if (filter.receiverList.app != null
&& r.mBackgroundStartPrivileges.allowsAny()
&& !r.ordered) {
postActivityStartTokenRemoval(filter.receiverList.app, r);
}
}
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
} catch (RemoteException e) {
Slog.w(TAG, "Failure sending broadcast " + r.intent, e);
// Clean up ProcessRecord state related to this broadcast attempt
if (filter.receiverList.app != null) {
filter.receiverList.app.removeBackgroundStartPrivileges(r);
if (ordered) {
filter.receiverList.app.mReceivers.removeCurReceiver(r);
// Something wrong, its oom adj could be downgraded, but not in a hurry.
mService.enqueueOomAdjTargetLocked(r.curApp);
}
}
// And BroadcastRecord state related to ordered delivery, if appropriate
if (ordered) {
r.curFilter = null;
filter.receiverList.curBroadcast = null;
}
}
}
void maybeScheduleTempAllowlistLocked(int uid, BroadcastRecord r,
@Nullable BroadcastOptions brOptions) {
if (brOptions == null || brOptions.getTemporaryAppAllowlistDuration() <= 0) {
return;
}
long duration = brOptions.getTemporaryAppAllowlistDuration();
final @TempAllowListType int type = brOptions.getTemporaryAppAllowlistType();
final @ReasonCode int reasonCode = brOptions.getTemporaryAppAllowlistReasonCode();
final String reason = brOptions.getTemporaryAppAllowlistReason();
if (duration > Integer.MAX_VALUE) {
duration = Integer.MAX_VALUE;
}
// XXX ideally we should pause the broadcast until everything behind this is done,
// or else we will likely start dispatching the broadcast before we have opened
// access to the app (there is a lot of asynchronicity behind this). It is probably
// not that big a deal, however, because the main purpose here is to allow apps
// to hold wake locks, and they will be able to acquire their wake lock immediately
// it just won't be enabled until we get through this work.
StringBuilder b = new StringBuilder();
b.append("broadcast:");
UserHandle.formatUid(b, r.callingUid);
b.append(":");
if (r.intent.getAction() != null) {
b.append(r.intent.getAction());
} else if (r.intent.getComponent() != null) {
r.intent.getComponent().appendShortString(b);
} else if (r.intent.getData() != null) {
b.append(r.intent.getData());
}
b.append(",reason:");
b.append(reason);
if (DEBUG_BROADCAST) {
Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
+ " type=" + type + " : " + b.toString());
}
// Only add to temp allowlist if it's not the APP_FREEZING_DELAYED type. That will be
// handled when the broadcast is actually being scheduled on the app thread.
if (type != TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
r.callingUid);
}
}
private void processNextBroadcast(boolean fromMsg) {
synchronized (mService) {
processNextBroadcastLocked(fromMsg, false);
}
}
private static Intent prepareReceiverIntent(@NonNull Intent originalIntent,
@Nullable Bundle filteredExtras) {
final Intent intent = new Intent(originalIntent);
if (filteredExtras != null) {
intent.replaceExtras(filteredExtras);
}
return intent;
}
public void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
BroadcastRecord r;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "processNextBroadcast ["
+ mQueueName + "]: "
+ mParallelBroadcasts.size() + " parallel broadcasts; "
+ mDispatcher.describeStateLocked());
mService.updateCpuStats();
if (fromMsg) {
mBroadcastsScheduled = false;
}
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
r.mIsReceiverAppRunning = true;
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
System.identityHashCode(r));
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
System.identityHashCode(r));
}
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
deliverToRegisteredReceiverLocked(r,
(BroadcastFilter) target, false, i);
}
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ mQueueName + "] " + r);
}
// Now take care of the next serialized one...
// If we are waiting for a process to come up to handle the next
// broadcast, then do nothing at this point. Just in case, we
// check that the process we're waiting for still exists.
if (mPendingBroadcast != null) {
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"processNextBroadcast [" + mQueueName + "]: waiting for "
+ mPendingBroadcast.curApp);
boolean isDead;
if (mPendingBroadcast.curApp.getPid() > 0) {
synchronized (mService.mPidsSelfLocked) {
ProcessRecord proc = mService.mPidsSelfLocked.get(
mPendingBroadcast.curApp.getPid());
isDead = proc == null || proc.mErrorState.isCrashing();
}
} else {
final ProcessRecord proc = mService.mProcessList.getProcessNamesLOSP().get(
mPendingBroadcast.curApp.processName, mPendingBroadcast.curApp.uid);
isDead = proc == null || !proc.isPendingStart();
}
if (!isDead) {
// It's still alive, so keep waiting
return;
} else {
Slog.w(TAG, "pending app ["
+ mQueueName + "]" + mPendingBroadcast.curApp
+ " died before responding to broadcast");
mPendingBroadcast.state = BroadcastRecord.IDLE;
mPendingBroadcast.nextReceiver = mPendingBroadcastRecvIndex;
mPendingBroadcast = null;
}
}
boolean looped = false;
do {
final long now = SystemClock.uptimeMillis();
r = mDispatcher.getNextBroadcastLocked(now);
if (r == null) {
// No more broadcasts are deliverable right now, so all done!
mDispatcher.scheduleDeferralCheckLocked(false);
synchronized (mService.mAppProfiler.mProfilerLock) {
mService.mAppProfiler.scheduleAppGcsLPf();
}
if (looped && !skipOomAdj) {
// If we had finished the last ordered broadcast, then
// make sure all processes have correct oom and sched
// adjustments.
mService.updateOomAdjPendingTargetsLocked(
OOM_ADJ_REASON_START_RECEIVER);
}
// when we have no more ordered broadcast on this queue, stop logging
if (mService.mUserController.mBootCompleted && mLogLatencyMetrics) {
mLogLatencyMetrics = false;
}
return;
}
boolean forceReceive = false;
// Ensure that even if something goes awry with the timeout
// detection, we catch "hung" broadcasts here, discard them,
// and continue to make progress.
//
// This is only done if the system is ready so that early-stage receivers
// don't get executed with timeouts; and of course other timeout-
// exempt broadcasts are ignored.
int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (mService.mProcessesReady && !r.timeoutExempt && r.dispatchTime > 0) {
if ((numReceivers > 0) &&
(now > r.dispatchTime + (2 * mConstants.TIMEOUT * numReceivers))) {
Slog.w(TAG, "Hung broadcast ["
+ mQueueName + "] discarded after timeout failure:"
+ " now=" + now
+ " dispatchTime=" + r.dispatchTime
+ " startTime=" + r.receiverTime
+ " intent=" + r.intent
+ " numReceivers=" + numReceivers
+ " nextReceiver=" + r.nextReceiver
+ " state=" + r.state);
broadcastTimeoutLocked(false); // forcibly finish this broadcast
forceReceive = true;
r.state = BroadcastRecord.IDLE;
}
}
if (r.state != BroadcastRecord.IDLE) {
if (DEBUG_BROADCAST) Slog.d(TAG_BROADCAST,
"processNextBroadcast("
+ mQueueName + ") called when not idle (state="
+ r.state + ")");
return;
}
// Is the current broadcast is done for any reason?
if (r.receivers == null || r.nextReceiver >= numReceivers
|| r.resultAbort || forceReceive) {
// Send the final result if requested
if (r.resultTo != null) {
boolean sendResult = true;
// if this was part of a split/deferral complex, update the refcount and only
// send the completion when we clear all of them
if (r.splitToken != 0) {
int newCount = mSplitRefcounts.get(r.splitToken) - 1;
if (newCount == 0) {
// done! clear out this record's bookkeeping and deliver
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST,
"Sending broadcast completion for split token "
+ r.splitToken + " : " + r.intent.getAction());
}
mSplitRefcounts.delete(r.splitToken);
} else {
// still have some split broadcast records in flight; update refcount
// and hold off on the callback
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST,
"Result refcount now " + newCount + " for split token "
+ r.splitToken + " : " + r.intent.getAction()
+ " - not sending completion yet");
}
sendResult = false;
mSplitRefcounts.put(r.splitToken, newCount);
}
}
if (sendResult) {
if (r.callerApp != null) {
mService.mOomAdjuster.unfreezeTemporarily(
r.callerApp,
CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
}
try {
if (DEBUG_BROADCAST) {
Slog.i(TAG_BROADCAST, "Finishing broadcast [" + mQueueName + "] "
+ r.intent.getAction() + " app=" + r.callerApp);
}
if (r.dispatchTime == 0) {
// The dispatch time here could be 0, in case it's a parallel
// broadcast but it has a result receiver. Set it to now.
r.dispatchTime = now;
}
r.mIsReceiverAppRunning = true;
performReceiveLocked(r.resultToApp, r.resultTo,
new Intent(r.intent), r.resultCode,
r.resultData, r.resultExtras, false, false, r.shareIdentity,
r.userId, r.callingUid, r.callingUid, r.callerPackage,
r.dispatchTime - r.enqueueTime,
now - r.dispatchTime);
logBootCompletedBroadcastCompletionLatencyIfPossible(r);
// Set this to null so that the reference
// (local and remote) isn't kept in the mBroadcastHistory.
r.resultTo = null;
} catch (RemoteException e) {
r.resultTo = null;
Slog.w(TAG, "Failure ["
+ mQueueName + "] sending broadcast result of "
+ r.intent, e);
}
}
}
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Cancelling BROADCAST_TIMEOUT_MSG");
cancelBroadcastTimeoutLocked();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
"Finished with ordered broadcast " + r);
// ... and on to the next...
addBroadcastToHistoryLocked(r);
if (r.intent.getComponent() == null && r.intent.getPackage() == null
&& (r.intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
// This was an implicit broadcast... let's record it for posterity.
mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
r.manifestCount, r.manifestSkipCount, r.finishTime-r.dispatchTime);
}
mDispatcher.retireBroadcastLocked(r);
r = null;
looped = true;
continue;
}
// Check whether the next receiver is under deferral policy, and handle that
// accordingly. If the current broadcast was already part of deferred-delivery
// tracking, we know that it must now be deliverable as-is without re-deferral.
if (!r.deferred) {
final int receiverUid = r.getReceiverUid(r.receivers.get(r.nextReceiver));
if (mDispatcher.isDeferringLocked(receiverUid)) {
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Next receiver in " + r + " uid " + receiverUid
+ " at " + r.nextReceiver + " is under deferral");
}
// If this is the only (remaining) receiver in the broadcast, "splitting"
// doesn't make sense -- just defer it as-is and retire it as the
// currently active outgoing broadcast.
BroadcastRecord defer;
if (r.nextReceiver + 1 == numReceivers) {
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Sole receiver of " + r
+ " is under deferral; setting aside and proceeding");
}
defer = r;
mDispatcher.retireBroadcastLocked(r);
} else {
// Nontrivial case; split out 'uid's receivers to a new broadcast record
// and defer that, then loop and pick up continuing delivery of the current
// record (now absent those receivers).
// The split operation is guaranteed to match at least at 'nextReceiver'
defer = r.splitRecipientsLocked(receiverUid, r.nextReceiver);
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "Post split:");
Slog.i(TAG_BROADCAST, "Original broadcast receivers:");
for (int i = 0; i < r.receivers.size(); i++) {
Slog.i(TAG_BROADCAST, " " + r.receivers.get(i));
}
Slog.i(TAG_BROADCAST, "Split receivers:");
for (int i = 0; i < defer.receivers.size(); i++) {
Slog.i(TAG_BROADCAST, " " + defer.receivers.get(i));
}
}
// Track completion refcount as well if relevant
if (r.resultTo != null) {
int token = r.splitToken;
if (token == 0) {
// first split of this record; refcount for 'r' and 'deferred'
r.splitToken = defer.splitToken = nextSplitTokenLocked();
mSplitRefcounts.put(r.splitToken, 2);
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST,
"Broadcast needs split refcount; using new token "
+ r.splitToken);
}
} else {
// new split from an already-refcounted situation; increment count
final int curCount = mSplitRefcounts.get(token);
if (DEBUG_BROADCAST_DEFERRAL) {
if (curCount == 0) {
Slog.wtf(TAG_BROADCAST,
"Split refcount is zero with token for " + r);
}
}
mSplitRefcounts.put(token, curCount + 1);
if (DEBUG_BROADCAST_DEFERRAL) {
Slog.i(TAG_BROADCAST, "New split count for token " + token
+ " is " + (curCount + 1));
}
}
}
}
mDispatcher.addDeferredBroadcast(receiverUid, defer);
r = null;
looped = true;
continue;
}
}
} while (r == null);
// Get the next receiver...
int recIdx = r.nextReceiver++;
// Keep track of when this receiver started, and make sure there
// is a timeout message pending to kill it if need be.
r.receiverTime = SystemClock.uptimeMillis();
r.scheduledTime[recIdx] = r.receiverTime;
if (recIdx == 0) {
r.dispatchTime = r.receiverTime;
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
if (mLogLatencyMetrics) {
FrameworkStatsLog.write(
FrameworkStatsLog.BROADCAST_DISPATCH_LATENCY_REPORTED,
r.dispatchClockTime - r.enqueueClockTime);
}
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
System.identityHashCode(r));
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
System.identityHashCode(r));
}
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing ordered broadcast ["
+ mQueueName + "] " + r);
}
if (! mPendingBroadcastTimeoutMessage) {
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Submitting BROADCAST_TIMEOUT_MSG ["
+ mQueueName + "] for " + r + " at " + timeoutTime);
setBroadcastTimeoutLocked(timeoutTime);
}
final BroadcastOptions brOptions = r.options;
final Object nextReceiver = r.receivers.get(recIdx);
if (nextReceiver instanceof BroadcastFilter) {
// Simple case: this is a registered receiver who gets
// a direct call.
BroadcastFilter filter = (BroadcastFilter)nextReceiver;
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering ordered ["
+ mQueueName + "] to registered "
+ filter + ": " + r);
r.mIsReceiverAppRunning = true;
deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx);
if ((r.curReceiver == null && r.curFilter == null) || !r.ordered) {
// The receiver has already finished, so schedule to
// process the next one.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Quick finishing ["
+ mQueueName + "]: ordered=" + r.ordered
+ " curFilter=" + r.curFilter
+ " curReceiver=" + r.curReceiver);
r.state = BroadcastRecord.IDLE;
scheduleBroadcastsLocked();
} else {
if (filter.receiverList != null) {
maybeAddBackgroundStartPrivileges(filter.receiverList.app, r);
// r is guaranteed ordered at this point, so we know finishReceiverLocked()
// will get a callback and handle the activity start token lifecycle.
}
}
return;
}
// Hard case: need to instantiate the receiver, possibly
// starting its application process to host it.
final ResolveInfo info =
(ResolveInfo)nextReceiver;
final ComponentName component = new ComponentName(
info.activityInfo.applicationInfo.packageName,
info.activityInfo.name);
final int receiverUid = info.activityInfo.applicationInfo.uid;
final String targetProcess = info.activityInfo.processName;
final ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
info.activityInfo.applicationInfo.uid);
boolean skip = mSkipPolicy.shouldSkip(r, info);
// Filter packages in the intent extras, skipping delivery if none of the packages is
// visible to the receiver.
Bundle filteredExtras = null;
if (!skip && r.filterExtrasForReceiver != null) {
final Bundle extras = r.intent.getExtras();
if (extras != null) {
filteredExtras = r.filterExtrasForReceiver.apply(receiverUid, extras);
if (filteredExtras == null) {
if (DEBUG_BROADCAST) {
Slog.v(TAG, "Skipping delivery to "
+ info.activityInfo.packageName + " / " + receiverUid
+ " : receiver is filtered by the package visibility");
}
skip = true;
}
}
}
if (skip) {
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Skipping delivery of ordered [" + mQueueName + "] "
+ r + " for reason described above");
r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED;
r.curFilter = null;
r.state = BroadcastRecord.IDLE;
r.manifestSkipCount++;
scheduleBroadcastsLocked();
return;
}
r.manifestCount++;
r.delivery[recIdx] = BroadcastRecord.DELIVERY_DELIVERED;
r.state = BroadcastRecord.APP_RECEIVE;
r.curComponent = component;
r.curReceiver = info.activityInfo;
r.curFilteredExtras = filteredExtras;
if (DEBUG_MU && r.callingUid > UserHandle.PER_USER_RANGE) {
Slog.v(TAG_MU, "Updated broadcast record activity info for secondary user, "
+ info.activityInfo + ", callingUid = " + r.callingUid + ", uid = "
+ receiverUid);
}
final boolean isActivityCapable =
(brOptions != null && brOptions.getTemporaryAppAllowlistDuration() > 0);
maybeScheduleTempAllowlistLocked(receiverUid, r, brOptions);
// Report that a component is used for explicit broadcasts.
if (r.intent.getComponent() != null && r.curComponent != null
&& !TextUtils.equals(r.curComponent.getPackageName(), r.callerPackage)) {
mService.mUsageStatsService.reportEvent(
r.curComponent.getPackageName(), r.userId, Event.APP_COMPONENT_USED);
}
// Broadcast is being executed, its package can't be stopped.
try {
mService.mPackageManagerInt.setPackageStoppedState(
r.curComponent.getPackageName(), false, r.userId);
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.curComponent.getPackageName() + ": " + e);
}
// Is this receiver's application already running?
if (app != null && app.getThread() != null && !app.isKilled()) {
try {
app.addPackage(info.activityInfo.packageName,
info.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
maybeAddBackgroundStartPrivileges(app, r);
r.mIsReceiverAppRunning = true;
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
final String msg = "Failed to schedule " + r.intent + " to " + info
+ " via " + app + ": " + e;
Slog.w(TAG, msg);
app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Failed sending broadcast to "
+ r.curComponent + " with " + r.intent, e);
// If some unexpected exception happened, just skip
// this broadcast. At this point we are not in the call
// from a client, so throwing an exception out from here
// will crash the entire system instead of just whoever
// sent the broadcast.
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
// We need to reset the state if we failed to start the receiver.
r.state = BroadcastRecord.IDLE;
return;
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
// Registered whether we're bringing this package out of a stopped state
r.mWasReceiverAppStopped =
(info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0;
// Not running -- get it started, to be executed when the app comes up.
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Need to start app ["
+ mQueueName + "] " + targetProcess + " for broadcast " + r);
r.curApp = mService.startProcessLocked(targetProcess,
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
r.intent.getAction(), r.getHostingRecordTriggerType()),
isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
if (r.curApp == null) {
// Ah, this recipient is unavailable. Finish it if necessary,
// and mark the broadcast record as ready for the next.
Slog.w(TAG, "Unable to launch app "
+ info.activityInfo.applicationInfo.packageName + "/"
+ receiverUid + " for broadcast "
+ r.intent + ": process is bad");
logBroadcastReceiverDiscardLocked(r);
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
r.state = BroadcastRecord.IDLE;
return;
}
maybeAddBackgroundStartPrivileges(r.curApp, r);
mPendingBroadcast = r;
mPendingBroadcastRecvIndex = recIdx;
}
@Nullable
private String getTargetPackage(BroadcastRecord r) {
if (r.intent == null) {
return null;
}
if (r.intent.getPackage() != null) {
return r.intent.getPackage();
} else if (r.intent.getComponent() != null) {
return r.intent.getComponent().getPackageName();
}
return null;
}
static void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
// Only log after last receiver.
// In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
// last BroadcastRecord of the split broadcast which has non-null resultTo.
final int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
if (r.nextReceiver < numReceivers) {
return;
}
final String action = r.intent.getAction();
int event = 0;
if (Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(action)) {
event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__LOCKED_BOOT_COMPLETED;
} else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
event = BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED__EVENT__BOOT_COMPLETED;
}
if (event != 0) {
final int dispatchLatency = (int)(r.dispatchTime - r.enqueueTime);
final int completeLatency = (int)
(SystemClock.uptimeMillis() - r.enqueueTime);
final int dispatchRealLatency = (int)(r.dispatchRealTime - r.enqueueRealTime);
final int completeRealLatency = (int)
(SystemClock.elapsedRealtime() - r.enqueueRealTime);
int userType = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN;
// This method is called very infrequently, no performance issue we call
// LocalServices.getService() here.
final UserManagerInternal umInternal = LocalServices.getService(
UserManagerInternal.class);
final UserInfo userInfo =
(umInternal != null) ? umInternal.getUserInfo(r.userId) : null;
if (userInfo != null) {
userType = UserJourneyLogger.getUserTypeForStatsd(userInfo.userType);
}
Slog.i(TAG_BROADCAST,
"BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED action:"
+ action
+ " dispatchLatency:" + dispatchLatency
+ " completeLatency:" + completeLatency
+ " dispatchRealLatency:" + dispatchRealLatency
+ " completeRealLatency:" + completeRealLatency
+ " receiversSize:" + numReceivers
+ " userId:" + r.userId
+ " userType:" + (userInfo != null? userInfo.userType : null));
FrameworkStatsLog.write(
BOOT_COMPLETED_BROADCAST_COMPLETION_LATENCY_REPORTED,
event,
dispatchLatency,
completeLatency,
dispatchRealLatency,
completeRealLatency,
r.userId,
userType);
}
}
private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
return;
}
final String targetPackage = getTargetPackage(r);
// Ignore non-explicit broadcasts
if (targetPackage == null) {
return;
}
mService.mUsageStatsService.reportBroadcastDispatched(
r.callingUid, targetPackage, UserHandle.of(r.userId),
r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
mService.getUidStateLocked(targetUid));
}
private void maybeAddBackgroundStartPrivileges(ProcessRecord proc, BroadcastRecord r) {
if (r == null || proc == null || !r.mBackgroundStartPrivileges.allowsAny()) {
return;
}
String msgToken = (proc.toShortString() + r.toString()).intern();
// first, if there exists a past scheduled request to remove this token, drop
// that request - we don't want the token to be swept from under our feet...
mHandler.removeCallbacksAndMessages(msgToken);
// ...then add the token
proc.addOrUpdateBackgroundStartPrivileges(r, r.mBackgroundStartPrivileges);
}
final void setBroadcastTimeoutLocked(long timeoutTime) {
if (! mPendingBroadcastTimeoutMessage) {
Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this);
mHandler.sendMessageAtTime(msg, timeoutTime);
mPendingBroadcastTimeoutMessage = true;
}
}
final void cancelBroadcastTimeoutLocked() {
if (mPendingBroadcastTimeoutMessage) {
mHandler.removeMessages(BROADCAST_TIMEOUT_MSG, this);
mPendingBroadcastTimeoutMessage = false;
}
}
final void broadcastTimeoutLocked(boolean fromMsg) {
if (fromMsg) {
mPendingBroadcastTimeoutMessage = false;
}
if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastTimeoutLocked()");
try {
long now = SystemClock.uptimeMillis();
BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
if (fromMsg) {
if (!mService.mProcessesReady) {
// Only process broadcast timeouts if the system is ready; some early
// broadcasts do heavy work setting up system facilities
return;
}
// If the broadcast is generally exempt from timeout tracking, we're done
if (r.timeoutExempt) {
if (DEBUG_BROADCAST) {
Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+ r.intent.getAction());
}
return;
}
long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
if (timeoutTime > now) {
// We can observe premature timeouts because we do not cancel and reset the
// broadcast timeout message after each receiver finishes. Instead, we set up
// an initial timeout then kick it down the road a little further as needed
// when it expires.
if (DEBUG_BROADCAST) {
Slog.v(TAG_BROADCAST,
"Premature timeout ["
+ mQueueName + "] @ " + now
+ ": resetting BROADCAST_TIMEOUT_MSG for "
+ timeoutTime);
}
setBroadcastTimeoutLocked(timeoutTime);
return;
}
}
if (r.state == BroadcastRecord.WAITING_SERVICES) {
// In this case the broadcast had already finished, but we had decided to wait
// for started services to finish as well before going on. So if we have actually
// waited long enough time timeout the broadcast, let's give up on the whole thing
// and just move on to the next.
Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
? r.curComponent.flattenToShortString() : "(null)"));
r.curComponent = null;
r.state = BroadcastRecord.IDLE;
processNextBroadcastLocked(false, false);
return;
}
// If the receiver app is being debugged we quietly ignore unresponsiveness, just
// tidying up and moving on to the next broadcast without crashing or ANRing this
// app just because it's stopped at a breakpoint.
final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
long timeoutDurationMs = now - r.receiverTime;
Slog.w(TAG, "Timeout of broadcast " + r + " - curFilter=" + r.curFilter
+ " curReceiver=" + r.curReceiver + ", started " + timeoutDurationMs
+ "ms ago");
r.receiverTime = now;
if (!debugging) {
r.anrCount++;
}
ProcessRecord app = null;
Object curReceiver;
if (r.nextReceiver > 0) {
curReceiver = r.receivers.get(r.nextReceiver - 1);
r.delivery[r.nextReceiver - 1] = BroadcastRecord.DELIVERY_TIMEOUT;
} else {
curReceiver = r.curReceiver;
}
Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
logBroadcastReceiverDiscardLocked(r);
TimeoutRecord timeoutRecord = TimeoutRecord.forBroadcastReceiver(r.intent,
timeoutDurationMs);
if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter) curReceiver;
if (bf.receiverList.pid != 0
&& bf.receiverList.pid != ActivityManagerService.MY_PID) {
timeoutRecord.mLatencyTracker.waitingOnPidLockStarted();
synchronized (mService.mPidsSelfLocked) {
timeoutRecord.mLatencyTracker.waitingOnPidLockEnded();
app = mService.mPidsSelfLocked.get(
bf.receiverList.pid);
}
}
} else {
app = r.curApp;
}
if (mPendingBroadcast == r) {
mPendingBroadcast = null;
}
// Move on to the next receiver.
finishReceiverLocked(r, r.resultCode, r.resultData,
r.resultExtras, r.resultAbort, false);
scheduleBroadcastsLocked();
// The ANR should only be triggered if we have a process record (app is non-null)
if (!debugging && app != null) {
mService.appNotResponding(app, timeoutRecord);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
if (original.callingUid < 0) {
// This was from a registerReceiver() call; ignore it.
return;
}
original.finishTime = SystemClock.uptimeMillis();
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
createBroadcastTraceTitle(original, BroadcastRecord.DELIVERY_DELIVERED),
System.identityHashCode(original));
}
mService.notifyBroadcastFinishedLocked(original);
mHistory.addBroadcastToHistoryLocked(original);
}
public boolean cleanupDisabledPackageReceiversLocked(
String packageName, Set<String> filterByClasses, int userId) {
boolean didSomething = false;
for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
packageName, filterByClasses, userId, true);
}
didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
filterByClasses, userId, true);
return didSomething;
}
final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
final int logIndex = r.nextReceiver - 1;
if (logIndex >= 0 && logIndex < r.receivers.size()) {
Object curReceiver = r.receivers.get(logIndex);
if (curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter) curReceiver;
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
bf.owningUserId, System.identityHashCode(r),
r.intent.getAction(), logIndex, System.identityHashCode(bf));
} else {
ResolveInfo ri = (ResolveInfo) curReceiver;
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
}
} else {
if (logIndex < 0) Slog.w(TAG,
"Discarding broadcast before first receiver is invoked: " + r);
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-1, System.identityHashCode(r),
r.intent.getAction(),
r.nextReceiver,
"NONE");
}
}
private String createBroadcastTraceTitle(BroadcastRecord record, int state) {
return formatSimple("Broadcast %s from %s (%s) %s",
state == BroadcastRecord.DELIVERY_PENDING ? "in queue" : "dispatched",
record.callerPackage == null ? "" : record.callerPackage,
record.callerApp == null ? "process unknown" : record.callerApp.toShortString(),
record.intent == null ? "" : record.intent.getAction());
}
public boolean isIdleLocked() {
return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle()
&& (mPendingBroadcast == null);
}
public boolean isBeyondBarrierLocked(long barrierTime) {
// If nothing active, we're beyond barrier
if (isIdleLocked()) return true;
// Check if parallel broadcasts are beyond barrier
for (int i = 0; i < mParallelBroadcasts.size(); i++) {
if (mParallelBroadcasts.get(i).enqueueTime <= barrierTime) {
return false;
}
}
// Check if pending broadcast is beyond barrier
final BroadcastRecord pending = getPendingBroadcastLocked();
if ((pending != null) && pending.enqueueTime <= barrierTime) {
return false;
}
return mDispatcher.isBeyondBarrier(barrierTime);
}
public boolean isDispatchedLocked(Intent intent) {
if (isIdleLocked()) return true;
for (int i = 0; i < mParallelBroadcasts.size(); i++) {
if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) {
return false;
}
}
final BroadcastRecord pending = getPendingBroadcastLocked();
if ((pending != null) && intent.filterEquals(pending.intent)) {
return false;
}
return mDispatcher.isDispatched(intent);
}
public void waitForIdle(PrintWriter pw) {
waitFor(() -> isIdleLocked(), pw, "idle");
}
public void waitForBarrier(PrintWriter pw) {
final long barrierTime = SystemClock.uptimeMillis();
waitFor(() -> isBeyondBarrierLocked(barrierTime), pw, "barrier");
}
public void waitForDispatched(Intent intent, PrintWriter pw) {
waitFor(() -> isDispatchedLocked(intent), pw, "dispatch");
}
private void waitFor(BooleanSupplier condition, PrintWriter pw, String conditionName) {
long lastPrint = 0;
while (true) {
synchronized (mService) {
if (condition.getAsBoolean()) {
final String msg = "Queue [" + mQueueName + "] reached " + conditionName
+ " condition";
Slog.v(TAG, msg);
if (pw != null) {
pw.println(msg);
pw.flush();
}
return;
}
}
// Print at most every second
final long now = SystemClock.uptimeMillis();
if (now >= lastPrint + 1000) {
lastPrint = now;
final String msg = "Queue [" + mQueueName + "] waiting for " + conditionName
+ " condition; state is " + describeStateLocked();
Slog.v(TAG, msg);
if (pw != null) {
pw.println(msg);
pw.flush();
}
}
// Push through any deferrals to try meeting our condition
cancelDeferrals();
SystemClock.sleep(100);
}
}
// Used by wait-for-broadcast-idle : fast-forward all current deferrals to
// be immediately deliverable.
public void cancelDeferrals() {
synchronized (mService) {
mDispatcher.cancelDeferralsLocked();
scheduleBroadcastsLocked();
}
}
public String describeStateLocked() {
return mParallelBroadcasts.size() + " parallel; "
+ mDispatcher.describeStateLocked();
}
@NeverCompile
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
int N;
N = mParallelBroadcasts.size();
for (int i = N - 1; i >= 0; i--) {
mParallelBroadcasts.get(i).dumpDebug(proto, BroadcastQueueProto.PARALLEL_BROADCASTS);
}
mDispatcher.dumpDebug(proto, BroadcastQueueProto.ORDERED_BROADCASTS);
if (mPendingBroadcast != null) {
mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST);
}
mHistory.dumpDebug(proto);
proto.end(token);
}
@NeverCompile
public boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpConstants, boolean dumpHistory, boolean dumpAll,
String dumpPackage, boolean needSep) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
if (!mParallelBroadcasts.isEmpty() || !mDispatcher.isEmpty()
|| mPendingBroadcast != null) {
boolean printed = false;
for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
BroadcastRecord br = mParallelBroadcasts.get(i);
if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) {
continue;
}
if (!printed) {
if (needSep) {
pw.println();
}
needSep = true;
printed = true;
pw.println(" Active broadcasts [" + mQueueName + "]:");
}
pw.println(" Active Broadcast " + mQueueName + " #" + i + ":");
br.dump(pw, " ", sdf);
}
mDispatcher.dumpLocked(pw, dumpPackage, mQueueName, sdf);
if (dumpPackage == null || (mPendingBroadcast != null
&& dumpPackage.equals(mPendingBroadcast.callerPackage))) {
pw.println();
pw.println(" Pending broadcast [" + mQueueName + "]:");
if (mPendingBroadcast != null) {
mPendingBroadcast.dump(pw, " ", sdf);
} else {
pw.println(" (null)");
}
needSep = true;
}
}
if (dumpConstants) {
mConstants.dump(new IndentingPrintWriter(pw));
}
if (dumpHistory) {
needSep = mHistory.dumpLocked(pw, dumpPackage, mQueueName, sdf, dumpAll, needSep);
}
return needSep;
}
}