Rename Looper::isIdling() to isPolling() to resolve confusion.
The loop isn't technically idle at this time, it's just checking
whether any file descriptors have pending events. However it's
still a good signal as to whether the loop is alive.
Added a real isIdle() function.
Bug: 19532373
Change-Id: Idd273e8774f469ccafb00d560818cf279dfd6ba6
diff --git a/api/current.txt b/api/current.txt
index c43307e..0060c41 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22369,6 +22369,7 @@
public final class MessageQueue {
method public void addIdleHandler(android.os.MessageQueue.IdleHandler);
+ method public boolean isIdle();
method public void removeIdleHandler(android.os.MessageQueue.IdleHandler);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 6acea9e..c2072bc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -23964,6 +23964,7 @@
public final class MessageQueue {
method public void addIdleHandler(android.os.MessageQueue.IdleHandler);
+ method public boolean isIdle();
method public void removeIdleHandler(android.os.MessageQueue.IdleHandler);
}
diff --git a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
index 0699ffb..e19ebf2 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHandlerThread.java
@@ -96,15 +96,15 @@
// Blocks until thread is idling
public void waitUntilIdle() {
Handler handler = waitAndGetHandler();
- Looper looper = handler.getLooper();
- if (looper.isIdling()) {
+ MessageQueue queue = handler.getLooper().getQueue();
+ if (queue.isIdle()) {
return;
}
mIdle.close();
- looper.getQueue().addIdleHandler(mIdleHandler);
+ queue.addIdleHandler(mIdleHandler);
// Ensure that the idle handler gets run even if the looper already went idle
handler.sendEmptyMessage(MSG_POKE_IDLE_HANDLER);
- if (looper.isIdling()) {
+ if (queue.isIdle()) {
return;
}
mIdle.block();
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 6d7c9cf..8b99196 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -50,6 +50,16 @@
* }</pre>
*/
public final class Looper {
+ /*
+ * API Implementation Note:
+ *
+ * This class contains the code required to set up and manage an event loop
+ * based on MessageQueue. APIs that affect the state of the queue should be
+ * defined on MessageQueue or Handler rather than on Looper itself. For example,
+ * idle handlers and sync barriers are defined on the queue whereas preparing the
+ * thread, looping and quitting are defined on the looper.
+ */
+
private static final String TAG = "Looper";
// sThreadLocal.get() will return null unless you've called prepare().
@@ -286,16 +296,6 @@
return mQueue;
}
- /**
- * Return whether this looper's thread is currently idle, waiting for new work
- * to do. This is intrinsically racy, since its state can change before you get
- * the result back.
- * @hide
- */
- public boolean isIdling() {
- return mQueue.isIdling();
- }
-
public void dump(Printer pw, String prefix) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ");
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 01a23ce..f4d609c 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -52,54 +52,7 @@
private native static void nativeDestroy(long ptr);
private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
- private native static boolean nativeIsIdling(long ptr);
-
- /**
- * Callback interface for discovering when a thread is going to block
- * waiting for more messages.
- */
- public static interface IdleHandler {
- /**
- * Called when the message queue has run out of messages and will now
- * wait for more. Return true to keep your idle handler active, false
- * to have it removed. This may be called if there are still messages
- * pending in the queue, but they are all scheduled to be dispatched
- * after the current time.
- */
- boolean queueIdle();
- }
-
- /**
- * Add a new {@link IdleHandler} to this message queue. This may be
- * removed automatically for you by returning false from
- * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
- * invoked, or explicitly removing it with {@link #removeIdleHandler}.
- *
- * <p>This method is safe to call from any thread.
- *
- * @param handler The IdleHandler to be added.
- */
- public void addIdleHandler(IdleHandler handler) {
- if (handler == null) {
- throw new NullPointerException("Can't add a null IdleHandler");
- }
- synchronized (this) {
- mIdleHandlers.add(handler);
- }
- }
-
- /**
- * Remove an {@link IdleHandler} from the queue that was previously added
- * with {@link #addIdleHandler}. If the given object is not currently
- * in the idle list, nothing is done.
- *
- * @param handler The IdleHandler to be removed.
- */
- public void removeIdleHandler(IdleHandler handler) {
- synchronized (this) {
- mIdleHandlers.remove(handler);
- }
- }
+ private native static boolean nativeIsPolling(long ptr);
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
@@ -124,6 +77,77 @@
}
}
+ /**
+ * Returns true if the looper has no pending messages which are due to be processed.
+ *
+ * <p>This method is safe to call from any thread.
+ *
+ * @return True if the looper is idle.
+ */
+ public boolean isIdle() {
+ synchronized (this) {
+ final long now = SystemClock.uptimeMillis();
+ return mMessages == null || now < mMessages.when;
+ }
+ }
+
+ /**
+ * Add a new {@link IdleHandler} to this message queue. This may be
+ * removed automatically for you by returning false from
+ * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
+ * invoked, or explicitly removing it with {@link #removeIdleHandler}.
+ *
+ * <p>This method is safe to call from any thread.
+ *
+ * @param handler The IdleHandler to be added.
+ */
+ public void addIdleHandler(IdleHandler handler) {
+ if (handler == null) {
+ throw new NullPointerException("Can't add a null IdleHandler");
+ }
+ synchronized (this) {
+ mIdleHandlers.add(handler);
+ }
+ }
+
+ /**
+ * Remove an {@link IdleHandler} from the queue that was previously added
+ * with {@link #addIdleHandler}. If the given object is not currently
+ * in the idle list, nothing is done.
+ *
+ * <p>This method is safe to call from any thread.
+ *
+ * @param handler The IdleHandler to be removed.
+ */
+ public void removeIdleHandler(IdleHandler handler) {
+ synchronized (this) {
+ mIdleHandlers.remove(handler);
+ }
+ }
+
+ /**
+ * Returns whether this looper's thread is currently polling for more work to do.
+ * This is a good signal that the loop is still alive rather than being stuck
+ * handling a callback. Note that this method is intrinsically racy, since the
+ * state of the loop can change before you get the result back.
+ *
+ * <p>This method is safe to call from any thread.
+ *
+ * @return True if the looper is currently polling for events.
+ * @hide
+ */
+ public boolean isPolling() {
+ synchronized (this) {
+ return isPollingLocked();
+ }
+ }
+
+ private boolean isPollingLocked() {
+ // If the loop is quitting then it must not be idling.
+ // We can assume mPtr != 0 when mQuitting is false.
+ return !mQuitting && nativeIsPolling(mPtr);
+ }
+
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
@@ -400,18 +424,6 @@
}
}
- boolean isIdling() {
- synchronized (this) {
- return isIdlingLocked();
- }
- }
-
- private boolean isIdlingLocked() {
- // If the loop is quitting then it must not be idling.
- // We can assume mPtr != 0 when mQuitting is false.
- return !mQuitting && nativeIsIdling(mPtr);
- }
-
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
@@ -559,8 +571,23 @@
pw.println(prefix + "Message " + n + ": " + msg.toString(now));
n++;
}
- pw.println(prefix + "(Total messages: " + n + ", idling=" + isIdlingLocked()
+ pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
+ ", quitting=" + mQuitting + ")");
}
}
+
+ /**
+ * Callback interface for discovering when a thread is going to block
+ * waiting for more messages.
+ */
+ public static interface IdleHandler {
+ /**
+ * Called when the message queue has run out of messages and will now
+ * wait for more. Return true to keep your idle handler active, false
+ * to have it removed. This may be called if there are still messages
+ * pending in the queue, but they are all scheduled to be dispatched
+ * after the current time.
+ */
+ boolean queueIdle();
+ }
}
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index 5d7877b..ee64044 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -143,9 +143,9 @@
return nativeMessageQueue->wake();
}
-static jboolean android_os_MessageQueue_nativeIsIdling(JNIEnv* env, jclass clazz, jlong ptr) {
+static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
- return nativeMessageQueue->getLooper()->isIdling();
+ return nativeMessageQueue->getLooper()->isPolling();
}
// ----------------------------------------------------------------------------
@@ -156,7 +156,7 @@
{ "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
- { "nativeIsIdling", "(J)Z", (void*)android_os_MessageQueue_nativeIsIdling }
+ { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling }
};
int register_android_os_MessageQueue(JNIEnv* env) {
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 8e46c4d..794e1b0 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -106,8 +106,8 @@
}
public void scheduleCheckLocked() {
- if (mMonitors.size() == 0 && mHandler.getLooper().isIdling()) {
- // If the target looper is or just recently was idling, then
+ if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
+ // If the target looper has recently been polling, then
// there is no reason to enqueue our checker on it since that
// is as good as it not being deadlocked. This avoid having
// to do a context switch to check the thread. Note that we