From 82a6a49514311d380dd71227e4bc7c63e08bdfdc Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Tue, 28 Feb 2023 16:24:25 -0700 Subject: [PATCH] Wait for IApplicationThread events via ping/pong. To aid in writing faster-running tests, clients might be interested in confirming that all pending IApplicationThread events have been processed. This change provides that mechanism by adding a ping/pong event which goes through the same IApplicationThread serialization ordering used for other events, and waiting for all running apps to process that ping/pong before returning. Bug: 269704322 Test: TH Change-Id: Idbf265a734998cf49dd89bf939e17f900d6b1fdd --- core/java/android/app/ActivityThread.java | 10 +++ core/java/android/app/IApplicationThread.aidl | 1 + .../server/am/ActivityManagerService.java | 78 ++++++++++++++++++- .../am/ActivityManagerShellCommand.java | 12 ++- .../am/SameProcessApplicationThread.java | 29 ++++--- 5 files changed, 116 insertions(+), 14 deletions(-) diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 50275abe249a8..ffaf0b8f1c8d6 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1157,6 +1157,11 @@ public final class ActivityThread extends ClientTransactionHandler sendMessage(H.TIMEOUT_SERVICE, token, startId); } + @Override + public final void schedulePing(RemoteCallback pong) { + sendMessage(H.PING, pong); + } + @Override public final void bindApplication(String processName, ApplicationInfo appInfo, String sdkSandboxClientAppVolumeUuid, String sdkSandboxClientAppPackage, @@ -2158,6 +2163,7 @@ public final class ActivityThread extends ClientTransactionHandler public static final int DUMP_GFXINFO = 165; public static final int DUMP_RESOURCES = 166; public static final int TIMEOUT_SERVICE = 167; + public static final int PING = 168; public static final int INSTRUMENT_WITHOUT_RESTART = 170; public static final int FINISH_INSTRUMENTATION_WITHOUT_RESTART = 171; @@ -2213,6 +2219,7 @@ public final class ActivityThread extends ClientTransactionHandler return "FINISH_INSTRUMENTATION_WITHOUT_RESTART"; case DUMP_RESOURCES: return "DUMP_RESOURCES"; case TIMEOUT_SERVICE: return "TIMEOUT_SERVICE"; + case PING: return "PING"; } } return Integer.toString(code); @@ -2296,6 +2303,9 @@ public final class ActivityThread extends ClientTransactionHandler handleTimeoutService((IBinder) msg.obj, msg.arg1); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; + case PING: + ((RemoteCallback) msg.obj).sendResult(null); + break; case CONFIGURATION_CHANGED: mConfigurationController.handleConfigurationChanged((Configuration) msg.obj); break; diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index dad9b435e9e78..4f77203c8c6f2 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -172,4 +172,5 @@ oneway interface IApplicationThread { in TranslationSpec targetSpec, in List viewIds, in UiTranslationSpec uiTranslationSpec); void scheduleTimeoutService(IBinder token, int startId); + void schedulePing(in RemoteCallback pong); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7df761371b8b6..1e1baad770f0b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -345,6 +345,7 @@ import android.util.FeatureFlagUtils; import android.util.IndentingPrintWriter; import android.util.IntArray; import android.util.Log; +import android.util.LogWriter; import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; @@ -485,9 +486,11 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiFunction; @@ -18906,10 +18909,11 @@ public class ActivityManagerService extends IActivityManager.Stub @Override public void waitForBroadcastBarrier() { - waitForBroadcastBarrier(/* printWriter= */ null, false); + waitForBroadcastBarrier(/* printWriter= */ null, false, false); } - public void waitForBroadcastBarrier(@Nullable PrintWriter pw, boolean flushBroadcastLoopers) { + public void waitForBroadcastBarrier(@Nullable PrintWriter pw, + boolean flushBroadcastLoopers, boolean flushApplicationThreads) { enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()"); if (flushBroadcastLoopers) { BroadcastLoopers.waitForBarrier(pw); @@ -18917,6 +18921,76 @@ public class ActivityManagerService extends IActivityManager.Stub for (BroadcastQueue queue : mBroadcastQueues) { queue.waitForBarrier(pw); } + if (flushApplicationThreads) { + waitForApplicationBarrier(pw); + } + } + + /** + * Wait for all pending {@link IApplicationThread} events to be processed in + * all currently running apps. + */ + public void waitForApplicationBarrier(@Nullable PrintWriter pw) { + if (pw == null) { + pw = new PrintWriter(new LogWriter(Log.VERBOSE, TAG)); + } + + final CountDownLatch finishedLatch = new CountDownLatch(1); + final AtomicInteger pingCount = new AtomicInteger(0); + final AtomicInteger pongCount = new AtomicInteger(0); + final RemoteCallback pongCallback = new RemoteCallback((result) -> { + if (pongCount.incrementAndGet() == pingCount.get()) { + finishedLatch.countDown(); + } + }); + + // Insert an extra "ping" as a sentinel value to guard us from finishing + // too quickly in parallel below + pingCount.incrementAndGet(); + + synchronized (mProcLock) { + final ArrayMap> pmap = + mProcessList.getProcessNamesLOSP().getMap(); + final int numProc = pmap.size(); + for (int iProc = 0; iProc < numProc; iProc++) { + final SparseArray apps = pmap.valueAt(iProc); + for (int iApp = 0, numApps = apps.size(); iApp < numApps; iApp++) { + final ProcessRecord app = apps.valueAt(iApp); + final IApplicationThread thread = app.getOnewayThread(); + if (thread != null) { + mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app, + OomAdjuster.OOM_ADJ_REASON_NONE); + pingCount.incrementAndGet(); + try { + thread.schedulePing(pongCallback); + } catch (RemoteException ignored) { + // When we failed to ping remote process, pretend as + // if we received the expected pong + pongCallback.sendResult(null); + } + } + } + } + } + + // Now that we've dispatched all "ping" events above, we can send our + // "pong" sentinel value + pongCallback.sendResult(null); + + // Wait for any remaining "pong" events to trickle in + for (int i = 0; i < 30; i++) { + try { + if (finishedLatch.await(1, TimeUnit.SECONDS)) { + pw.println("Finished application barriers!"); + return; + } else { + pw.println("Waiting for application barriers, at " + pongCount.get() + " of " + + pingCount.get() + "..."); + } + } catch (InterruptedException ignored) { + } + } + pw.println("Gave up waiting for application barriers!"); } void setIgnoreDeliveryGroupPolicy(@NonNull String broadcastAction) { diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 4c1835eb80f88..30000d35915a1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -359,6 +359,8 @@ final class ActivityManagerShellCommand extends ShellCommand { return runWaitForBroadcastIdle(pw); case "wait-for-broadcast-barrier": return runWaitForBroadcastBarrier(pw); + case "wait-for-application-barrier": + return runWaitForApplicationBarrier(pw); case "set-ignore-delivery-group-policy": return runSetIgnoreDeliveryGroupPolicy(pw); case "clear-ignore-delivery-group-policy": @@ -3332,16 +3334,24 @@ final class ActivityManagerShellCommand extends ShellCommand { int runWaitForBroadcastBarrier(PrintWriter pw) throws RemoteException { boolean flushBroadcastLoopers = false; + boolean flushApplicationThreads = false; String opt; while ((opt = getNextOption()) != null) { if (opt.equals("--flush-broadcast-loopers")) { flushBroadcastLoopers = true; + } else if (opt.equals("--flush-application-threads")) { + flushApplicationThreads = true; } else { getErrPrintWriter().println("Error: Unknown option: " + opt); return -1; } } - mInternal.waitForBroadcastBarrier(pw, flushBroadcastLoopers); + mInternal.waitForBroadcastBarrier(pw, flushBroadcastLoopers, flushApplicationThreads); + return 0; + } + + int runWaitForApplicationBarrier(PrintWriter pw) throws RemoteException { + mInternal.waitForApplicationBarrier(pw); return 0; } diff --git a/services/core/java/com/android/server/am/SameProcessApplicationThread.java b/services/core/java/com/android/server/am/SameProcessApplicationThread.java index 82dd5c2cf2c3d..6deaf7b7fe4f5 100644 --- a/services/core/java/com/android/server/am/SameProcessApplicationThread.java +++ b/services/core/java/com/android/server/am/SameProcessApplicationThread.java @@ -25,6 +25,7 @@ import android.content.pm.ActivityInfo; import android.content.res.CompatibilityInfo; import android.os.Bundle; import android.os.Handler; +import android.os.RemoteCallback; import android.os.RemoteException; import java.util.List; @@ -77,17 +78,23 @@ public class SameProcessApplicationThread extends IApplicationThread.Default { @Override public void scheduleReceiverList(List info) { - for (int i = 0; i < info.size(); i++) { - ReceiverInfo r = info.get(i); - if (r.registered) { - scheduleRegisteredReceiver(r.receiver, r.intent, - r.resultCode, r.data, r.extras, r.ordered, r.sticky, r.assumeDelivered, - r.sendingUser, r.processState, r.sendingUid, r.sendingPackage); - } else { - scheduleReceiver(r.intent, r.activityInfo, r.compatInfo, - r.resultCode, r.data, r.extras, r.sync, r.assumeDelivered, - r.sendingUser, r.processState, r.sendingUid, r.sendingPackage); + mHandler.post(() -> { + try { + mWrapped.scheduleReceiverList(info); + } catch (RemoteException e) { + throw new RuntimeException(e); } - } + }); + } + + @Override + public void schedulePing(RemoteCallback pong) { + mHandler.post(() -> { + try { + mWrapped.schedulePing(pong); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + }); } }