diff options
13 files changed, 463 insertions, 350 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4c7bc6da7fb2..80121b729779 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1390,8 +1390,9 @@ class ContextImpl extends Context { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = new LoadedApk.ReceiverDispatcher( - resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver(); + rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(), + resultReceiver, getOuterContext(), scheduler, null, false) + .getIIntentReceiver(); } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); @@ -1497,8 +1498,9 @@ class ContextImpl extends Context { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = new LoadedApk.ReceiverDispatcher(resultReceiver, getOuterContext(), - scheduler, null, false).getIIntentReceiver(); + rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(), + resultReceiver, getOuterContext(), scheduler, null, false) + .getIIntentReceiver(); } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); @@ -1616,8 +1618,9 @@ class ContextImpl extends Context { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = new LoadedApk.ReceiverDispatcher( - resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver(); + rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(), + resultReceiver, getOuterContext(), scheduler, null, false) + .getIIntentReceiver(); } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); @@ -1699,8 +1702,9 @@ class ContextImpl extends Context { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = new LoadedApk.ReceiverDispatcher( - resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver(); + rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(), + resultReceiver, getOuterContext(), scheduler, null, false) + .getIIntentReceiver(); } } String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); @@ -1802,7 +1806,7 @@ class ContextImpl extends Context { if (scheduler == null) { scheduler = mMainThread.getHandler(); } - rd = new LoadedApk.ReceiverDispatcher( + rd = new LoadedApk.ReceiverDispatcher(mMainThread.getApplicationThread(), receiver, context, scheduler, null, true).getIIntentReceiver(); } } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 0849a6f67dbc..3620a601d355 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -1598,8 +1598,8 @@ public final class LoadedApk { } } if (rd == null) { - rd = new ReceiverDispatcher(r, context, handler, - instrumentation, registered); + rd = new ReceiverDispatcher(mActivityThread.getApplicationThread(), r, context, + handler, instrumentation, registered); if (registered) { if (map == null) { map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>(); @@ -1714,6 +1714,7 @@ public final class LoadedApk { } } + final IApplicationThread mAppThread; final IIntentReceiver.Stub mIIntentReceiver; @UnsupportedAppUsage final BroadcastReceiver mReceiver; @@ -1736,7 +1737,7 @@ public final class LoadedApk { boolean ordered, boolean sticky, int sendingUser) { super(resultCode, resultData, resultExtras, mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered, - sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags()); + sticky, mAppThread.asBinder(), sendingUser, intent.getFlags()); mCurIntent = intent; mOrdered = ordered; } @@ -1811,13 +1812,14 @@ public final class LoadedApk { } } - ReceiverDispatcher(BroadcastReceiver receiver, Context context, - Handler activityThread, Instrumentation instrumentation, + ReceiverDispatcher(IApplicationThread appThread, BroadcastReceiver receiver, + Context context, Handler activityThread, Instrumentation instrumentation, boolean registered) { if (activityThread == null) { throw new NullPointerException("Handler must not be null"); } + mAppThread = appThread; mIIntentReceiver = new InnerReceiver(this, !registered); mReceiver = receiver; mContext = context; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2cf24fa9df1f..0a3323c76a50 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2412,13 +2412,13 @@ public class ActivityManagerService extends IActivityManager.Stub mBroadcastQueues = new BroadcastQueue[4]; mFgBroadcastQueue = new BroadcastQueueImpl(this, mHandler, - "foreground", foreConstants, false); + "foreground", foreConstants, false, ProcessList.SCHED_GROUP_DEFAULT); mBgBroadcastQueue = new BroadcastQueueImpl(this, mHandler, - "background", backConstants, true); + "background", backConstants, true, ProcessList.SCHED_GROUP_BACKGROUND); mBgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler, - "offload_bg", offloadConstants, true); + "offload_bg", offloadConstants, true, ProcessList.SCHED_GROUP_BACKGROUND); mFgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler, - "offload_fg", foreConstants, true); + "offload_fg", foreConstants, true, ProcessList.SCHED_GROUP_BACKGROUND); mBroadcastQueues[0] = mFgBroadcastQueue; mBroadcastQueues[1] = mBgBroadcastQueue; mBroadcastQueues[2] = mBgOffloadBroadcastQueue; @@ -3263,13 +3263,20 @@ public class ActivityManagerService extends IActivityManager.Stub if (thread == null) { return null; } + return getRecordForAppLOSP(thread.asBinder()); + } + + @GuardedBy(anyOf = {"this", "mProcLock"}) + ProcessRecord getRecordForAppLOSP(IBinder threadBinder) { + if (threadBinder == null) { + return null; + } - ProcessRecord record = mProcessList.getLRURecordForAppLOSP(thread); + ProcessRecord record = mProcessList.getLRURecordForAppLOSP(threadBinder); if (record != null) return record; // Validation: if it isn't in the LRU list, it shouldn't exist, but let's // double-check that. - final IBinder threadBinder = thread.asBinder(); final ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessList.getProcessNamesLOSP().getMap(); for (int i = pmap.size()-1; i >= 0; i--) { @@ -10649,7 +10656,7 @@ public class ActivityManagerService extends IActivityManager.Stub if (!onlyHistory && !onlyReceivers && dumpAll) { pw.println(); for (BroadcastQueue queue : mBroadcastQueues) { - pw.println(" Queue " + queue.toString() + ": " + queue.describeState()); + pw.println(" Queue " + queue.toString() + ": " + queue.describeStateLocked()); } pw.println(" mHandler:"); mHandler.dump(new PrintWriterPrinter(pw), " "); @@ -13370,7 +13377,7 @@ public class ActivityManagerService extends IActivityManager.Stub final BroadcastRecord r = rl.curBroadcast; if (r != null) { final boolean doNext = r.queue.finishReceiverLocked( - receiver.asBinder(), r.resultCode, r.resultData, r.resultExtras, + rl.app, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, false); if (doNext) { doTrim = true; @@ -14538,9 +14545,9 @@ public class ActivityManagerService extends IActivityManager.Stub } } - public void finishReceiver(IBinder who, int resultCode, String resultData, + public void finishReceiver(IBinder caller, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, int flags) { - if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + who); + if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Finish receiver: " + caller); // Refuse possible leaked file descriptors if (resultExtras != null && resultExtras.hasFileDescriptors()) { @@ -14549,12 +14556,15 @@ public class ActivityManagerService extends IActivityManager.Stub final long origId = Binder.clearCallingIdentity(); try { - boolean doNext = false; - BroadcastRecord r; - BroadcastQueue queue; synchronized(this) { - queue = broadcastQueueForFlags(flags); - doNext = queue.finishReceiverLocked(who, resultCode, + final ProcessRecord callerApp = getRecordForAppLOSP(caller); + if (callerApp == null) { + Slog.w(TAG, "finishReceiver: no app for " + caller); + return; + } + + final BroadcastQueue queue = broadcastQueueForFlags(flags); + queue.finishReceiverLocked(callerApp, resultCode, resultData, resultExtras, resultAbort, true); // updateOomAdjLocked() will be done here trimApplicationsLocked(false, OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER); @@ -15106,30 +15116,13 @@ public class ActivityManagerService extends IActivityManager.Stub // LIFETIME MANAGEMENT // ========================================================= - // Returns whether the app is receiving broadcast. - // If receiving, fetch all broadcast queues which the app is - // the current [or imminent] receiver on. - boolean isReceivingBroadcastLocked(ProcessRecord app, - ArraySet<BroadcastQueue> receivingQueues) { - final ProcessReceiverRecord prr = app.mReceivers; - final int numOfReceivers = prr.numberOfCurReceivers(); - if (numOfReceivers > 0) { - for (int i = 0; i < numOfReceivers; i++) { - receivingQueues.add(prr.getCurReceiverAt(i).queue); - } - return true; - } - - // It's not the current receiver, but it might be starting up to become one + boolean isReceivingBroadcastLocked(ProcessRecord app, int[] outSchedGroup) { + int res = ProcessList.SCHED_GROUP_UNDEFINED; for (BroadcastQueue queue : mBroadcastQueues) { - final BroadcastRecord r = queue.getPendingBroadcastLocked(); - if (r != null && r.curApp == app) { - // found it; report which queue it's in - receivingQueues.add(queue); - } + res = Math.max(res, queue.getPreferredSchedulingGroupLocked(app)); } - - return !receivingQueues.isEmpty(); + outSchedGroup[0] = res; + return res != ProcessList.SCHED_GROUP_UNDEFINED; } Association startAssociationLocked(int sourceUid, String sourceProcess, int sourceState, @@ -15237,7 +15230,7 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") final boolean canGcNowLocked() { for (BroadcastQueue q : mBroadcastQueues) { - if (!q.isIdle()) { + if (!q.isIdleLocked()) { return false; } } @@ -17814,35 +17807,15 @@ public class ActivityManagerService extends IActivityManager.Stub public void waitForBroadcastIdle(@Nullable PrintWriter pw) { enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()"); - while (true) { - boolean idle = true; - synchronized (this) { - for (BroadcastQueue queue : mBroadcastQueues) { - if (!queue.isIdle()) { - final String msg = "Waiting for queue " + queue + " to become idle..."; - if (pw != null) { - pw.println(msg); - pw.println(queue.describeState()); - pw.flush(); - } - Slog.v(TAG, msg); - queue.flush(); - idle = false; - } - } - } + for (BroadcastQueue queue : mBroadcastQueues) { + queue.waitForIdle(pw); + } + } - if (idle) { - final String msg = "All broadcast queues are idle!"; - if (pw != null) { - pw.println(msg); - pw.flush(); - } - Slog.v(TAG, msg); - return; - } else { - SystemClock.sleep(1000); - } + public void waitForBroadcastBarrier(@Nullable PrintWriter pw) { + enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()"); + for (BroadcastQueue queue : mBroadcastQueues) { + queue.waitForBarrier(pw); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index a42b2a48f886..b4f6e35b3df3 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -341,6 +341,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runNoHomeScreen(pw); case "wait-for-broadcast-idle": return runWaitForBroadcastIdle(pw); + case "wait-for-broadcast-barrier": + return runWaitForBroadcastBarrier(pw); case "compat": return runCompat(pw); case "refresh-settings-cache": @@ -3112,6 +3114,11 @@ final class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runWaitForBroadcastBarrier(PrintWriter pw) throws RemoteException { + mInternal.waitForBroadcastBarrier(pw); + return 0; + } + int runRefreshSettingsCache() throws RemoteException { mInternal.refreshSettingsCache(); return 0; diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java new file mode 100644 index 000000000000..d820d6c802b3 --- /dev/null +++ b/services/core/java/com/android/server/am/BroadcastHistory.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2022 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 android.app.ActivityManager; +import android.content.Intent; +import android.os.Bundle; +import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; + +import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Collection of recent historical broadcasts that are available to be dumped + * for debugging purposes. Automatically trims itself over time. + */ +public class BroadcastHistory { + static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50; + static final int MAX_BROADCAST_SUMMARY_HISTORY + = ActivityManager.isLowRamDeviceStatic() ? 25 : 300; + + /** + * Historical data of past broadcasts, for debugging. This is a ring buffer + * whose last element is at mHistoryNext. + */ + final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY]; + int mHistoryNext = 0; + + /** + * Summary of historical data of past broadcasts, for debugging. This is a + * ring buffer whose last element is at mSummaryHistoryNext. + */ + final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY]; + int mSummaryHistoryNext = 0; + + /** + * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring + * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall + * clock time, not elapsed. + */ + final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; + final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; + final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; + + public void addBroadcastToHistoryLocked(BroadcastRecord original) { + // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords, + // So don't change the incoming record directly. + final BroadcastRecord historyRecord = original.maybeStripForHistory(); + + mBroadcastHistory[mHistoryNext] = historyRecord; + mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY); + + mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent; + mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime; + mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime; + mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis(); + mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY); + } + + private final int ringAdvance(int x, final int increment, final int ringSize) { + x += increment; + if (x < 0) return (ringSize - 1); + else if (x >= ringSize) return 0; + else return x; + } + + public void dumpDebug(ProtoOutputStream proto) { + int lastIndex = mHistoryNext; + int ringIndex = lastIndex; + do { + // increasing index = more recent entry, and we want to print the most + // recent first and work backwards, so we roll through the ring backwards. + ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY); + BroadcastRecord r = mBroadcastHistory[ringIndex]; + if (r != null) { + r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS); + } + } while (ringIndex != lastIndex); + + lastIndex = ringIndex = mSummaryHistoryNext; + do { + ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); + Intent intent = mBroadcastSummaryHistory[ringIndex]; + if (intent == null) { + continue; + } + long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY); + intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT, + false, true, true, false); + proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS, + mSummaryHistoryEnqueueTime[ringIndex]); + proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS, + mSummaryHistoryDispatchTime[ringIndex]); + proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS, + mSummaryHistoryFinishTime[ringIndex]); + proto.end(summaryToken); + } while (ringIndex != lastIndex); + } + + public boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName, + SimpleDateFormat sdf, boolean dumpAll, boolean needSep) { + int i; + boolean printed = false; + + i = -1; + int lastIndex = mHistoryNext; + int ringIndex = lastIndex; + do { + // increasing index = more recent entry, and we want to print the most + // recent first and work backwards, so we roll through the ring backwards. + ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY); + BroadcastRecord r = mBroadcastHistory[ringIndex]; + if (r == null) { + continue; + } + + i++; // genuine record of some sort even if we're filtering it out + if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Historical broadcasts [" + queueName + "]:"); + printed = true; + } + if (dumpAll) { + pw.print(" Historical Broadcast " + queueName + " #"); + pw.print(i); pw.println(":"); + r.dump(pw, " ", sdf); + } else { + pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); + pw.print(" "); + pw.println(r.intent.toShortString(false, true, true, false)); + if (r.targetComp != null && r.targetComp != r.intent.getComponent()) { + pw.print(" targetComp: "); pw.println(r.targetComp.toShortString()); + } + Bundle bundle = r.intent.getExtras(); + if (bundle != null) { + pw.print(" extras: "); pw.println(bundle.toString()); + } + } + } while (ringIndex != lastIndex); + + if (dumpPackage == null) { + lastIndex = ringIndex = mSummaryHistoryNext; + if (dumpAll) { + printed = false; + i = -1; + } else { + // roll over the 'i' full dumps that have already been issued + for (int j = i; + j > 0 && ringIndex != lastIndex;) { + ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); + BroadcastRecord r = mBroadcastHistory[ringIndex]; + if (r == null) { + continue; + } + j--; + } + } + // done skipping; dump the remainder of the ring. 'i' is still the ordinal within + // the overall broadcast history. + do { + ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); + Intent intent = mBroadcastSummaryHistory[ringIndex]; + if (intent == null) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Historical broadcasts summary [" + queueName + "]:"); + printed = true; + } + if (!dumpAll && i >= 50) { + pw.println(" ..."); + break; + } + i++; + pw.print(" #"); pw.print(i); pw.print(": "); + pw.println(intent.toShortString(false, true, true, false)); + pw.print(" "); + TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex] + - mSummaryHistoryEnqueueTime[ringIndex], pw); + pw.print(" dispatch "); + TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex] + - mSummaryHistoryDispatchTime[ringIndex], pw); + pw.println(" finish"); + pw.print(" enq="); + pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex]))); + pw.print(" disp="); + pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex]))); + pw.print(" fin="); + pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex]))); + Bundle bundle = intent.getExtras(); + if (bundle != null) { + pw.print(" extras: "); pw.println(bundle.toString()); + } + } while (ringIndex != lastIndex); + } + return needSep; + } +} diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 752c6b683e05..6814509922b1 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -18,12 +18,10 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; @@ -43,16 +41,18 @@ public abstract class BroadcastQueue { final @NonNull Handler mHandler; final @NonNull BroadcastConstants mConstants; final @NonNull BroadcastSkipPolicy mSkipPolicy; + final @NonNull BroadcastHistory mHistory; final @NonNull String mQueueName; BroadcastQueue(@NonNull ActivityManagerService service, @NonNull Handler handler, @NonNull String name, @NonNull BroadcastConstants constants, - @NonNull BroadcastSkipPolicy skipPolicy) { + @NonNull BroadcastSkipPolicy skipPolicy, @NonNull BroadcastHistory history) { mService = Objects.requireNonNull(service); mHandler = Objects.requireNonNull(handler); mQueueName = Objects.requireNonNull(name); mConstants = Objects.requireNonNull(constants); mSkipPolicy = Objects.requireNonNull(skipPolicy); + mHistory = Objects.requireNonNull(history); } void start(@NonNull ContentResolver resolver) { @@ -66,11 +66,15 @@ public abstract class BroadcastQueue { public abstract boolean isDelayBehindServices(); - @GuardedBy("mService") - public abstract @Nullable BroadcastRecord getPendingBroadcastLocked(); - - @GuardedBy("mService") - public abstract @Nullable BroadcastRecord getActiveBroadcastLocked(); + /** + * Return the preferred scheduling group for the given process, typically + * influenced by a broadcast being actively dispatched. + * + * @return scheduling group such as {@link ProcessList#SCHED_GROUP_DEFAULT}, + * otherwise {@link ProcessList#SCHED_GROUP_UNDEFINED} if this queue + * has no opinion. + */ + public abstract int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app); /** * Enqueue the given broadcast to be eventually dispatched. @@ -85,17 +89,14 @@ public abstract class BroadcastQueue { public abstract void enqueueBroadcastLocked(@NonNull BroadcastRecord r); /** - * Signal delivered back from a {@link BroadcastReceiver} to indicate that - * it's finished processing the current broadcast being dispatched to it. + * Signal delivered back from the given process to indicate that it's + * finished processing the current broadcast being dispatched to it. * <p> * If this signal isn't delivered back in a timely fashion, we assume the * receiver has somehow wedged and we trigger an ANR. - * - * @param receiver the value to match against - * {@link BroadcastRecord#receiver} to identify the caller. */ @GuardedBy("mService") - public abstract boolean finishReceiverLocked(@NonNull IBinder receiver, int resultCode, + public abstract boolean finishReceiverLocked(@NonNull ProcessRecord app, int resultCode, @Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort, boolean waitForServices); @@ -144,22 +145,40 @@ public abstract class BroadcastQueue { * Quickly determine if this queue has broadcasts that are still waiting to * be delivered at some point in the future. * - * @see #flush() + * @see #waitForIdle + * @see #waitForBarrier */ - public abstract boolean isIdle(); + @GuardedBy("mService") + public abstract boolean isIdleLocked(); /** - * Brief summary of internal state, useful for debugging purposes. + * Wait until this queue becomes completely idle. + * <p> + * Any broadcasts waiting to be delivered at some point in the future will + * be dispatched as quickly as possible. + * <p> + * Callers are cautioned that the queue may take a long time to go idle, + * since running apps can continue sending new broadcasts in perpetuity; + * consider using {@link #waitForBarrier} instead. */ - public abstract @NonNull String describeState(); + public abstract void waitForIdle(@Nullable PrintWriter pw); /** - * Flush any broadcasts still waiting to be delivered, causing them to be - * delivered as soon as possible. - * - * @see #isIdle() + * Wait until any currently waiting broadcasts have been dispatched. + * <p> + * Any broadcasts waiting to be delivered at some point in the future will + * be dispatched as quickly as possible. + * <p> + * Callers are advised that this method will <em>not</em> wait for any + * future broadcasts that are newly enqueued after being invoked. */ - public abstract void flush(); + public abstract void waitForBarrier(@Nullable PrintWriter pw); + + /** + * Brief summary of internal state, useful for debugging purposes. + */ + @GuardedBy("mService") + public abstract @NonNull String describeStateLocked(); public abstract void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId); diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java index d9146121fc27..56112cfbc3e9 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java @@ -73,7 +73,6 @@ import android.text.TextUtils; import android.util.EventLog; import android.util.Slog; import android.util.SparseIntArray; -import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.os.TimeoutRecord; @@ -85,8 +84,8 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.Set; +import java.util.function.BooleanSupplier; /** * BROADCASTS @@ -99,16 +98,14 @@ public class BroadcastQueueImpl extends BroadcastQueue { private static final String TAG_MU = TAG + POSTFIX_MU; private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST; - static final int MAX_BROADCAST_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 10 : 50; - static final int MAX_BROADCAST_SUMMARY_HISTORY - = ActivityManager.isLowRamDeviceStatic() ? 25 : 300; - /** * 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 @@ -133,29 +130,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { private int mNextToken = 0; /** - * Historical data of past broadcasts, for debugging. This is a ring buffer - * whose last element is at mHistoryNext. - */ - final BroadcastRecord[] mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY]; - int mHistoryNext = 0; - - /** - * Summary of historical data of past broadcasts, for debugging. This is a - * ring buffer whose last element is at mSummaryHistoryNext. - */ - final Intent[] mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY]; - int mSummaryHistoryNext = 0; - - /** - * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring - * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall - * clock time, not elapsed. - */ - final long[] mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; - final long[] mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; - final long[] mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; - - /** * Set when we current have a BROADCAST_INTENT_MSG in flight. */ boolean mBroadcastsScheduled = false; @@ -211,17 +185,19 @@ public class BroadcastQueueImpl extends BroadcastQueue { } BroadcastQueueImpl(ActivityManagerService service, Handler handler, - String name, BroadcastConstants constants, boolean allowDelayBehindServices) { + String name, BroadcastConstants constants, boolean allowDelayBehindServices, + int schedGroup) { this(service, handler, name, constants, new BroadcastSkipPolicy(service), - allowDelayBehindServices); + new BroadcastHistory(), allowDelayBehindServices, schedGroup); } BroadcastQueueImpl(ActivityManagerService service, Handler handler, String name, BroadcastConstants constants, BroadcastSkipPolicy skipPolicy, - boolean allowDelayBehindServices) { - super(service, handler, name, constants, skipPolicy); + BroadcastHistory history, boolean allowDelayBehindServices, int schedGroup) { + super(service, handler, name, constants, skipPolicy, history); mHandler = new BroadcastHandler(handler.getLooper()); mDelayBehindServices = allowDelayBehindServices; + mSchedGroup = schedGroup; mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService); } @@ -242,6 +218,18 @@ public class BroadcastQueueImpl extends BroadcastQueue { 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) { final boolean replacePending = (r.intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0; @@ -369,7 +357,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { return; } - r.receiver = thread.asBinder(); r.curApp = app; final ProcessReceiverRecord prr = app.mReceivers; prr.addCurReceiver(r); @@ -407,7 +394,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { if (!started) { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Process cur broadcast " + r + ": NOT STARTED!"); - r.receiver = null; r.curApp = null; prr.removeCurReceiver(r); } @@ -524,16 +510,16 @@ public class BroadcastQueueImpl extends BroadcastQueue { mBroadcastsScheduled = true; } - public BroadcastRecord getMatchingOrderedReceiver(IBinder receiver) { + 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.receiver != receiver) { + if (br.curApp != app) { Slog.w(TAG_BROADCAST, "getMatchingOrderedReceiver [" + mQueueName - + "] active broadcast " + br.receiver + " doesn't match " + receiver); + + "] active broadcast " + br.curApp + " doesn't match " + app); return null; } return br; @@ -564,9 +550,9 @@ public class BroadcastQueueImpl extends BroadcastQueue { }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT)); } - public boolean finishReceiverLocked(IBinder receiver, int resultCode, + public boolean finishReceiverLocked(ProcessRecord app, int resultCode, String resultData, Bundle resultExtras, boolean resultAbort, boolean waitForServices) { - final BroadcastRecord r = getMatchingOrderedReceiver(receiver); + final BroadcastRecord r = getMatchingOrderedReceiver(app); if (r != null) { return finishReceiverLocked(r, resultCode, resultData, resultExtras, resultAbort, waitForServices); @@ -647,7 +633,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { } } - r.receiver = null; r.intent.setComponent(null); if (r.curApp != null && r.curApp.mReceivers.hasCurReceiver(r)) { r.curApp.mReceivers.removeCurReceiver(r); @@ -674,7 +659,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { // 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() - && r.queue.getActiveBroadcastLocked() == r) { + && ((BroadcastQueueImpl) r.queue).getActiveBroadcastLocked() == r) { ActivityInfo nextReceiver; if (r.nextReceiver < r.receivers.size()) { Object obj = r.receivers.get(r.nextReceiver); @@ -805,7 +790,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { // don't want to touch the fields that keep track of the current // state of ordered broadcasts. if (ordered) { - r.receiver = filter.receiverList.receiver.asBinder(); r.curFilter = filter; filter.receiverList.curBroadcast = r; r.state = BroadcastRecord.CALL_IN_RECEIVE; @@ -869,7 +853,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { } // And BroadcastRecord state related to ordered delivery, if appropriate if (ordered) { - r.receiver = null; r.curFilter = null; filter.receiverList.curBroadcast = null; } @@ -1289,12 +1272,13 @@ public class BroadcastQueueImpl extends BroadcastQueue { + filter + ": " + r); r.mIsReceiverAppRunning = true; deliverToRegisteredReceiverLocked(r, filter, r.ordered, recIdx); - if (r.receiver == null || !r.ordered) { + 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 + " receiver=" + r.receiver); + + mQueueName + "]: ordered=" + r.ordered + + " curFilter=" + r.curFilter + + " curReceiver=" + r.curReceiver); r.state = BroadcastRecord.IDLE; scheduleBroadcastsLocked(); } else { @@ -1346,7 +1330,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { "Skipping delivery of ordered [" + mQueueName + "] " + r + " for reason described above"); r.delivery[recIdx] = BroadcastRecord.DELIVERY_SKIPPED; - r.receiver = null; r.curFilter = null; r.state = BroadcastRecord.IDLE; r.manifestSkipCount++; @@ -1636,8 +1619,8 @@ public class BroadcastQueueImpl extends BroadcastQueue { final boolean debugging = (r.curApp != null && r.curApp.isDebugging()); long timeoutDurationMs = now - r.receiverTime; - Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver - + ", started " + timeoutDurationMs + "ms ago"); + Slog.w(TAG, "Timeout of broadcast " + r + " - curFilter=" + r.curFilter + " curReceiver=" + + r.curReceiver + ", started " + timeoutDurationMs + "ms ago"); r.receiverTime = now; if (!debugging) { r.anrCount++; @@ -1689,13 +1672,6 @@ public class BroadcastQueueImpl extends BroadcastQueue { } } - private final int ringAdvance(int x, final int increment, final int ringSize) { - x += increment; - if (x < 0) return (ringSize - 1); - else if (x >= ringSize) return 0; - else return x; - } - private final void addBroadcastToHistoryLocked(BroadcastRecord original) { if (original.callingUid < 0) { // This was from a registerReceiver() call; ignore it. @@ -1716,18 +1692,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { original.callingUid, 0, callerPackage).sendToTarget(); } - // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords, - // So don't change the incoming record directly. - final BroadcastRecord historyRecord = original.maybeStripForHistory(); - - mBroadcastHistory[mHistoryNext] = historyRecord; - mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY); - - mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent; - mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime; - mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime; - mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis(); - mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY); + mHistory.addBroadcastToHistoryLocked(original); } public boolean cleanupDisabledPackageReceiversLocked( @@ -1781,13 +1746,72 @@ public class BroadcastQueueImpl extends BroadcastQueue { record.intent == null ? "" : record.intent.getAction()); } - public boolean isIdle() { + public boolean isIdleLocked() { return mParallelBroadcasts.isEmpty() && mDispatcher.isIdle() && (mPendingBroadcast == null); } - public void flush() { - cancelDeferrals(); + public boolean isBeyondBarrierLocked(long barrierTime) { + // If nothing active, we're beyond barrier + if (isIdleLocked()) return true; + + // Check if active broadcast is beyond barrier + final BroadcastRecord active = getActiveBroadcastLocked(); + if (active != null && active.enqueueTime > barrierTime) { + return true; + } + + // Check if pending broadcast is beyond barrier + final BroadcastRecord pending = getPendingBroadcastLocked(); + if (pending != null && pending.enqueueTime > barrierTime) { + return true; + } + + return false; + } + + public void waitForIdle(PrintWriter pw) { + waitFor(() -> isIdleLocked(), pw, "idle"); + } + + public void waitForBarrier(PrintWriter pw) { + final long barrierTime = SystemClock.uptimeMillis(); + waitFor(() -> isBeyondBarrierLocked(barrierTime), pw, "barrier"); + } + + 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 @@ -1799,11 +1823,9 @@ public class BroadcastQueueImpl extends BroadcastQueue { } } - public String describeState() { - synchronized (mService) { - return mParallelBroadcasts.size() + " parallel; " - + mDispatcher.describeStateLocked(); - } + public String describeStateLocked() { + return mParallelBroadcasts.size() + " parallel; " + + mDispatcher.describeStateLocked(); } public void dumpDebug(ProtoOutputStream proto, long fieldId) { @@ -1818,37 +1840,7 @@ public class BroadcastQueueImpl extends BroadcastQueue { if (mPendingBroadcast != null) { mPendingBroadcast.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCAST); } - - int lastIndex = mHistoryNext; - int ringIndex = lastIndex; - do { - // increasing index = more recent entry, and we want to print the most - // recent first and work backwards, so we roll through the ring backwards. - ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY); - BroadcastRecord r = mBroadcastHistory[ringIndex]; - if (r != null) { - r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS); - } - } while (ringIndex != lastIndex); - - lastIndex = ringIndex = mSummaryHistoryNext; - do { - ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); - Intent intent = mBroadcastSummaryHistory[ringIndex]; - if (intent == null) { - continue; - } - long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY); - intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT, - false, true, true, false); - proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS, - mSummaryHistoryEnqueueTime[ringIndex]); - proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS, - mSummaryHistoryDispatchTime[ringIndex]); - proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS, - mSummaryHistoryFinishTime[ringIndex]); - proto.end(summaryToken); - } while (ringIndex != lastIndex); + mHistory.dumpDebug(proto); proto.end(token); } @@ -1889,114 +1881,8 @@ public class BroadcastQueueImpl extends BroadcastQueue { needSep = true; } } - mConstants.dump(pw); - - int i; - boolean printed = false; - - i = -1; - int lastIndex = mHistoryNext; - int ringIndex = lastIndex; - do { - // increasing index = more recent entry, and we want to print the most - // recent first and work backwards, so we roll through the ring backwards. - ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY); - BroadcastRecord r = mBroadcastHistory[ringIndex]; - if (r == null) { - continue; - } - - i++; // genuine record of some sort even if we're filtering it out - if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) { - continue; - } - if (!printed) { - if (needSep) { - pw.println(); - } - needSep = true; - pw.println(" Historical broadcasts [" + mQueueName + "]:"); - printed = true; - } - if (dumpAll) { - pw.print(" Historical Broadcast " + mQueueName + " #"); - pw.print(i); pw.println(":"); - r.dump(pw, " ", sdf); - } else { - pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); - pw.print(" "); - pw.println(r.intent.toShortString(false, true, true, false)); - if (r.targetComp != null && r.targetComp != r.intent.getComponent()) { - pw.print(" targetComp: "); pw.println(r.targetComp.toShortString()); - } - Bundle bundle = r.intent.getExtras(); - if (bundle != null) { - pw.print(" extras: "); pw.println(bundle.toString()); - } - } - } while (ringIndex != lastIndex); - - if (dumpPackage == null) { - lastIndex = ringIndex = mSummaryHistoryNext; - if (dumpAll) { - printed = false; - i = -1; - } else { - // roll over the 'i' full dumps that have already been issued - for (int j = i; - j > 0 && ringIndex != lastIndex;) { - ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); - BroadcastRecord r = mBroadcastHistory[ringIndex]; - if (r == null) { - continue; - } - j--; - } - } - // done skipping; dump the remainder of the ring. 'i' is still the ordinal within - // the overall broadcast history. - do { - ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); - Intent intent = mBroadcastSummaryHistory[ringIndex]; - if (intent == null) { - continue; - } - if (!printed) { - if (needSep) { - pw.println(); - } - needSep = true; - pw.println(" Historical broadcasts summary [" + mQueueName + "]:"); - printed = true; - } - if (!dumpAll && i >= 50) { - pw.println(" ..."); - break; - } - i++; - pw.print(" #"); pw.print(i); pw.print(": "); - pw.println(intent.toShortString(false, true, true, false)); - pw.print(" "); - TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex] - - mSummaryHistoryEnqueueTime[ringIndex], pw); - pw.print(" dispatch "); - TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex] - - mSummaryHistoryDispatchTime[ringIndex], pw); - pw.println(" finish"); - pw.print(" enq="); - pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex]))); - pw.print(" disp="); - pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex]))); - pw.print(" fin="); - pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex]))); - Bundle bundle = intent.getExtras(); - if (bundle != null) { - pw.print(" extras: "); pw.println(bundle.toString()); - } - } while (ringIndex != lastIndex); - } - + needSep = mHistory.dumpLocked(pw, dumpPackage, mQueueName, sdf, dumpAll, needSep); return needSep; } } diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 18fbfdeb832c..817831cb003b 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -103,7 +103,6 @@ final class BroadcastRecord extends Binder { Bundle resultExtras; // current result extra data values. boolean resultAbort; // current result abortBroadcast value. int nextReceiver; // next receiver to be executed. - IBinder receiver; // who is currently running, null if none. int state; int anrCount; // has this broadcast record hit any ANRs? int manifestCount; // number of manifest receivers dispatched. @@ -133,15 +132,10 @@ final class BroadcastRecord extends Binder { static final int DELIVERY_SKIPPED = 2; static final int DELIVERY_TIMEOUT = 3; - // The following are set when we are calling a receiver (one that - // was found in our list of registered receivers). - BroadcastFilter curFilter; - - // The following are set only when we are launching a receiver (one - // that was found by querying the package manager). ProcessRecord curApp; // hosting application of current receiver. ComponentName curComponent; // the receiver class that is currently running. - ActivityInfo curReceiver; // info about the receiver that is currently running. + ActivityInfo curReceiver; // the manifest receiver that is currently running. + BroadcastFilter curFilter; // the registered receiver currently running. Bundle curFilteredExtras; // the bundle that has been filtered by the package visibility rules boolean mIsReceiverAppRunning; // Was the receiver's app already running. @@ -217,9 +211,8 @@ final class BroadcastRecord extends Binder { pw.print(" sticky="); pw.print(sticky); pw.print(" initialSticky="); pw.println(initialSticky); } - if (nextReceiver != 0 || receiver != null) { + if (nextReceiver != 0) { pw.print(prefix); pw.print("nextReceiver="); pw.print(nextReceiver); - pw.print(" receiver="); pw.println(receiver); } if (curFilter != null) { pw.print(prefix); pw.print("curFilter="); pw.println(curFilter); @@ -368,7 +361,6 @@ final class BroadcastRecord extends Binder { resultExtras = from.resultExtras; resultAbort = from.resultAbort; nextReceiver = from.nextReceiver; - receiver = from.receiver; state = from.state; anrCount = from.anrCount; manifestCount = from.manifestCount; diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 12aa66b84d85..80dd2669a9a9 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -305,7 +305,7 @@ public class OomAdjuster { */ private final Handler mProcessGroupHandler; - private final ArraySet<BroadcastQueue> mTmpBroadcastQueue = new ArraySet(); + private final int[] mTmpSchedGroup = new int[1]; private final ActivityManagerService mService; private final ProcessList mProcessList; @@ -1677,14 +1677,13 @@ public class OomAdjuster { if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making instrumentation: " + app); } - } else if (state.getCachedIsReceivingBroadcast(mTmpBroadcastQueue)) { + } else if (state.getCachedIsReceivingBroadcast(mTmpSchedGroup)) { // An app that is currently receiving a broadcast also // counts as being in the foreground for OOM killer purposes. // It's placed in a sched group based on the nature of the // broadcast as reflected by which queue it's active in. adj = ProcessList.FOREGROUND_APP_ADJ; - schedGroup = (mTmpBroadcastQueue.contains(mService.mFgBroadcastQueue)) - ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; + schedGroup = mTmpSchedGroup[0]; state.setAdjType("broadcast"); procState = ActivityManager.PROCESS_STATE_RECEIVER; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 90719a3f83ea..affb084708eb 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -276,6 +276,8 @@ public final class ProcessList { // Memory pages are 4K. static final int PAGE_SIZE = 4 * 1024; + // Activity manager's version of an undefined schedule group + static final int SCHED_GROUP_UNDEFINED = Integer.MIN_VALUE; // Activity manager's version of Process.THREAD_GROUP_BACKGROUND static final int SCHED_GROUP_BACKGROUND = 0; // Activity manager's version of Process.THREAD_GROUP_RESTRICTED @@ -3642,7 +3644,14 @@ public final class ProcessList { if (thread == null) { return null; } - final IBinder threadBinder = thread.asBinder(); + return getLRURecordForAppLOSP(thread.asBinder()); + } + + @GuardedBy(anyOf = {"mService", "mProcLock"}) + ProcessRecord getLRURecordForAppLOSP(IBinder threadBinder) { + if (threadBinder == null) { + return null; + } // Find the application record. for (int i = mLruProcesses.size() - 1; i >= 0; i--) { final ProcessRecord rec = mLruProcesses.get(i); diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index eb1fd3aa49be..ef137787905f 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -30,7 +30,6 @@ import android.annotation.ElapsedRealtimeLong; import android.app.ActivityManager; import android.content.ComponentName; import android.os.SystemClock; -import android.util.ArraySet; import android.util.Slog; import android.util.TimeUtils; @@ -1071,14 +1070,12 @@ final class ProcessStateRecord { } @GuardedBy("mService") - boolean getCachedIsReceivingBroadcast(ArraySet<BroadcastQueue> tmpQueue) { + boolean getCachedIsReceivingBroadcast(int[] outSchedGroup) { if (mCachedIsReceivingBroadcast == VALUE_INVALID) { - tmpQueue.clear(); - mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(mApp, tmpQueue) + mCachedIsReceivingBroadcast = mService.isReceivingBroadcastLocked(mApp, outSchedGroup) ? VALUE_TRUE : VALUE_FALSE; if (mCachedIsReceivingBroadcast == VALUE_TRUE) { - mCachedSchedGroup = tmpQueue.contains(mService.mFgBroadcastQueue) - ? ProcessList.SCHED_GROUP_DEFAULT : ProcessList.SCHED_GROUP_BACKGROUND; + mCachedSchedGroup = outSchedGroup[0]; mApp.mProfile.addHostingComponentType(HOSTING_COMPONENT_TYPE_BROADCAST_RECEIVER); } else { mApp.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_BROADCAST_RECEIVER); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java index c00c707b2c66..9bdc93e11b6a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java @@ -168,10 +168,15 @@ public class BroadcastQueueTest { return false; } }; + final BroadcastHistory emptyHistory = new BroadcastHistory() { + public void addBroadcastToHistoryLocked(BroadcastRecord original) { + } + }; if (mImpl == Impl.DEFAULT) { mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG, - constants, emptySkipPolicy, false); + constants, emptySkipPolicy, emptyHistory, false, + ProcessList.SCHED_GROUP_DEFAULT); } else { throw new UnsupportedOperationException(); } @@ -225,7 +230,7 @@ public class BroadcastQueueTest { Log.v(TAG, "Intercepting scheduleReceiver() for " + Arrays.toString(invocation.getArguments())); mHandlerThread.getThreadHandler().post(() -> { - mQueue.finishReceiverLocked(threadBinder, Activity.RESULT_OK, + mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null, null, false, false); }); return null; @@ -236,7 +241,7 @@ public class BroadcastQueueTest { Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for " + Arrays.toString(invocation.getArguments())); mHandlerThread.getThreadHandler().post(() -> { - mQueue.finishReceiverLocked(receiverBinder, Activity.RESULT_OK, null, null, + mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null, null, false, false); }); return null; @@ -298,11 +303,7 @@ public class BroadcastQueueTest { } private void waitForIdle() throws Exception { - for (int i = 0; i < 100; i++) { - if (mQueue.isIdle()) break; - SystemClock.sleep(100); - } - assertTrue(mQueue.isIdle()); + mQueue.waitForIdle(null); } private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception { diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 598c6b0d9ab5..1c345484bd41 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -317,11 +317,11 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), - any(ArraySet.class)); + any(int[].class)); sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); sService.mOomAdjuster.updateOomAdjLocked(app, OomAdjuster.OOM_ADJ_REASON_NONE); doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), - any(ArraySet.class)); + any(int[].class)); assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); } |