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