• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 android.os;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.util.Log;
23 import android.util.Printer;
24 import android.util.Slog;
25 import android.util.proto.ProtoOutputStream;
26 
27 import java.util.Objects;
28 
29 /**
30   * Class used to run a message loop for a thread.  Threads by default do
31   * not have a message loop associated with them; to create one, call
32   * {@link #prepare} in the thread that is to run the loop, and then
33   * {@link #loop} to have it process messages until the loop is stopped.
34   *
35   * <p>Most interaction with a message loop is through the
36   * {@link Handler} class.
37   *
38   * <p>This is a typical example of the implementation of a Looper thread,
39   * using the separation of {@link #prepare} and {@link #loop} to create an
40   * initial Handler to communicate with the Looper.
41   *
42   * <pre>
43   *  class LooperThread extends Thread {
44   *      public Handler mHandler;
45   *
46   *      public void run() {
47   *          Looper.prepare();
48   *
49   *          mHandler = new Handler(Looper.myLooper()) {
50   *              public void handleMessage(Message msg) {
51   *                  // process incoming messages here
52   *              }
53   *          };
54   *
55   *          Looper.loop();
56   *      }
57   *  }</pre>
58   */
59 @android.ravenwood.annotation.RavenwoodKeepWholeClass
60 public final class Looper {
61     /*
62      * API Implementation Note:
63      *
64      * This class contains the code required to set up and manage an event loop
65      * based on MessageQueue.  APIs that affect the state of the queue should be
66      * defined on MessageQueue or Handler rather than on Looper itself.  For example,
67      * idle handlers and sync barriers are defined on the queue whereas preparing the
68      * thread, looping, and quitting are defined on the looper.
69      */
70 
71     private static final String TAG = "Looper";
72 
73     private static class NoImagePreloadHolder {
74         // Enable/Disable verbose logging with a system prop. e.g.
75         // adb shell 'setprop log.looper.slow.verbose false && stop && start'
76         private static final boolean sVerboseLogging =
77                 SystemProperties.getBoolean("log.looper.slow.verbose", false);
78     }
79 
80     // sThreadLocal.get() will return null unless you've called prepare().
81     @UnsupportedAppUsage
82     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
83     @UnsupportedAppUsage
84     private static Looper sMainLooper;  // guarded by Looper.class
85     private static Observer sObserver;
86 
87     @UnsupportedAppUsage
88     final MessageQueue mQueue;
89     final Thread mThread;
90     private boolean mInLoop;
91 
92     @UnsupportedAppUsage
93     private Printer mLogging;
94     private long mTraceTag;
95 
96     /**
97      * If set, the looper will show a warning log if a message dispatch takes longer than this.
98      */
99     private long mSlowDispatchThresholdMs;
100 
101     /**
102      * If set, the looper will show a warning log if a message delivery (actual delivery time -
103      * post time) takes longer than this.
104      */
105     private long mSlowDeliveryThresholdMs;
106 
107     /**
108      * True if a message delivery takes longer than {@link #mSlowDeliveryThresholdMs}.
109      */
110     private boolean mSlowDeliveryDetected;
111 
112     /** Initialize the current thread as a looper.
113       * This gives you a chance to create handlers that then reference
114       * this looper, before actually starting the loop. Be sure to call
115       * {@link #loop()} after calling this method, and end it by calling
116       * {@link #quit()}.
117       */
prepare()118     public static void prepare() {
119         prepare(true);
120     }
121 
prepare(boolean quitAllowed)122     private static void prepare(boolean quitAllowed) {
123         if (sThreadLocal.get() != null) {
124             throw new RuntimeException("Only one Looper may be created per thread");
125         }
126         sThreadLocal.set(new Looper(quitAllowed));
127     }
128 
129     /**
130      * Initialize the current thread as a looper, marking it as an
131      * application's main looper. See also: {@link #prepare()}
132      *
133      * @deprecated The main looper for your application is created by the Android environment,
134      *   so you should never need to call this function yourself.
135      */
136     @Deprecated
prepareMainLooper()137     public static void prepareMainLooper() {
138         prepare(false);
139         synchronized (Looper.class) {
140             if (sMainLooper != null) {
141                 throw new IllegalStateException("The main Looper has already been prepared.");
142             }
143             sMainLooper = myLooper();
144         }
145     }
146 
147     /**
148      * Returns the application's main looper, which lives in the main thread of the application.
149      */
getMainLooper()150     public static Looper getMainLooper() {
151         synchronized (Looper.class) {
152             return sMainLooper;
153         }
154     }
155 
156     /**
157      * Force the application's main looper to the given value.  The main looper is typically
158      * configured automatically by the OS, so this capability is only intended to enable testing.
159      *
160      * @hide
161      */
setMainLooperForTest(@onNull Looper looper)162     public static void setMainLooperForTest(@NonNull Looper looper) {
163         synchronized (Looper.class) {
164             sMainLooper = Objects.requireNonNull(looper);
165         }
166     }
167 
168     /**
169      * Clear the application's main looper to be undefined.  The main looper is typically
170      * configured automatically by the OS, so this capability is only intended to enable testing.
171      *
172      * @hide
173      */
clearMainLooperForTest()174     public static void clearMainLooperForTest() {
175         synchronized (Looper.class) {
176             sMainLooper = null;
177         }
178     }
179 
180     /**
181      * Set the transaction observer for all Loopers in this process.
182      *
183      * @hide
184      */
setObserver(@ullable Observer observer)185     public static void setObserver(@Nullable Observer observer) {
186         sObserver = observer;
187     }
188 
189     /**
190      * Poll and deliver single message, return true if the outer loop should continue.
191      */
192     @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
193             "ClearIdentityCallNotFollowedByTryFinally"})
loopOnce(final Looper me, final long ident, final int thresholdOverride)194     private static boolean loopOnce(final Looper me,
195             final long ident, final int thresholdOverride) {
196         Message msg = me.mQueue.next(); // might block
197         if (msg == null) {
198             // No message indicates that the message queue is quitting.
199             return false;
200         }
201 
202         PerfettoTrace.begin(PerfettoTrace.MQ_CATEGORY, "message_queue_receive")
203                 .beginProto()
204                 .beginNested(2004 /* message_queue */)
205                 .addField(1 /* sending_thread_name */, msg.mSendingThreadName)
206                 .endNested()
207                 .endProto()
208                 .setTerminatingFlow(msg.mEventId.get())
209                 .emit();
210 
211         // This must be in a local variabe, in case a UI event sets the logger
212         final Printer logging = me.mLogging;
213         if (logging != null) {
214             logging.println(">>>>> Dispatching to " + msg.target + " "
215                     + msg.callback + ": " + msg.what);
216         }
217         // Make sure the observer won't change while processing a transaction.
218         final Observer observer = sObserver;
219 
220         final long traceTag = me.mTraceTag;
221         long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
222         long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
223 
224         final boolean hasOverride = thresholdOverride >= 0;
225         if (hasOverride) {
226             slowDispatchThresholdMs = thresholdOverride;
227             slowDeliveryThresholdMs = thresholdOverride;
228         }
229         final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0 || hasOverride)
230                 && (msg.when > 0);
231         final boolean logSlowDispatch = (slowDispatchThresholdMs > 0 || hasOverride);
232 
233         final boolean needStartTime = logSlowDelivery || logSlowDispatch;
234         final boolean needEndTime = logSlowDispatch;
235 
236         if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
237             Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
238         }
239 
240         final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
241         final long dispatchEnd;
242         Object token = null;
243         if (observer != null) {
244             token = observer.messageDispatchStarting();
245         }
246         long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
247         try {
248             msg.target.dispatchMessage(msg);
249             if (observer != null) {
250                 observer.messageDispatched(token, msg);
251             }
252             dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
253         } catch (Exception exception) {
254             if (observer != null) {
255                 observer.dispatchingThrewException(token, msg, exception);
256             }
257             throw exception;
258         } finally {
259             ThreadLocalWorkSource.restore(origWorkSource);
260             if (traceTag != 0) {
261                 Trace.traceEnd(traceTag);
262             }
263         }
264         if (logSlowDelivery) {
265             boolean slow = false;
266 
267             if (!me.mSlowDeliveryDetected || NoImagePreloadHolder.sVerboseLogging) {
268                 slow = showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart,
269                         "delivery", msg);
270             }
271             if (me.mSlowDeliveryDetected) {
272                 if (!slow && (dispatchStart - msg.when) <= 10) {
273                     Slog.w(TAG, "Drained");
274                     me.mSlowDeliveryDetected = false;
275                 }
276             } else if (slow) {
277                 // A slow delivery is detected, suppressing further logs unless verbose logging
278                 // is enabled.
279                 me.mSlowDeliveryDetected = true;
280             }
281         }
282         if (logSlowDispatch) {
283             showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
284         }
285 
286         if (logging != null) {
287             logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
288         }
289 
290         // Make sure that during the course of dispatching the
291         // identity of the thread wasn't corrupted.
292         final long newIdent = Binder.clearCallingIdentity();
293         if (ident != newIdent) {
294             Log.wtf(TAG, "Thread identity changed from 0x"
295                     + Long.toHexString(ident) + " to 0x"
296                     + Long.toHexString(newIdent) + " while dispatching to "
297                     + msg.target.getClass().getName() + " "
298                     + msg.callback + " what=" + msg.what);
299         }
300 
301         PerfettoTrace.end(PerfettoTrace.MQ_CATEGORY).emit();
302         msg.recycleUnchecked();
303 
304         return true;
305     }
306 
307     /**
308      * Run the message queue in this thread. Be sure to call
309      * {@link #quit()} to end the loop.
310      */
311     @SuppressWarnings({"UnusedTokenOfOriginalCallingIdentity",
312             "ClearIdentityCallNotFollowedByTryFinally",
313             "ResultOfClearIdentityCallNotStoredInVariable"})
loop()314     public static void loop() {
315         final Looper me = myLooper();
316         if (me == null) {
317             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
318         }
319         if (me.mInLoop) {
320             Slog.w(TAG, "Loop again would have the queued messages be executed"
321                     + " before this one completed.");
322         }
323 
324         me.mInLoop = true;
325 
326         // Make sure the identity of this thread is that of the local process,
327         // and keep track of what that identity token actually is.
328         Binder.clearCallingIdentity();
329         final long ident = Binder.clearCallingIdentity();
330 
331         // Allow overriding a threshold with a system prop. e.g.
332         // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
333         final int thresholdOverride = getThresholdOverride();
334 
335         me.mSlowDeliveryDetected = false;
336 
337         for (;;) {
338             if (!loopOnce(me, ident, thresholdOverride)) {
339                 return;
340             }
341         }
342     }
343 
344     @android.ravenwood.annotation.RavenwoodReplace
getThresholdOverride()345     private static int getThresholdOverride() {
346         // Allow overriding the threshold for all processes' main looper with a system prop.
347         // e.g. adb shell 'setprop log.looper.any.main.slow 1 && stop && start'
348         if (myLooper() == getMainLooper()) {
349             final int globalOverride = SystemProperties.getInt("log.looper.any.main.slow", -1);
350             if (globalOverride >= 0) {
351                 return globalOverride;
352             }
353         }
354 
355         // Allow overriding the threshold for all threads within a process with a system prop.
356         // e.g. adb shell 'setprop log.looper.1000.any.slow 1 && stop && start'
357         final int processOverride = SystemProperties.getInt("log.looper."
358                 + Process.myUid() + ".any.slow", -1);
359         if (processOverride >= 0) {
360             return processOverride;
361         }
362 
363         return SystemProperties.getInt("log.looper."
364                 + Process.myUid() + "."
365                 + Thread.currentThread().getName()
366                 + ".slow", -1);
367     }
368 
getThresholdOverride$ravenwood()369     private static int getThresholdOverride$ravenwood() {
370         return -1;
371     }
372 
getThreadGroup()373     private static int getThreadGroup() {
374         int threadGroup = Process.THREAD_GROUP_DEFAULT;
375 
376         if (!Process.isIsolated()) {
377             threadGroup = Process.getProcessGroup(Process.myTid());
378         }
379         return threadGroup;
380     }
381 
threadGroupToString(int threadGroup)382     private static String threadGroupToString(int threadGroup) {
383         switch (threadGroup) {
384             case Process.THREAD_GROUP_BACKGROUND:
385                 return "BACKGROUND";
386             case Process.THREAD_GROUP_FOREGROUND:
387                 return "FOREGROUND";
388             case Process.THREAD_GROUP_SYSTEM:
389                 return "SYSTEM";
390             case Process.THREAD_GROUP_AUDIO_APP:
391                 return "AUDIO_APP";
392             case Process.THREAD_GROUP_AUDIO_SYS:
393                 return "AUDIO_SYS";
394             case Process.THREAD_GROUP_TOP_APP:
395                 return "TOP_APP";
396             case Process.THREAD_GROUP_RT_APP:
397                 return "RT_APP";
398             case Process.THREAD_GROUP_RESTRICTED:
399                 return "RESTRICTED";
400             default:
401                 return "UNKNOWN";
402         }
403     }
404 
showSlowLog(long threshold, long measureStart, long measureEnd, String what, Message msg)405     private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
406             String what, Message msg) {
407         final long actualTime = measureEnd - measureStart;
408         if (actualTime < threshold) {
409             return false;
410         }
411 
412         String name = Process.myProcessName();
413         String threadGroup = threadGroupToString(getThreadGroup());
414         boolean isMain = myLooper() == getMainLooper();
415 
416         // For slow delivery, the current message isn't really important, but log it anyway.
417         Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
418                 + Thread.currentThread().getName() + " app=" + name
419                 + " main=" + isMain + " group=" + threadGroup
420                 + " h=" + msg.target.getClass().getName() + " c=" + msg.callback
421                 + " m=" + msg.what);
422         return true;
423     }
424 
425     /**
426      * Return the Looper object associated with the current thread.  Returns
427      * null if the calling thread is not associated with a Looper.
428      */
myLooper()429     public static @Nullable Looper myLooper() {
430         return sThreadLocal.get();
431     }
432 
433     /**
434      * Return the {@link MessageQueue} object associated with the current
435      * thread.  This must be called from a thread running a Looper, or a
436      * NullPointerException will be thrown.
437      */
myQueue()438     public static @NonNull MessageQueue myQueue() {
439         return myLooper().mQueue;
440     }
441 
Looper(boolean quitAllowed)442     private Looper(boolean quitAllowed) {
443         mQueue = new MessageQueue(quitAllowed);
444         mThread = Thread.currentThread();
445     }
446 
447     /**
448      * Returns true if the current thread is this looper's thread.
449      */
isCurrentThread()450     public boolean isCurrentThread() {
451         return Thread.currentThread() == mThread;
452     }
453 
454     /**
455      * Control logging of messages as they are processed by this Looper.  If
456      * enabled, a log message will be written to <var>printer</var>
457      * at the beginning and ending of each message dispatch, identifying the
458      * target Handler and message contents.
459      *
460      * @param printer A Printer object that will receive log messages, or
461      * null to disable message logging.
462      */
setMessageLogging(@ullable Printer printer)463     public void setMessageLogging(@Nullable Printer printer) {
464         mLogging = printer;
465     }
466 
467     /** {@hide} */
468     @UnsupportedAppUsage
setTraceTag(long traceTag)469     public void setTraceTag(long traceTag) {
470         mTraceTag = traceTag;
471     }
472 
473     /**
474      * Set a thresholds for slow dispatch/delivery log.
475      * {@hide}
476      */
setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs)477     public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
478         mSlowDispatchThresholdMs = slowDispatchThresholdMs;
479         mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
480     }
481 
482     /**
483      * Quits the looper.
484      * <p>
485      * Causes the {@link #loop} method to terminate without processing any
486      * more messages in the message queue.
487      * </p><p>
488      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
489      * For example, the {@link Handler#sendMessage(Message)} method will return false.
490      * </p><p class="note">
491      * Using this method may be unsafe because some messages may not be delivered
492      * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
493      * that all pending work is completed in an orderly manner.
494      * </p>
495      *
496      * @see #quitSafely
497      */
quit()498     public void quit() {
499         mQueue.quit(false);
500     }
501 
502     /**
503      * Quits the looper safely.
504      * <p>
505      * Causes the {@link #loop} method to terminate as soon as all remaining messages
506      * in the message queue that are already due to be delivered have been handled.
507      * However pending delayed messages with due times in the future will not be
508      * delivered before the loop terminates.
509      * </p><p>
510      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
511      * For example, the {@link Handler#sendMessage(Message)} method will return false.
512      * </p>
513      */
quitSafely()514     public void quitSafely() {
515         mQueue.quit(true);
516     }
517 
518     /**
519      * Gets the Thread associated with this Looper.
520      *
521      * @return The looper's thread.
522      */
getThread()523     public @NonNull Thread getThread() {
524         return mThread;
525     }
526 
527     /**
528      * Gets this looper's message queue.
529      *
530      * @return The looper's message queue.
531      */
getQueue()532     public @NonNull MessageQueue getQueue() {
533         return mQueue;
534     }
535 
536     /**
537      * Dumps the state of the looper for debugging purposes.
538      *
539      * @param pw A printer to receive the contents of the dump.
540      * @param prefix A prefix to prepend to each line which is printed.
541      */
dump(@onNull Printer pw, @NonNull String prefix)542     public void dump(@NonNull Printer pw, @NonNull String prefix) {
543         pw.println(prefix + toString());
544         mQueue.dump(pw, prefix + "  ", null);
545     }
546 
547     /**
548      * Dumps the state of the looper for debugging purposes.
549      *
550      * @param pw A printer to receive the contents of the dump.
551      * @param prefix A prefix to prepend to each line which is printed.
552      * @param handler Only dump messages for this Handler.
553      * @hide
554      */
dump(@onNull Printer pw, @NonNull String prefix, Handler handler)555     public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
556         pw.println(prefix + toString());
557         mQueue.dump(pw, prefix + "  ", handler);
558     }
559 
560     /** @hide */
dumpDebug(ProtoOutputStream proto, long fieldId)561     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
562         final long looperToken = proto.start(fieldId);
563         proto.write(LooperProto.THREAD_NAME, mThread.getName());
564         proto.write(LooperProto.THREAD_ID, mThread.getId());
565         if (mQueue != null) {
566             mQueue.dumpDebug(proto, LooperProto.QUEUE);
567         }
568         proto.end(looperToken);
569     }
570 
571     @Override
toString()572     public String toString() {
573         return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
574                 + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
575     }
576 
577     /** {@hide} */
578     public interface Observer {
579         /**
580          * Called right before a message is dispatched.
581          *
582          * <p> The token type is not specified to allow the implementation to specify its own type.
583          *
584          * @return a token used for collecting telemetry when dispatching a single message.
585          *         The token token must be passed back exactly once to either
586          *         {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException}
587          *         and must not be reused again.
588          *
589          */
messageDispatchStarting()590         Object messageDispatchStarting();
591 
592         /**
593          * Called when a message was processed by a Handler.
594          *
595          * @param token Token obtained by previously calling
596          *              {@link Observer#messageDispatchStarting} on the same Observer instance.
597          * @param msg The message that was dispatched.
598          */
messageDispatched(Object token, Message msg)599         void messageDispatched(Object token, Message msg);
600 
601         /**
602          * Called when an exception was thrown while processing a message.
603          *
604          * @param token Token obtained by previously calling
605          *              {@link Observer#messageDispatchStarting} on the same Observer instance.
606          * @param msg The message that was dispatched and caused an exception.
607          * @param exception The exception that was thrown.
608          */
dispatchingThrewException(Object token, Message msg, Exception exception)609         void dispatchingThrewException(Object token, Message msg, Exception exception);
610     }
611 }
612