Loading api/current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -22372,7 +22372,17 @@ package android.os { public final class MessageQueue { method public void addIdleHandler(android.os.MessageQueue.IdleHandler); method public boolean isIdle(); method public void registerFileDescriptorCallback(java.io.FileDescriptor, int, android.os.MessageQueue.FileDescriptorCallback); method public void removeIdleHandler(android.os.MessageQueue.IdleHandler); method public void unregisterFileDescriptorCallback(java.io.FileDescriptor); } public static abstract class MessageQueue.FileDescriptorCallback { ctor public MessageQueue.FileDescriptorCallback(); method public int onFileDescriptorEvents(java.io.FileDescriptor, int); field public static final int EVENT_ERROR = 4; // 0x4 field public static final int EVENT_INPUT = 1; // 0x1 field public static final int EVENT_OUTPUT = 2; // 0x2 } public static abstract interface MessageQueue.IdleHandler { api/system-current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -23967,7 +23967,17 @@ package android.os { public final class MessageQueue { method public void addIdleHandler(android.os.MessageQueue.IdleHandler); method public boolean isIdle(); method public void registerFileDescriptorCallback(java.io.FileDescriptor, int, android.os.MessageQueue.FileDescriptorCallback); method public void removeIdleHandler(android.os.MessageQueue.IdleHandler); method public void unregisterFileDescriptorCallback(java.io.FileDescriptor); } public static abstract class MessageQueue.FileDescriptorCallback { ctor public MessageQueue.FileDescriptorCallback(); method public int onFileDescriptorEvents(java.io.FileDescriptor, int); field public static final int EVENT_ERROR = 4; // 0x4 field public static final int EVENT_INPUT = 1; // 0x1 field public static final int EVENT_OUTPUT = 2; // 0x2 } public static abstract interface MessageQueue.IdleHandler { core/java/android/os/Looper.java +1 −0 Original line number Diff line number Diff line Loading @@ -273,6 +273,7 @@ public final class Looper { mQueue.dump(pw, prefix + " "); } @Override public String toString() { return "Looper (" + mThread.getName() + ", tid " + mThread.getId() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; Loading core/java/android/os/MessageQueue.java +253 −6 Original line number Diff line number Diff line Loading @@ -16,9 +16,15 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; import android.util.Log; import android.util.Printer; import android.util.SparseArray; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; /** Loading @@ -30,6 +36,9 @@ import java.util.ArrayList; * {@link Looper#myQueue() Looper.myQueue()}. */ public final class MessageQueue { private static final String TAG = "MessageQueue"; private static final boolean DEBUG = false; // True if the message queue can be quit. private final boolean mQuitAllowed; Loading @@ -38,6 +47,7 @@ public final class MessageQueue { Message mMessages; private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; private IdleHandler[] mPendingIdleHandlers; private boolean mQuitting; Loading @@ -50,9 +60,10 @@ public final class MessageQueue { private native static long nativeInit(); private native static void nativeDestroy(long ptr); private native static void nativePollOnce(long ptr, int timeoutMillis); private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ private native static void nativeWake(long ptr); private native static boolean nativeIsPolling(long ptr); private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; Loading Loading @@ -101,7 +112,7 @@ public final class MessageQueue { * * @param handler The IdleHandler to be added. */ public void addIdleHandler(IdleHandler handler) { public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } Loading @@ -119,7 +130,7 @@ public final class MessageQueue { * * @param handler The IdleHandler to be removed. */ public void removeIdleHandler(IdleHandler handler) { public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } Loading Loading @@ -148,6 +159,151 @@ public final class MessageQueue { return !mQuitting && nativeIsPolling(mPtr); } /** * Registers a file descriptor callback to receive notification when file descriptor * related events occur. * <p> * If the file descriptor has already been registered, the specified events * and callback will replace any that were previously associated with it. * It is not possible to set more than one callback per file descriptor. * </p><p> * It is important to always unregister the callback when the file descriptor * is no longer of use. * </p> * * @param fd The file descriptor for which a callback will be registered. * @param events The set of events to receive: a combination of the * {@link FileDescriptorCallback#EVENT_INPUT}, * {@link FileDescriptorCallback#EVENT_OUTPUT}, and * {@link FileDescriptorCallback#EVENT_ERROR} event masks. If the requested * set of events is zero, then the callback is unregistered. * @param callback The callback to invoke when file descriptor events occur. * * @see FileDescriptorCallback * @see #unregisterFileDescriptorCallback */ public void registerFileDescriptorCallback(@NonNull FileDescriptor fd, @FileDescriptorCallback.Events int events, @NonNull FileDescriptorCallback callback) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } synchronized (this) { setFileDescriptorCallbackLocked(fd, events, callback); } } /** * Unregisters a file descriptor callback. * <p> * This method does nothing if no callback has been registered for the * specified file descriptor. * </p> * * @param fd The file descriptor whose callback will be unregistered. * * @see FileDescriptorCallback * @see #registerFileDescriptorCallback */ public void unregisterFileDescriptorCallback(@NonNull FileDescriptor fd) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } synchronized (this) { setFileDescriptorCallbackLocked(fd, 0, null); } } private void setFileDescriptorCallbackLocked(FileDescriptor fd, int events, FileDescriptorCallback callback) { final int fdNum = fd.getInt$(); int index = -1; FileDescriptorRecord record = null; if (mFileDescriptorRecords != null) { index = mFileDescriptorRecords.indexOfKey(fdNum); if (index >= 0) { record = mFileDescriptorRecords.valueAt(index); if (record != null && record.mEvents == events) { return; } } } if (events != 0) { events |= FileDescriptorCallback.EVENT_ERROR; if (record == null) { if (mFileDescriptorRecords == null) { mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); } record = new FileDescriptorRecord(fd, events, callback); mFileDescriptorRecords.put(fdNum, record); } else { record.mCallback = callback; record.mEvents = events; record.mSeq += 1; } nativeSetFileDescriptorEvents(mPtr, fdNum, events); } else if (record != null) { record.mEvents = 0; mFileDescriptorRecords.removeAt(index); } } // Called from native code. private int dispatchEvents(int fd, int events) { // Get the file descriptor record and any state that might change. final FileDescriptorRecord record; final int oldWatchedEvents; final FileDescriptorCallback callback; final int seq; synchronized (this) { record = mFileDescriptorRecords.get(fd); if (record == null) { return 0; // spurious, no callback registered } oldWatchedEvents = record.mEvents; events &= oldWatchedEvents; // filter events based on current watched set if (events == 0) { return oldWatchedEvents; // spurious, watched events changed } callback = record.mCallback; seq = record.mSeq; } // Invoke the callback outside of the lock. int newWatchedEvents = callback.onFileDescriptorEvents( record.mDescriptor, events); if (newWatchedEvents != 0) { newWatchedEvents |= FileDescriptorCallback.EVENT_ERROR; } // Update the file descriptor record if the callback changed the set of // events to watch and the callback itself hasn't been updated since. if (newWatchedEvents != oldWatchedEvents) { synchronized (this) { int index = mFileDescriptorRecords.indexOfKey(fd); if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record && record.mSeq == seq) { record.mEvents = newWatchedEvents; if (newWatchedEvents == 0) { mFileDescriptorRecords.removeAt(index); } } } } // Return the new set of events to watch for native code to take care of. return newWatchedEvents; } 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 Loading Loading @@ -191,7 +347,8 @@ public final class MessageQueue { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { Loading Loading @@ -234,7 +391,7 @@ public final class MessageQueue { try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { Loading Loading @@ -385,7 +542,7 @@ public final class MessageQueue { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } Loading Loading @@ -627,4 +784,94 @@ public final class MessageQueue { */ boolean queueIdle(); } /** * A callback which is invoked when file descriptor related events occur. */ public static abstract class FileDescriptorCallback { /** * File descriptor event: Indicates that the file descriptor is ready for input * operations, such as reading. * <p> * The callback should read all available data from the file descriptor * then return <code>true</code> to keep the callback active or <code>false</code> * to remove the callback. * </p><p> * In the case of a socket, this event may be generated to indicate * that there is at least one incoming connection that the callback * should accept. * </p><p> * This event will only be generated if the {@link #EVENT_INPUT} event mask was * specified when the callback was added. * </p> */ public static final int EVENT_INPUT = 1 << 0; /** * File descriptor event: Indicates that the file descriptor is ready for output * operations, such as writing. * <p> * The callback should write as much data as it needs. If it could not * write everything at once, then it should return <code>true</code> to * keep the callback active. Otherwise, it should return <code>false</code> * to remove the callback then re-register it later when it needs to write * something else. * </p><p> * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was * specified when the callback was added. * </p> */ public static final int EVENT_OUTPUT = 1 << 1; /** * File descriptor event: Indicates that the file descriptor encountered a * fatal error. * <p> * File descriptor errors can occur for various reasons. One common error * is when the remote peer of a socket or pipe closes its end of the connection. * </p><p> * This event may be generated at any time regardless of whether the * {@link #EVENT_ERROR} event mask was specified when the callback was added. * </p> */ public static final int EVENT_ERROR = 1 << 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR}) public @interface Events {} /** * Called when a file descriptor receives events. * <p> * The default implementation does nothing and returns 0 to unregister the callback. * </p> * * @param fd The file descriptor. * @param events The set of events that occurred: a combination of the * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks. * @return The new set of events to watch, or 0 to unregister the callback. * * @see #EVENT_INPUT * @see #EVENT_OUTPUT * @see #EVENT_ERROR */ public @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events) { return 0; } } private static final class FileDescriptorRecord { public final FileDescriptor mDescriptor; public int mEvents; public FileDescriptorCallback mCallback; public int mSeq; public FileDescriptorRecord(FileDescriptor descriptor, int events, FileDescriptorCallback callback) { mDescriptor = descriptor; mEvents = events; mCallback = callback; } } } core/jni/android_os_MessageQueue.cpp +77 −15 Original line number Diff line number Diff line Loading @@ -29,22 +29,31 @@ namespace android { static struct { jfieldID mPtr; // native object attached to the DVM MessageQueue jmethodID dispatchEvents; } gMessageQueueClassInfo; // Must be kept in sync with the constants in Looper.FileDescriptorCallback static const int CALLBACK_EVENT_INPUT = 1 << 0; static const int CALLBACK_EVENT_OUTPUT = 1 << 1; static const int CALLBACK_EVENT_ERROR = 1 << 2; class NativeMessageQueue : public MessageQueue { class NativeMessageQueue : public MessageQueue, public LooperCallback { public: NativeMessageQueue(); virtual ~NativeMessageQueue(); virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj); void pollOnce(JNIEnv* env, int timeoutMillis); void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis); void wake(); void setFileDescriptorEvents(int fd, int events); virtual int handleEvent(int fd, int events, void* data); private: bool mInCallback; JNIEnv* mPollEnv; jobject mPollObj; jthrowable mExceptionObj; }; Loading @@ -66,10 +75,11 @@ bool MessageQueue::raiseAndClearException(JNIEnv* env, const char* msg) { return false; } NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) { NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); mLooper = new Looper(true); Looper::setForThread(mLooper); } } Loading @@ -79,7 +89,7 @@ NativeMessageQueue::~NativeMessageQueue() { void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) { if (exceptionObj) { if (mInCallback) { if (mPollEnv == env) { if (mExceptionObj) { env->DeleteLocalRef(mExceptionObj); } Loading @@ -94,10 +104,13 @@ void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable } } void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) { mInCallback = true; void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) { mPollEnv = env; mPollObj = pollObj; mLooper->pollOnce(timeoutMillis); mInCallback = false; mPollObj = NULL; mPollEnv = NULL; if (mExceptionObj) { env->Throw(mExceptionObj); env->DeleteLocalRef(mExceptionObj); Loading @@ -109,6 +122,46 @@ void NativeMessageQueue::wake() { mLooper->wake(); } void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) { if (events) { int looperEvents = 0; if (events & CALLBACK_EVENT_INPUT) { looperEvents |= Looper::EVENT_INPUT; } if (events & CALLBACK_EVENT_OUTPUT) { looperEvents |= Looper::EVENT_OUTPUT; } mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this, reinterpret_cast<void*>(events)); } else { mLooper->removeFd(fd); } } int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) { int events = 0; if (looperEvents & Looper::EVENT_INPUT) { events |= CALLBACK_EVENT_INPUT; } if (looperEvents & Looper::EVENT_OUTPUT) { events |= CALLBACK_EVENT_OUTPUT; } if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) { events |= CALLBACK_EVENT_ERROR; } int oldWatchedEvents = reinterpret_cast<int>(data); int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj, gMessageQueueClassInfo.dispatchEvents, fd, events); if (!newWatchedEvents) { return 0; // unregister the fd } if (newWatchedEvents != oldWatchedEvents) { setFileDescriptorEvents(fd, newWatchedEvents); } return 1; } // ---------------------------------------------------------------------------- sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) { Loading @@ -132,15 +185,15 @@ static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlo nativeMessageQueue->decStrong(env); } static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz, static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, timeoutMillis); nativeMessageQueue->pollOnce(env, obj, timeoutMillis); } static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); return nativeMessageQueue->wake(); nativeMessageQueue->wake(); } static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass clazz, jlong ptr) { Loading @@ -148,6 +201,12 @@ static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass claz return nativeMessageQueue->getLooper()->isPolling(); } static void android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz, jlong ptr, jint fd, jint events) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->setFileDescriptorEvents(fd, events); } // ---------------------------------------------------------------------------- static JNINativeMethod gMessageQueueMethods[] = { Loading @@ -156,7 +215,9 @@ static JNINativeMethod gMessageQueueMethods[] = { { "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy }, { "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce }, { "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake }, { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling } { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling }, { "nativeSetFileDescriptorEvents", "(JII)V", (void*)android_os_MessageQueue_nativeSetFileDescriptorEvents }, }; int register_android_os_MessageQueue(JNIEnv* env) { Loading @@ -164,8 +225,9 @@ int register_android_os_MessageQueue(JNIEnv* env) { NELEM(gMessageQueueMethods)); jclass clazz = FindClassOrDie(env, "android/os/MessageQueue"); gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J"); gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz, "dispatchEvents", "(II)I"); return res; } Loading Loading
api/current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -22372,7 +22372,17 @@ package android.os { public final class MessageQueue { method public void addIdleHandler(android.os.MessageQueue.IdleHandler); method public boolean isIdle(); method public void registerFileDescriptorCallback(java.io.FileDescriptor, int, android.os.MessageQueue.FileDescriptorCallback); method public void removeIdleHandler(android.os.MessageQueue.IdleHandler); method public void unregisterFileDescriptorCallback(java.io.FileDescriptor); } public static abstract class MessageQueue.FileDescriptorCallback { ctor public MessageQueue.FileDescriptorCallback(); method public int onFileDescriptorEvents(java.io.FileDescriptor, int); field public static final int EVENT_ERROR = 4; // 0x4 field public static final int EVENT_INPUT = 1; // 0x1 field public static final int EVENT_OUTPUT = 2; // 0x2 } public static abstract interface MessageQueue.IdleHandler {
api/system-current.txt +10 −0 Original line number Diff line number Diff line Loading @@ -23967,7 +23967,17 @@ package android.os { public final class MessageQueue { method public void addIdleHandler(android.os.MessageQueue.IdleHandler); method public boolean isIdle(); method public void registerFileDescriptorCallback(java.io.FileDescriptor, int, android.os.MessageQueue.FileDescriptorCallback); method public void removeIdleHandler(android.os.MessageQueue.IdleHandler); method public void unregisterFileDescriptorCallback(java.io.FileDescriptor); } public static abstract class MessageQueue.FileDescriptorCallback { ctor public MessageQueue.FileDescriptorCallback(); method public int onFileDescriptorEvents(java.io.FileDescriptor, int); field public static final int EVENT_ERROR = 4; // 0x4 field public static final int EVENT_INPUT = 1; // 0x1 field public static final int EVENT_OUTPUT = 2; // 0x2 } public static abstract interface MessageQueue.IdleHandler {
core/java/android/os/Looper.java +1 −0 Original line number Diff line number Diff line Loading @@ -273,6 +273,7 @@ public final class Looper { mQueue.dump(pw, prefix + " "); } @Override public String toString() { return "Looper (" + mThread.getName() + ", tid " + mThread.getId() + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}"; Loading
core/java/android/os/MessageQueue.java +253 −6 Original line number Diff line number Diff line Loading @@ -16,9 +16,15 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; import android.util.Log; import android.util.Printer; import android.util.SparseArray; import java.io.FileDescriptor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; /** Loading @@ -30,6 +36,9 @@ import java.util.ArrayList; * {@link Looper#myQueue() Looper.myQueue()}. */ public final class MessageQueue { private static final String TAG = "MessageQueue"; private static final boolean DEBUG = false; // True if the message queue can be quit. private final boolean mQuitAllowed; Loading @@ -38,6 +47,7 @@ public final class MessageQueue { Message mMessages; private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; private IdleHandler[] mPendingIdleHandlers; private boolean mQuitting; Loading @@ -50,9 +60,10 @@ public final class MessageQueue { private native static long nativeInit(); private native static void nativeDestroy(long ptr); private native static void nativePollOnce(long ptr, int timeoutMillis); private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ private native static void nativeWake(long ptr); private native static boolean nativeIsPolling(long ptr); private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; Loading Loading @@ -101,7 +112,7 @@ public final class MessageQueue { * * @param handler The IdleHandler to be added. */ public void addIdleHandler(IdleHandler handler) { public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } Loading @@ -119,7 +130,7 @@ public final class MessageQueue { * * @param handler The IdleHandler to be removed. */ public void removeIdleHandler(IdleHandler handler) { public void removeIdleHandler(@NonNull IdleHandler handler) { synchronized (this) { mIdleHandlers.remove(handler); } Loading Loading @@ -148,6 +159,151 @@ public final class MessageQueue { return !mQuitting && nativeIsPolling(mPtr); } /** * Registers a file descriptor callback to receive notification when file descriptor * related events occur. * <p> * If the file descriptor has already been registered, the specified events * and callback will replace any that were previously associated with it. * It is not possible to set more than one callback per file descriptor. * </p><p> * It is important to always unregister the callback when the file descriptor * is no longer of use. * </p> * * @param fd The file descriptor for which a callback will be registered. * @param events The set of events to receive: a combination of the * {@link FileDescriptorCallback#EVENT_INPUT}, * {@link FileDescriptorCallback#EVENT_OUTPUT}, and * {@link FileDescriptorCallback#EVENT_ERROR} event masks. If the requested * set of events is zero, then the callback is unregistered. * @param callback The callback to invoke when file descriptor events occur. * * @see FileDescriptorCallback * @see #unregisterFileDescriptorCallback */ public void registerFileDescriptorCallback(@NonNull FileDescriptor fd, @FileDescriptorCallback.Events int events, @NonNull FileDescriptorCallback callback) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } synchronized (this) { setFileDescriptorCallbackLocked(fd, events, callback); } } /** * Unregisters a file descriptor callback. * <p> * This method does nothing if no callback has been registered for the * specified file descriptor. * </p> * * @param fd The file descriptor whose callback will be unregistered. * * @see FileDescriptorCallback * @see #registerFileDescriptorCallback */ public void unregisterFileDescriptorCallback(@NonNull FileDescriptor fd) { if (fd == null) { throw new IllegalArgumentException("fd must not be null"); } synchronized (this) { setFileDescriptorCallbackLocked(fd, 0, null); } } private void setFileDescriptorCallbackLocked(FileDescriptor fd, int events, FileDescriptorCallback callback) { final int fdNum = fd.getInt$(); int index = -1; FileDescriptorRecord record = null; if (mFileDescriptorRecords != null) { index = mFileDescriptorRecords.indexOfKey(fdNum); if (index >= 0) { record = mFileDescriptorRecords.valueAt(index); if (record != null && record.mEvents == events) { return; } } } if (events != 0) { events |= FileDescriptorCallback.EVENT_ERROR; if (record == null) { if (mFileDescriptorRecords == null) { mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); } record = new FileDescriptorRecord(fd, events, callback); mFileDescriptorRecords.put(fdNum, record); } else { record.mCallback = callback; record.mEvents = events; record.mSeq += 1; } nativeSetFileDescriptorEvents(mPtr, fdNum, events); } else if (record != null) { record.mEvents = 0; mFileDescriptorRecords.removeAt(index); } } // Called from native code. private int dispatchEvents(int fd, int events) { // Get the file descriptor record and any state that might change. final FileDescriptorRecord record; final int oldWatchedEvents; final FileDescriptorCallback callback; final int seq; synchronized (this) { record = mFileDescriptorRecords.get(fd); if (record == null) { return 0; // spurious, no callback registered } oldWatchedEvents = record.mEvents; events &= oldWatchedEvents; // filter events based on current watched set if (events == 0) { return oldWatchedEvents; // spurious, watched events changed } callback = record.mCallback; seq = record.mSeq; } // Invoke the callback outside of the lock. int newWatchedEvents = callback.onFileDescriptorEvents( record.mDescriptor, events); if (newWatchedEvents != 0) { newWatchedEvents |= FileDescriptorCallback.EVENT_ERROR; } // Update the file descriptor record if the callback changed the set of // events to watch and the callback itself hasn't been updated since. if (newWatchedEvents != oldWatchedEvents) { synchronized (this) { int index = mFileDescriptorRecords.indexOfKey(fd); if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record && record.mSeq == seq) { record.mEvents = newWatchedEvents; if (newWatchedEvents == 0) { mFileDescriptorRecords.removeAt(index); } } } } // Return the new set of events to watch for native code to take care of. return newWatchedEvents; } 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 Loading Loading @@ -191,7 +347,8 @@ public final class MessageQueue { mMessages = msg.next; } msg.next = null; if (false) Log.v("MessageQueue", "Returning message: " + msg); if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { Loading Loading @@ -234,7 +391,7 @@ public final class MessageQueue { try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf("MessageQueue", "IdleHandler threw exception", t); Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { Loading Loading @@ -385,7 +542,7 @@ public final class MessageQueue { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } Loading Loading @@ -627,4 +784,94 @@ public final class MessageQueue { */ boolean queueIdle(); } /** * A callback which is invoked when file descriptor related events occur. */ public static abstract class FileDescriptorCallback { /** * File descriptor event: Indicates that the file descriptor is ready for input * operations, such as reading. * <p> * The callback should read all available data from the file descriptor * then return <code>true</code> to keep the callback active or <code>false</code> * to remove the callback. * </p><p> * In the case of a socket, this event may be generated to indicate * that there is at least one incoming connection that the callback * should accept. * </p><p> * This event will only be generated if the {@link #EVENT_INPUT} event mask was * specified when the callback was added. * </p> */ public static final int EVENT_INPUT = 1 << 0; /** * File descriptor event: Indicates that the file descriptor is ready for output * operations, such as writing. * <p> * The callback should write as much data as it needs. If it could not * write everything at once, then it should return <code>true</code> to * keep the callback active. Otherwise, it should return <code>false</code> * to remove the callback then re-register it later when it needs to write * something else. * </p><p> * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was * specified when the callback was added. * </p> */ public static final int EVENT_OUTPUT = 1 << 1; /** * File descriptor event: Indicates that the file descriptor encountered a * fatal error. * <p> * File descriptor errors can occur for various reasons. One common error * is when the remote peer of a socket or pipe closes its end of the connection. * </p><p> * This event may be generated at any time regardless of whether the * {@link #EVENT_ERROR} event mask was specified when the callback was added. * </p> */ public static final int EVENT_ERROR = 1 << 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR}) public @interface Events {} /** * Called when a file descriptor receives events. * <p> * The default implementation does nothing and returns 0 to unregister the callback. * </p> * * @param fd The file descriptor. * @param events The set of events that occurred: a combination of the * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks. * @return The new set of events to watch, or 0 to unregister the callback. * * @see #EVENT_INPUT * @see #EVENT_OUTPUT * @see #EVENT_ERROR */ public @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events) { return 0; } } private static final class FileDescriptorRecord { public final FileDescriptor mDescriptor; public int mEvents; public FileDescriptorCallback mCallback; public int mSeq; public FileDescriptorRecord(FileDescriptor descriptor, int events, FileDescriptorCallback callback) { mDescriptor = descriptor; mEvents = events; mCallback = callback; } } }
core/jni/android_os_MessageQueue.cpp +77 −15 Original line number Diff line number Diff line Loading @@ -29,22 +29,31 @@ namespace android { static struct { jfieldID mPtr; // native object attached to the DVM MessageQueue jmethodID dispatchEvents; } gMessageQueueClassInfo; // Must be kept in sync with the constants in Looper.FileDescriptorCallback static const int CALLBACK_EVENT_INPUT = 1 << 0; static const int CALLBACK_EVENT_OUTPUT = 1 << 1; static const int CALLBACK_EVENT_ERROR = 1 << 2; class NativeMessageQueue : public MessageQueue { class NativeMessageQueue : public MessageQueue, public LooperCallback { public: NativeMessageQueue(); virtual ~NativeMessageQueue(); virtual void raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj); void pollOnce(JNIEnv* env, int timeoutMillis); void pollOnce(JNIEnv* env, jobject obj, int timeoutMillis); void wake(); void setFileDescriptorEvents(int fd, int events); virtual int handleEvent(int fd, int events, void* data); private: bool mInCallback; JNIEnv* mPollEnv; jobject mPollObj; jthrowable mExceptionObj; }; Loading @@ -66,10 +75,11 @@ bool MessageQueue::raiseAndClearException(JNIEnv* env, const char* msg) { return false; } NativeMessageQueue::NativeMessageQueue() : mInCallback(false), mExceptionObj(NULL) { NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { mLooper = Looper::getForThread(); if (mLooper == NULL) { mLooper = new Looper(false); mLooper = new Looper(true); Looper::setForThread(mLooper); } } Loading @@ -79,7 +89,7 @@ NativeMessageQueue::~NativeMessageQueue() { void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable exceptionObj) { if (exceptionObj) { if (mInCallback) { if (mPollEnv == env) { if (mExceptionObj) { env->DeleteLocalRef(mExceptionObj); } Loading @@ -94,10 +104,13 @@ void NativeMessageQueue::raiseException(JNIEnv* env, const char* msg, jthrowable } } void NativeMessageQueue::pollOnce(JNIEnv* env, int timeoutMillis) { mInCallback = true; void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) { mPollEnv = env; mPollObj = pollObj; mLooper->pollOnce(timeoutMillis); mInCallback = false; mPollObj = NULL; mPollEnv = NULL; if (mExceptionObj) { env->Throw(mExceptionObj); env->DeleteLocalRef(mExceptionObj); Loading @@ -109,6 +122,46 @@ void NativeMessageQueue::wake() { mLooper->wake(); } void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) { if (events) { int looperEvents = 0; if (events & CALLBACK_EVENT_INPUT) { looperEvents |= Looper::EVENT_INPUT; } if (events & CALLBACK_EVENT_OUTPUT) { looperEvents |= Looper::EVENT_OUTPUT; } mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this, reinterpret_cast<void*>(events)); } else { mLooper->removeFd(fd); } } int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) { int events = 0; if (looperEvents & Looper::EVENT_INPUT) { events |= CALLBACK_EVENT_INPUT; } if (looperEvents & Looper::EVENT_OUTPUT) { events |= CALLBACK_EVENT_OUTPUT; } if (looperEvents & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP | Looper::EVENT_INVALID)) { events |= CALLBACK_EVENT_ERROR; } int oldWatchedEvents = reinterpret_cast<int>(data); int newWatchedEvents = mPollEnv->CallIntMethod(mPollObj, gMessageQueueClassInfo.dispatchEvents, fd, events); if (!newWatchedEvents) { return 0; // unregister the fd } if (newWatchedEvents != oldWatchedEvents) { setFileDescriptorEvents(fd, newWatchedEvents); } return 1; } // ---------------------------------------------------------------------------- sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) { Loading @@ -132,15 +185,15 @@ static void android_os_MessageQueue_nativeDestroy(JNIEnv* env, jclass clazz, jlo nativeMessageQueue->decStrong(env); } static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jclass clazz, static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj, jlong ptr, jint timeoutMillis) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->pollOnce(env, timeoutMillis); nativeMessageQueue->pollOnce(env, obj, timeoutMillis); } static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); return nativeMessageQueue->wake(); nativeMessageQueue->wake(); } static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass clazz, jlong ptr) { Loading @@ -148,6 +201,12 @@ static jboolean android_os_MessageQueue_nativeIsPolling(JNIEnv* env, jclass claz return nativeMessageQueue->getLooper()->isPolling(); } static void android_os_MessageQueue_nativeSetFileDescriptorEvents(JNIEnv* env, jclass clazz, jlong ptr, jint fd, jint events) { NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr); nativeMessageQueue->setFileDescriptorEvents(fd, events); } // ---------------------------------------------------------------------------- static JNINativeMethod gMessageQueueMethods[] = { Loading @@ -156,7 +215,9 @@ static JNINativeMethod gMessageQueueMethods[] = { { "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy }, { "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce }, { "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake }, { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling } { "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling }, { "nativeSetFileDescriptorEvents", "(JII)V", (void*)android_os_MessageQueue_nativeSetFileDescriptorEvents }, }; int register_android_os_MessageQueue(JNIEnv* env) { Loading @@ -164,8 +225,9 @@ int register_android_os_MessageQueue(JNIEnv* env) { NELEM(gMessageQueueMethods)); jclass clazz = FindClassOrDie(env, "android/os/MessageQueue"); gMessageQueueClassInfo.mPtr = GetFieldIDOrDie(env, clazz, "mPtr", "J"); gMessageQueueClassInfo.dispatchEvents = GetMethodIDOrDie(env, clazz, "dispatchEvents", "(II)I"); return res; } Loading