1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.am; 18 19 import android.annotation.NonNull; 20 import android.os.Handler; 21 import android.os.Looper; 22 import android.os.Message; 23 import android.os.MessageQueue; 24 import android.os.SystemClock; 25 import android.util.ArraySet; 26 import android.util.Slog; 27 28 import com.android.internal.annotations.GuardedBy; 29 30 import java.io.PrintWriter; 31 import java.util.Objects; 32 import java.util.concurrent.CountDownLatch; 33 import java.util.function.BiConsumer; 34 35 /** 36 * Collection of {@link Looper} that are known to be used for broadcast dispatch 37 * within the system. This collection can be useful for callers interested in 38 * confirming that all pending broadcasts have been successfully enqueued. 39 */ 40 public class BroadcastLoopers { 41 private static final String TAG = "BroadcastLoopers"; 42 43 @GuardedBy("sLoopers") 44 private static final ArraySet<Looper> sLoopers = new ArraySet<>(); 45 46 /** 47 * Register the given {@link Looper} as possibly having messages that will 48 * dispatch broadcasts. 49 */ addLooper(@onNull Looper looper)50 public static void addLooper(@NonNull Looper looper) { 51 synchronized (sLoopers) { 52 sLoopers.add(Objects.requireNonNull(looper)); 53 } 54 } 55 56 /** 57 * If the current thread is hosting a {@link Looper}, then register it as 58 * possibly having messages that will dispatch broadcasts. 59 */ addMyLooper()60 public static void addMyLooper() { 61 final Looper looper = Looper.myLooper(); 62 if (looper != null) { 63 synchronized (sLoopers) { 64 if (sLoopers.add(looper)) { 65 Slog.w(TAG, "Found previously unknown looper " + looper.getThread()); 66 } 67 } 68 } 69 } 70 71 /** 72 * Wait for all registered {@link Looper} instances to become idle, as 73 * defined by {@link MessageQueue#isIdle()}. Note that {@link Message#when} 74 * still in the future are ignored for the purposes of the idle test. 75 */ waitForIdle(@onNull PrintWriter pw)76 public static void waitForIdle(@NonNull PrintWriter pw) { 77 waitForCondition(pw, (looper, latch) -> { 78 final MessageQueue queue = looper.getQueue(); 79 queue.addIdleHandler(() -> { 80 latch.countDown(); 81 return false; 82 }); 83 }); 84 } 85 86 /** 87 * Wait for all registered {@link Looper} instances to handle currently waiting messages. 88 * Note that {@link Message#when} still in the future are ignored for the purposes 89 * of the idle test. 90 */ waitForBarrier(@onNull PrintWriter pw)91 public static void waitForBarrier(@NonNull PrintWriter pw) { 92 waitForCondition(pw, (looper, latch) -> { 93 (new Handler(looper)).post(() -> { 94 latch.countDown(); 95 }); 96 }); 97 } 98 99 /** 100 * Wait for all registered {@link Looper} instances to meet a certain condition. 101 */ waitForCondition(@onNull PrintWriter pw, @NonNull BiConsumer<Looper, CountDownLatch> condition)102 private static void waitForCondition(@NonNull PrintWriter pw, 103 @NonNull BiConsumer<Looper, CountDownLatch> condition) { 104 final CountDownLatch latch; 105 synchronized (sLoopers) { 106 final int N = sLoopers.size(); 107 latch = new CountDownLatch(N); 108 for (int i = 0; i < N; i++) { 109 final Looper looper = sLoopers.valueAt(i); 110 final MessageQueue queue = looper.getQueue(); 111 if (queue.isIdle()) { 112 latch.countDown(); 113 } else { 114 condition.accept(looper, latch); 115 } 116 } 117 } 118 119 long lastPrint = 0; 120 while (latch.getCount() > 0) { 121 final long now = SystemClock.uptimeMillis(); 122 if (now >= lastPrint + 1000) { 123 lastPrint = now; 124 pw.println("Waiting for " + latch.getCount() + " loopers to drain..."); 125 pw.flush(); 126 } 127 SystemClock.sleep(100); 128 } 129 pw.println("Loopers drained!"); 130 pw.flush(); 131 } 132 } 133