Merge changes from topic "oct26"

* changes:
  Enable "modern" BroadcastQueue by default; take two.
  BroadcastQueue: make "oneway" calls non-blocking.
  BroadcastQueue: only OOM adjust for manifest.
This commit is contained in:
Jeff Sharkey
2022-10-26 15:54:12 +00:00
committed by Android (Google) Code Review
6 changed files with 192 additions and 35 deletions

View File

@@ -85,9 +85,16 @@ class BroadcastProcessQueue {
@Nullable ProcessRecord app;
/**
* Track name to use for {@link Trace} events.
* Track name to use for {@link Trace} events, defined as part of upgrading
* into a running slot.
*/
@Nullable String traceTrackName;
@Nullable String runningTraceTrackName;
/**
* Flag indicating if this process should be OOM adjusted, defined as part
* of upgrading into a running slot.
*/
boolean runningOomAdjusted;
/**
* Snapshotted value of {@link ProcessRecord#getCpuDelayTime()}, typically
@@ -141,7 +148,8 @@ class BroadcastProcessQueue {
private boolean mActiveViaColdStart;
/**
* Count of {@link #mPending} broadcasts of these various flavors.
* Count of {@link #mPending} and {@link #mPendingUrgent} broadcasts of
* these various flavors.
*/
private int mCountForeground;
private int mCountOrdered;
@@ -150,6 +158,7 @@ class BroadcastProcessQueue {
private int mCountInteractive;
private int mCountResultTo;
private int mCountInstrumented;
private int mCountManifest;
private @UptimeMillisLong long mRunnableAt = Long.MAX_VALUE;
private @Reason int mRunnableAtReason = REASON_EMPTY;
@@ -206,7 +215,7 @@ class BroadcastProcessQueue {
// with implicit responsiveness expectations.
final ArrayDeque<SomeArgs> queue = record.isUrgent() ? mPendingUrgent : mPending;
queue.addLast(newBroadcastArgs);
onBroadcastEnqueued(record);
onBroadcastEnqueued(record, recordIndex);
}
/**
@@ -224,7 +233,8 @@ class BroadcastProcessQueue {
while (it.hasNext()) {
final SomeArgs args = it.next();
final BroadcastRecord testRecord = (BroadcastRecord) args.arg1;
final Object testReceiver = testRecord.receivers.get(args.argi1);
final int testRecordIndex = args.argi1;
final Object testReceiver = testRecord.receivers.get(testRecordIndex);
if ((record.callingUid == testRecord.callingUid)
&& (record.userId == testRecord.userId)
&& record.intent.filterEquals(testRecord.intent)
@@ -233,8 +243,8 @@ class BroadcastProcessQueue {
args.arg1 = record;
args.argi1 = recordIndex;
args.argi2 = blockedUntilTerminalCount;
onBroadcastDequeued(testRecord);
onBroadcastEnqueued(record);
onBroadcastDequeued(testRecord, testRecordIndex);
onBroadcastEnqueued(record, recordIndex);
return true;
}
}
@@ -284,13 +294,13 @@ class BroadcastProcessQueue {
while (it.hasNext()) {
final SomeArgs args = it.next();
final BroadcastRecord record = (BroadcastRecord) args.arg1;
final int index = args.argi1;
if (predicate.test(record, index)) {
consumer.accept(record, index);
final int recordIndex = args.argi1;
if (predicate.test(record, recordIndex)) {
consumer.accept(record, recordIndex);
if (andRemove) {
args.recycle();
it.remove();
onBroadcastDequeued(record);
onBroadcastDequeued(record, recordIndex);
}
didSomething = true;
}
@@ -339,7 +349,7 @@ class BroadcastProcessQueue {
* Return if we know of an actively running "warm" process for this queue.
*/
public boolean isProcessWarm() {
return (app != null) && (app.getThread() != null) && !app.isKilled();
return (app != null) && (app.getOnewayThread() != null) && !app.isKilled();
}
public int getPreferredSchedulingGroupLocked() {
@@ -385,7 +395,7 @@ class BroadcastProcessQueue {
mActiveCountSinceIdle++;
mActiveViaColdStart = false;
next.recycle();
onBroadcastDequeued(mActive);
onBroadcastDequeued(mActive, mActiveIndex);
}
/**
@@ -403,7 +413,7 @@ class BroadcastProcessQueue {
/**
* Update summary statistics when the given record has been enqueued.
*/
private void onBroadcastEnqueued(@NonNull BroadcastRecord record) {
private void onBroadcastEnqueued(@NonNull BroadcastRecord record, int recordIndex) {
if (record.isForeground()) {
mCountForeground++;
}
@@ -425,13 +435,16 @@ class BroadcastProcessQueue {
if (record.callerInstrumented) {
mCountInstrumented++;
}
if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
mCountManifest++;
}
invalidateRunnableAt();
}
/**
* Update summary statistics when the given record has been dequeued.
*/
private void onBroadcastDequeued(@NonNull BroadcastRecord record) {
private void onBroadcastDequeued(@NonNull BroadcastRecord record, int recordIndex) {
if (record.isForeground()) {
mCountForeground--;
}
@@ -453,34 +466,37 @@ class BroadcastProcessQueue {
if (record.callerInstrumented) {
mCountInstrumented--;
}
if (record.receivers.get(recordIndex) instanceof ResolveInfo) {
mCountManifest--;
}
invalidateRunnableAt();
}
public void traceProcessStartingBegin() {
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
traceTrackName, toShortString() + " starting", hashCode());
runningTraceTrackName, toShortString() + " starting", hashCode());
}
public void traceProcessRunningBegin() {
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
traceTrackName, toShortString() + " running", hashCode());
runningTraceTrackName, toShortString() + " running", hashCode());
}
public void traceProcessEnd() {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
traceTrackName, hashCode());
runningTraceTrackName, hashCode());
}
public void traceActiveBegin() {
final int cookie = mActive.receivers.get(mActiveIndex).hashCode();
Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
traceTrackName, mActive.toShortString() + " scheduled", cookie);
runningTraceTrackName, mActive.toShortString() + " scheduled", cookie);
}
public void traceActiveEnd() {
final int cookie = mActive.receivers.get(mActiveIndex).hashCode();
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
traceTrackName, cookie);
runningTraceTrackName, cookie);
}
/**
@@ -539,6 +555,14 @@ class BroadcastProcessQueue {
return (queue != null) ? (BroadcastRecord) queue.peekFirst().arg1 : null;
}
/**
* Quickly determine if this queue has broadcasts waiting to be delivered to
* manifest receivers, which indicates we should request an OOM adjust.
*/
public boolean isPendingManifest() {
return mCountManifest > 0;
}
/**
* Quickly determine if this queue has broadcasts that are still waiting to
* be delivered at some point in the future.

View File

@@ -401,7 +401,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
mRunnableHead = removeFromRunnableList(mRunnableHead, queue);
// Emit all trace events for this process into a consistent track
queue.traceTrackName = TAG + ".mRunning[" + queueIndex + "]";
queue.runningTraceTrackName = TAG + ".mRunning[" + queueIndex + "]";
queue.runningOomAdjusted = queue.isPendingManifest();
// If we're already warm, schedule next pending broadcast now;
// otherwise we'll wait for the cold start to circle back around
@@ -415,9 +416,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
scheduleReceiverColdLocked(queue);
}
// We've moved at least one process into running state above, so we
// need to kick off an OOM adjustment pass
updateOomAdj = true;
// Only kick off an OOM adjustment pass if needed
updateOomAdj |= queue.runningOomAdjusted;
// Move to considering next runnable queue
queue = nextQueue;
@@ -736,7 +736,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);
final IApplicationThread thread = app.getThread();
final IApplicationThread thread = app.getOnewayThread();
if (thread != null) {
try {
if (receiver instanceof BroadcastFilter) {
@@ -777,7 +777,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
private void scheduleResultTo(@NonNull BroadcastRecord r) {
if ((r.resultToApp == null) || (r.resultTo == null)) return;
final ProcessRecord app = r.resultToApp;
final IApplicationThread thread = app.getThread();
final IApplicationThread thread = app.getOnewayThread();
if (thread != null) {
mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
app, OOM_ADJ_REASON_FINISH_RECEIVER);
@@ -1245,8 +1245,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (queue.app != null) {
queue.app.mReceivers.incrementCurReceivers();
queue.app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
// Don't bump its LRU position if it's in the background restricted.
if (mService.mInternal.getRestrictionLevel(
queue.uid) < ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
@@ -1256,7 +1254,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(queue.app,
OOM_ADJ_REASON_START_RECEIVER);
mService.enqueueOomAdjTargetLocked(queue.app);
if (queue.runningOomAdjusted) {
queue.app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
mService.enqueueOomAdjTargetLocked(queue.app);
}
}
}
@@ -1266,10 +1267,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
*/
private void notifyStoppedRunning(@NonNull BroadcastProcessQueue queue) {
if (queue.app != null) {
// Update during our next pass; no need for an immediate update
mService.enqueueOomAdjTargetLocked(queue.app);
queue.app.mReceivers.decrementCurReceivers();
if (queue.runningOomAdjusted) {
mService.enqueueOomAdjTargetLocked(queue.app);
}
}
}

View File

@@ -54,6 +54,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.Zygote;
import com.android.server.FgThread;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
@@ -142,6 +143,13 @@ class ProcessRecord implements WindowProcessListener {
@CompositeRWLock({"mService", "mProcLock"})
private IApplicationThread mThread;
/**
* Instance of {@link #mThread} that will always meet the {@code oneway}
* contract, possibly by using {@link SameProcessApplicationThread}.
*/
@CompositeRWLock({"mService", "mProcLock"})
private IApplicationThread mOnewayThread;
/**
* Always keep this application running?
*/
@@ -603,16 +611,27 @@ class ProcessRecord implements WindowProcessListener {
return mThread;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
IApplicationThread getOnewayThread() {
return mOnewayThread;
}
@GuardedBy({"mService", "mProcLock"})
public void makeActive(IApplicationThread thread, ProcessStatsService tracker) {
mProfile.onProcessActive(thread, tracker);
mThread = thread;
if (mPid == Process.myPid()) {
mOnewayThread = new SameProcessApplicationThread(thread, FgThread.getHandler());
} else {
mOnewayThread = thread;
}
mWindowProcessController.setThread(thread);
}
@GuardedBy({"mService", "mProcLock"})
public void makeInactive(ProcessStatsService tracker) {
mThread = null;
mOnewayThread = null;
mWindowProcessController.setThread(null);
mProfile.onProcessInactive(tracker);
}

View File

@@ -0,0 +1,73 @@
/*
* 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.annotation.NonNull;
import android.app.IApplicationThread;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import java.util.Objects;
/**
* Wrapper around an {@link IApplicationThread} that delegates selected calls
* through a {@link Handler} so they meet the {@code oneway} contract of
* returning immediately after dispatch.
*/
public class SameProcessApplicationThread extends IApplicationThread.Default {
private final IApplicationThread mWrapped;
private final Handler mHandler;
public SameProcessApplicationThread(@NonNull IApplicationThread wrapped,
@NonNull Handler handler) {
mWrapped = Objects.requireNonNull(wrapped);
mHandler = Objects.requireNonNull(handler);
}
@Override
public void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
int resultCode, String data, Bundle extras, boolean sync, int sendingUser,
int processState) {
mHandler.post(() -> {
try {
mWrapped.scheduleReceiver(intent, info, compatInfo, resultCode, data, extras, sync,
sendingUser, processState);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
});
}
@Override
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, int resultCode,
String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser,
int processState) {
mHandler.post(() -> {
try {
mWrapped.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras,
ordered, sticky, sendingUser, processState);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
});
}
}

View File

@@ -87,6 +87,10 @@ public class BroadcastQueueModernImplTest {
mHandlerThread.start();
mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.DELAY_URGENT_MILLIS = -120_000;
mConstants.DELAY_NORMAL_MILLIS = 10_000;
mConstants.DELAY_CACHED_MILLIS = 120_000;
mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
mConstants, mConstants);

View File

@@ -888,7 +888,7 @@ public class BroadcastQueueTest {
}) {
// Confirm expected OOM adjustments; we were invoked once to upgrade
// and once to downgrade
assertEquals(ActivityManager.PROCESS_STATE_RECEIVER,
assertEquals(String.valueOf(receiverApp), ActivityManager.PROCESS_STATE_RECEIVER,
receiverApp.mState.getReportedProcState());
verify(mAms, times(2)).enqueueOomAdjTargetLocked(eq(receiverApp));
@@ -897,8 +897,8 @@ public class BroadcastQueueTest {
// cold-started apps to be thawed, but the modern stack does
} else {
// Confirm that app was thawed
verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(receiverApp),
eq(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER));
verify(mAms.mOomAdjuster.mCachedAppOptimizer, atLeastOnce()).unfreezeTemporarily(
eq(receiverApp), eq(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER));
// Confirm that we added package to process
verify(receiverApp, atLeastOnce()).addPackage(eq(receiverApp.info.packageName),
@@ -1599,4 +1599,39 @@ public class BroadcastQueueTest {
assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
assertTrue(mQueue.isBeyondBarrierLocked(afterSecond));
}
/**
* Verify that we OOM adjust for manifest receivers.
*/
@Test
public void testOomAdjust_Manifest() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
makeManifestReceiver(PACKAGE_GREEN, CLASS_BLUE),
makeManifestReceiver(PACKAGE_GREEN, CLASS_RED))));
waitForIdle();
verify(mAms, atLeastOnce()).enqueueOomAdjTargetLocked(any());
}
/**
* Verify that we never OOM adjust for registered receivers.
*/
@Test
public void testOomAdjust_Registered() throws Exception {
final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
List.of(makeRegisteredReceiver(receiverApp),
makeRegisteredReceiver(receiverApp),
makeRegisteredReceiver(receiverApp))));
waitForIdle();
verify(mAms, never()).enqueueOomAdjTargetLocked(any());
}
}