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