diff options
-rw-r--r-- | core/java/android/view/View.java | 2 | ||||
-rw-r--r-- | include/ui/Input.h | 8 | ||||
-rw-r--r-- | include/ui/Keyboard.h | 5 | ||||
-rw-r--r-- | include/utils/Looper.h | 107 | ||||
-rw-r--r-- | libs/ui/Keyboard.cpp | 21 | ||||
-rw-r--r-- | libs/utils/Looper.cpp | 212 | ||||
-rw-r--r-- | libs/utils/tests/Looper_test.cpp | 268 | ||||
-rw-r--r-- | native/include/android/looper.h | 3 | ||||
-rw-r--r-- | services/input/InputReader.cpp | 44 | ||||
-rw-r--r-- | services/input/InputReader.h | 10 | ||||
-rw-r--r-- | services/input/PointerController.cpp | 118 | ||||
-rw-r--r-- | services/input/PointerController.h | 39 | ||||
-rw-r--r-- | services/input/tests/InputReader_test.cpp | 12 | ||||
-rw-r--r-- | services/java/com/android/server/wm/InputManager.java | 28 | ||||
-rw-r--r-- | services/java/com/android/server/wm/WindowManagerService.java | 1 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 62 |
16 files changed, 877 insertions, 63 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 01bc2df..2170d72 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1678,7 +1678,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility public static final int STATUS_BAR_VISIBLE = 0; /** - * View has requested the status bar to be visible (the default). + * View has requested the status bar to be hidden. * * @see #setSystemUiVisibility(int) */ diff --git a/include/ui/Input.h b/include/ui/Input.h index 86ce098..082f11c 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -52,6 +52,14 @@ enum { }; /* + * SystemUiVisibility constants from View. + */ +enum { + ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0, + ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001, +}; + +/* * Maximum number of pointers supported per motion event. * Smallest number of pointers is 1. * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h index 54bc968..609f319 100644 --- a/include/ui/Keyboard.h +++ b/include/ui/Keyboard.h @@ -127,6 +127,11 @@ extern const char* getAxisLabel(int32_t axisId); */ extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState); +/** + * Returns true if a key is a meta key like ALT or CAPS_LOCK. + */ +extern bool isMetaKey(int32_t keyCode); + } // namespace android #endif // _UI_KEYBOARD_H diff --git a/include/utils/Looper.h b/include/utils/Looper.h index eefff31..3c2905d 100644 --- a/include/utils/Looper.h +++ b/include/utils/Looper.h @@ -45,6 +45,51 @@ struct ALooper { namespace android { /** + * A message that can be posted to a Looper. + */ +struct Message { + Message() : what(0) { } + Message(int what) : what(what) { } + + /* The message type. (interpretation is left up to the handler) */ + int what; +}; + + +/** + * Interface for a Looper message handler. + * + * The Looper holds a strong reference to the message handler whenever it has + * a message to deliver to it. Make sure to call Looper::removeMessages + * to remove any pending messages destined for the handler so that the handler + * can be destroyed. + */ +class MessageHandler : public virtual RefBase { +protected: + virtual ~MessageHandler() { } + +public: + /** + * Handles a message. + */ + virtual void handleMessage(const Message& message) = 0; +}; + + +/** + * A simple proxy that holds a weak reference to a message handler. + */ +class WeakMessageHandler : public MessageHandler { +public: + WeakMessageHandler(const wp<MessageHandler>& handler); + virtual void handleMessage(const Message& message); + +private: + wp<MessageHandler> mHandler; +}; + + +/** * A polling loop that supports monitoring file descriptor events, optionally * using callbacks. The implementation uses epoll() internally. * @@ -166,6 +211,52 @@ public: int removeFd(int fd); /** + * Enqueues a message to be processed by the specified handler. + * + * The handler must not be null. + * This method can be called on any thread. + */ + void sendMessage(const sp<MessageHandler>& handler, const Message& message); + + /** + * Enqueues a message to be processed by the specified handler after all pending messages + * after the specified delay. + * + * The time delay is specified in uptime nanoseconds. + * The handler must not be null. + * This method can be called on any thread. + */ + void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, + const Message& message); + + /** + * Enqueues a message to be processed by the specified handler after all pending messages + * at the specified time. + * + * The time is specified in uptime nanoseconds. + * The handler must not be null. + * This method can be called on any thread. + */ + void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, + const Message& message); + + /** + * Removes all messages for the specified handler from the queue. + * + * The handler must not be null. + * This method can be called on any thread. + */ + void removeMessages(const sp<MessageHandler>& handler); + + /** + * Removes all messages of a particular type for the specified handler from the queue. + * + * The handler must not be null. + * This method can be called on any thread. + */ + void removeMessages(const sp<MessageHandler>& handler, int what); + + /** * Prepares a looper associated with the calling thread, and returns it. * If the thread already has a looper, it is returned. Otherwise, a new * one is created, associated with the thread, and returned. @@ -201,12 +292,27 @@ private: Request request; }; + struct MessageEnvelope { + MessageEnvelope() : uptime(0) { } + + MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler, + const Message& message) : uptime(uptime), handler(handler), message(message) { + } + + nsecs_t uptime; + sp<MessageHandler> handler; + Message message; + }; + const bool mAllowNonCallbacks; // immutable int mWakeReadPipeFd; // immutable int mWakeWritePipeFd; // immutable Mutex mLock; + Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock + bool mSendingMessage; // guarded by mLock + #ifdef LOOPER_USES_EPOLL int mEpollFd; // immutable @@ -256,6 +362,7 @@ private: // it runs on a single thread. Vector<Response> mResponses; size_t mResponseIndex; + nsecs_t mNextMessageUptime; // set to LLONG_MAX when none int pollInner(int timeoutMillis); void awoken(); diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp index 8b6300a..600a951 100644 --- a/libs/ui/Keyboard.cpp +++ b/libs/ui/Keyboard.cpp @@ -322,5 +322,26 @@ int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { } } +bool isMetaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + case AKEYCODE_ALT_RIGHT: + case AKEYCODE_SHIFT_LEFT: + case AKEYCODE_SHIFT_RIGHT: + case AKEYCODE_SYM: + case AKEYCODE_FUNCTION: + case AKEYCODE_CTRL_LEFT: + case AKEYCODE_CTRL_RIGHT: + case AKEYCODE_META_LEFT: + case AKEYCODE_META_RIGHT: + case AKEYCODE_CAPS_LOCK: + case AKEYCODE_NUM_LOCK: + case AKEYCODE_SCROLL_LOCK: + return true; + default: + return false; + } +} + } // namespace android diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index a5363d6..18f858b 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -19,10 +19,27 @@ #include <unistd.h> #include <fcntl.h> +#include <limits.h> namespace android { +// --- WeakMessageHandler --- + +WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) : + mHandler(handler) { +} + +void WeakMessageHandler::handleMessage(const Message& message) { + sp<MessageHandler> handler = mHandler.promote(); + if (handler != NULL) { + handler->handleMessage(message); + } +} + + +// --- Looper --- + #ifdef LOOPER_USES_EPOLL // Hint for number of file descriptors to be associated with the epoll instance. static const int EPOLL_SIZE_HINT = 8; @@ -35,8 +52,8 @@ static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; static pthread_key_t gTLSKey = 0; Looper::Looper(bool allowNonCallbacks) : - mAllowNonCallbacks(allowNonCallbacks), - mResponseIndex(0) { + mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), + mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { int wakeFds[2]; int result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); @@ -161,17 +178,21 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa for (;;) { while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); - if (! response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (!callback) { + int ident = response.request.ident; + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - returning signalled identifier %d: " - "fd=%d, events=0x%x, data=%p", this, - response.request.ident, response.request.fd, - response.events, response.request.data); + "fd=%d, events=0x%x, data=%p", + this, ident, fd, events, data); #endif - if (outFd != NULL) *outFd = response.request.fd; - if (outEvents != NULL) *outEvents = response.events; - if (outData != NULL) *outData = response.request.data; - return response.request.ident; + if (outFd != NULL) *outFd = fd; + if (outEvents != NULL) *outEvents = events; + if (outData != NULL) *outData = data; + return ident; } } @@ -194,6 +215,25 @@ int Looper::pollInner(int timeoutMillis) { LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); #endif + // Adjust the timeout based on when the next message is due. + if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (mNextMessageUptime <= now) { + timeoutMillis = 0; + } else { + uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL; + if (delay < INT_MAX + && (timeoutMillis < 0 || int(delay) < timeoutMillis)) { + timeoutMillis = int(delay); + } + } +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", + this, mNextMessageUptime - now, timeoutMillis); +#endif + } + + // Poll. int result = ALOOPER_POLL_WAKE; mResponses.clear(); mResponseIndex = 0; @@ -205,7 +245,6 @@ int Looper::pollInner(int timeoutMillis) { #ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); - bool acquiredLock = false; #else // Wait for wakeAndLock() waiters to run then set mPolling to true. mLock.lock(); @@ -219,16 +258,20 @@ int Looper::pollInner(int timeoutMillis) { int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); #endif + // Acquire lock. + mLock.lock(); + + // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { goto Done; } - LOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } + // Check for poll timeout. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); @@ -237,6 +280,7 @@ int Looper::pollInner(int timeoutMillis) { goto Done; } + // Handle all events. #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif @@ -252,11 +296,6 @@ int Looper::pollInner(int timeoutMillis) { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { - if (! acquiredLock) { - mLock.lock(); - acquiredLock = true; - } - ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; @@ -271,9 +310,6 @@ int Looper::pollInner(int timeoutMillis) { } } } - if (acquiredLock) { - mLock.unlock(); - } Done: ; #else for (size_t i = 0; i < requestedCount; i++) { @@ -301,15 +337,12 @@ Done: ; } } } - Done: // Set mPolling to false and wake up the wakeAndLock() waiters. - mLock.lock(); mPolling = false; if (mWaiters != 0) { mAwake.broadcast(); } - mLock.unlock(); #endif #ifdef LOOPER_STATISTICS @@ -335,19 +368,59 @@ Done: } #endif + // Invoke pending message callbacks. + mNextMessageUptime = LLONG_MAX; + while (mMessageEnvelopes.size() != 0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0); + if (messageEnvelope.uptime <= now) { + // Remove the envelope from the list. + // We keep a strong reference to the handler until the call to handleMessage + // finishes. Then we drop it so that the handler can be deleted *before* + // we reacquire our lock. + { // obtain handler + sp<MessageHandler> handler = messageEnvelope.handler; + Message message = messageEnvelope.message; + mMessageEnvelopes.removeAt(0); + mSendingMessage = true; + mLock.unlock(); + +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", + this, handler.get(), message.what); +#endif + handler->handleMessage(message); + } // release handler + + mLock.lock(); + mSendingMessage = false; + result = ALOOPER_POLL_CALLBACK; + } else { + // The last message left at the head of the queue determines the next wakeup time. + mNextMessageUptime = messageEnvelope.uptime; + break; + } + } + + // Release lock. + mLock.unlock(); + + // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { const Response& response = mResponses.itemAt(i); - if (response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (callback) { + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this, - response.request.fd, response.events, response.request.data); + LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", + this, callback, fd, events, data); #endif - int callbackResult = response.request.callback( - response.request.fd, response.events, response.request.data); + int callbackResult = callback(fd, events, data); if (callbackResult == 0) { - removeFd(response.request.fd); + removeFd(fd); } - result = ALOOPER_POLL_CALLBACK; } } @@ -593,4 +666,83 @@ void Looper::wakeAndLock() { } #endif +void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) { + sendMessageAtTime(LLONG_MIN, handler, message); +} + +void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, + const Message& message) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sendMessageAtTime(now + uptimeDelay, handler, message); +} + +void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, + const Message& message) { +#if DEBUG_CALLBACKS + LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", + this, uptime, handler.get(), message.what); +#endif + + size_t i = 0; + { // acquire lock + AutoMutex _l(mLock); + + size_t messageCount = mMessageEnvelopes.size(); + while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { + i += 1; + } + + MessageEnvelope messageEnvelope(uptime, handler, message); + mMessageEnvelopes.insertAt(messageEnvelope, i, 1); + + // Optimization: If the Looper is currently sending a message, then we can skip + // the call to wake() because the next thing the Looper will do after processing + // messages is to decide when the next wakeup time should be. In fact, it does + // not even matter whether this code is running on the Looper thread. + if (mSendingMessage) { + return; + } + } // release lock + + // Wake the poll loop only when we enqueue a new message at the head. + if (i == 0) { + wake(); + } +} + +void Looper::removeMessages(const sp<MessageHandler>& handler) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p", this, handler.get()); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + +void Looper::removeMessages(const sp<MessageHandler>& handler, int what) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler + && messageEnvelope.message.what == what) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + } // namespace android diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp index cea1313..8bf2ba2 100644 --- a/libs/utils/tests/Looper_test.cpp +++ b/libs/utils/tests/Looper_test.cpp @@ -16,6 +16,13 @@ namespace android { +enum { + MSG_TEST1 = 1, + MSG_TEST2 = 2, + MSG_TEST3 = 3, + MSG_TEST4 = 4, +}; + class DelayedWake : public DelayedTask { sp<Looper> mLooper; @@ -82,6 +89,15 @@ protected: } }; +class StubMessageHandler : public MessageHandler { +public: + Vector<Message> messages; + + virtual void handleMessage(const Message& message) { + messages.push(message); + } +}; + class LooperTest : public testing::Test { protected: sp<Looper> mLooper; @@ -421,5 +437,257 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInv << "replacement handler callback should be invoked"; } +TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) { + sp<StubMessageHandler> handler1 = new StubMessageHandler(); + sp<StubMessageHandler> handler2 = new StubMessageHandler(); + mLooper->sendMessage(handler1, Message(MSG_TEST1)); + mLooper->sendMessage(handler2, Message(MSG_TEST2)); + mLooper->sendMessage(handler1, Message(MSG_TEST3)); + mLooper->sendMessage(handler1, Message(MSG_TEST4)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(3), handler1->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler1->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST3, handler1->messages[1].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler1->messages[2].what) + << "handled message"; + EXPECT_EQ(size_t(1), handler2->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST2, handler2->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->removeMessages(handler); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->sendMessage(handler, Message(MSG_TEST4)); + mLooper->removeMessages(handler, MSG_TEST3); + mLooper->removeMessages(handler, MSG_TEST1); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no messages to handle"; + EXPECT_EQ(MSG_TEST2, handler->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler->messages[1].what) + << "handled message"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no more messages to handle"; +} } // namespace android diff --git a/native/include/android/looper.h b/native/include/android/looper.h index a9d8426..24e3967 100644 --- a/native/include/android/looper.h +++ b/native/include/android/looper.h @@ -148,7 +148,8 @@ enum { /** * For callback-based event loops, this is the prototype of the function - * that is called. It is given the file descriptor it is associated with, + * that is called when a file descriptor event occurs. + * It is given the file descriptor it is associated with, * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT), * and the data pointer that was originally supplied. * diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index a865d9f..c3c143c 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -399,6 +399,17 @@ bool InputReader::shouldDropVirtualKey(nsecs_t now, } } +void InputReader::fadePointer() { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->fadePointer(); + } + } // release device registry reader lock +} + void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { { // acquire state lock AutoMutex _l(mStateLock); @@ -695,6 +706,14 @@ int32_t InputDevice::getMetaState() { return result; } +void InputDevice::fadePointer() { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->fadePointer(); + } +} + // --- InputMapper --- @@ -739,6 +758,9 @@ int32_t InputMapper::getMetaState() { return 0; } +void InputMapper::fadePointer() { +} + void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, const RawAbsoluteAxisInfo& axis, const char* name) { if (axis.valid) { @@ -967,6 +989,10 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, getContext()->updateGlobalMetaState(); } + if (down && !isMetaKey(keyCode)) { + getContext()->fadePointer(); + } + if (policyFlags & POLICY_FLAG_FUNCTION) { newMetaState |= AMETA_FUNCTION_ON; } @@ -1348,6 +1374,9 @@ void CursorInputMapper::sync(nsecs_t when) { } else { hscroll = 0; } + if (hscroll != 0 || vscroll != 0) { + mPointerController->unfade(); + } } // release lock int32_t metaState = mContext->getGlobalMetaState(); @@ -1376,6 +1405,13 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod } } +void CursorInputMapper::fadePointer() { + { // acquire lock + AutoMutex _l(mLock); + mPointerController->fade(); + } // release lock +} + // --- TouchInputMapper --- @@ -2275,7 +2311,6 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { uint32_t policyFlags = 0; // Preprocess pointer data. - if (mParameters.useBadTouchFilter) { if (applyBadTouchFilter()) { havePointerIds = false; @@ -2303,8 +2338,12 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { savedTouch = & mCurrentTouch; } - // Process touches and virtual keys. + // Hide the pointer on an initial down. + if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) { + getContext()->fadePointer(); + } + // Process touches and virtual keys. TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); if (touchResult == DISPATCH_TOUCH) { detectGestures(when); @@ -2312,7 +2351,6 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { } // Copy current touch to last touch in preparation for the next cycle. - if (touchResult == DROP_STROKE) { mLastTouch.clear(); } else { diff --git a/services/input/InputReader.h b/services/input/InputReader.h index cf41535..b344ffe 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -157,6 +157,8 @@ public: virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, int32_t scanCode) = 0; + virtual void fadePointer() = 0; + virtual InputReaderPolicyInterface* getPolicy() = 0; virtual InputDispatcherInterface* getDispatcher() = 0; virtual EventHubInterface* getEventHub() = 0; @@ -241,6 +243,8 @@ private: virtual void updateGlobalMetaState(); virtual int32_t getGlobalMetaState(); + virtual void fadePointer(); + InputConfiguration mInputConfiguration; void updateInputConfiguration(); @@ -299,6 +303,8 @@ public: int32_t getMetaState(); + void fadePointer(); + inline const PropertyMap& getConfiguration() { return mConfiguration; } @@ -351,6 +357,8 @@ public: virtual int32_t getMetaState(); + virtual void fadePointer(); + protected: InputDevice* mDevice; InputReaderContext* mContext; @@ -459,6 +467,8 @@ public: virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual void fadePointer(); + private: // Amount that trackball needs to move in order to generate a key event. static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp index 92af51e..954872b 100644 --- a/services/input/PointerController.cpp +++ b/services/input/PointerController.cpp @@ -35,8 +35,22 @@ namespace android { // --- PointerController --- -PointerController::PointerController(int32_t pointerLayer) : - mPointerLayer(pointerLayer) { +// Time to wait before starting the fade when the pointer is inactive. +static const nsecs_t INACTIVITY_FADE_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds +static const nsecs_t INACTIVITY_FADE_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds + +// Time to spend fading out the pointer completely. +static const nsecs_t FADE_DURATION = 500 * 1000000LL; // 500 ms + +// Time to wait between frames. +static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60; + +// Amount to subtract from alpha per frame. +static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION; + + +PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) : + mLooper(looper), mPointerLayer(pointerLayer) { AutoMutex _l(mLock); mLocked.displayWidth = -1; @@ -51,12 +65,19 @@ PointerController::PointerController(int32_t pointerLayer) : mLocked.iconHotSpotX = 0; mLocked.iconHotSpotY = 0; + mLocked.fadeAlpha = 1; + mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL; + mLocked.wantVisible = false; mLocked.visible = false; mLocked.drawn = false; + + mHandler = new WeakMessageHandler(this); } PointerController::~PointerController() { + mLooper->removeMessages(mHandler); + if (mSurfaceControl != NULL) { mSurfaceControl->clear(); mSurfaceControl.clear(); @@ -120,7 +141,7 @@ void PointerController::setButtonState(uint32_t buttonState) { if (mLocked.buttonState != buttonState) { mLocked.buttonState = buttonState; - mLocked.wantVisible = true; + unfadeBeforeUpdateLocked(); updateLocked(); } } @@ -157,7 +178,7 @@ void PointerController::setPositionLocked(float x, float y) { } else { mLocked.pointerY = y; } - mLocked.wantVisible = true; + unfadeBeforeUpdateLocked(); updateLocked(); } } @@ -169,6 +190,29 @@ void PointerController::getPosition(float* outX, float* outY) const { *outY = mLocked.pointerY; } +void PointerController::fade() { + AutoMutex _l(mLock); + + startFadeLocked(); +} + +void PointerController::unfade() { + AutoMutex _l(mLock); + + if (unfadeBeforeUpdateLocked()) { + updateLocked(); + } +} + +void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay) { + AutoMutex _l(mLock); + + if (mLocked.inactivityFadeDelay != inactivityFadeDelay) { + mLocked.inactivityFadeDelay = inactivityFadeDelay; + startInactivityFadeDelayLocked(); + } +} + void PointerController::updateLocked() { bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap; @@ -201,6 +245,12 @@ void PointerController::updateLocked() { goto CloseTransaction; } + status = mSurfaceControl->setAlpha(mLocked.fadeAlpha); + if (status) { + LOGE("Error %d setting pointer surface alpha.", status); + goto CloseTransaction; + } + if (!mLocked.visible) { status = mSurfaceControl->setLayer(mPointerLayer); if (status) { @@ -412,4 +462,64 @@ bool PointerController::resizeSurfaceLocked(int32_t width, int32_t height) { return true; } +void PointerController::handleMessage(const Message& message) { + switch (message.what) { + case MSG_FADE_STEP: { + AutoMutex _l(mLock); + fadeStepLocked(); + break; + } + } +} + +bool PointerController::unfadeBeforeUpdateLocked() { + sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked()); + + if (isFadingLocked()) { + mLocked.wantVisible = true; + mLocked.fadeAlpha = 1; + return true; // update required to effect the unfade + } + return false; // update not required +} + +void PointerController::startFadeLocked() { + if (!isFadingLocked()) { + sendFadeStepMessageDelayedLocked(0); + } +} + +void PointerController::startInactivityFadeDelayLocked() { + if (!isFadingLocked()) { + sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked()); + } +} + +void PointerController::fadeStepLocked() { + if (mLocked.wantVisible) { + mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME; + if (mLocked.fadeAlpha < 0) { + mLocked.fadeAlpha = 0; + mLocked.wantVisible = false; + } else { + sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL); + } + updateLocked(); + } +} + +bool PointerController::isFadingLocked() { + return !mLocked.wantVisible || mLocked.fadeAlpha != 1; +} + +nsecs_t PointerController::getInactivityFadeDelayTimeLocked() { + return mLocked.inactivityFadeDelay == INACTIVITY_FADE_DELAY_SHORT + ? INACTIVITY_FADE_DELAY_TIME_SHORT : INACTIVITY_FADE_DELAY_TIME_NORMAL; +} + +void PointerController::sendFadeStepMessageDelayedLocked(nsecs_t delayTime) { + mLooper->removeMessages(mHandler, MSG_FADE_STEP); + mLooper->sendMessageDelayed(delayTime, mHandler, Message(MSG_FADE_STEP)); +} + } // namespace android diff --git a/services/input/PointerController.h b/services/input/PointerController.h index a2a9955..e28dd7d 100644 --- a/services/input/PointerController.h +++ b/services/input/PointerController.h @@ -18,7 +18,9 @@ #define _UI_POINTER_CONTROLLER_H #include <ui/DisplayInfo.h> +#include <ui/Input.h> #include <utils/RefBase.h> +#include <utils/Looper.h> #include <utils/String8.h> #include <surfaceflinger/Surface.h> @@ -64,6 +66,12 @@ public: /* Gets the absolute location of the pointer. */ virtual void getPosition(float* outX, float* outY) const = 0; + + /* Fades the pointer out now. */ + virtual void fade() = 0; + + /* Makes the pointer visible if it has faded out. */ + virtual void unfade() = 0; }; @@ -72,12 +80,17 @@ public: * * Handles pointer acceleration and animation. */ -class PointerController : public PointerControllerInterface { +class PointerController : public PointerControllerInterface, public MessageHandler { protected: virtual ~PointerController(); public: - PointerController(int32_t pointerLayer); + enum InactivityFadeDelay { + INACTIVITY_FADE_DELAY_NORMAL = 0, + INACTIVITY_FADE_DELAY_SHORT = 1, + }; + + PointerController(const sp<Looper>& looper, int32_t pointerLayer); virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -86,14 +99,22 @@ public: virtual uint32_t getButtonState() const; virtual void setPosition(float x, float y); virtual void getPosition(float* outX, float* outY) const; + virtual void fade(); + virtual void unfade(); void setDisplaySize(int32_t width, int32_t height); void setDisplayOrientation(int32_t orientation); void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY); + void setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay); private: + enum { + MSG_FADE_STEP = 0, + }; + mutable Mutex mLock; + sp<Looper> mLooper; int32_t mPointerLayer; sp<SurfaceComposerClient> mSurfaceComposerClient; sp<SurfaceControl> mSurfaceControl; @@ -111,17 +132,31 @@ private: float iconHotSpotX; float iconHotSpotY; + float fadeAlpha; + InactivityFadeDelay inactivityFadeDelay; + bool wantVisible; bool visible; bool drawn; } mLocked; + sp<WeakMessageHandler> mHandler; + bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; void setPositionLocked(float x, float y); void updateLocked(); bool createSurfaceIfNeededLocked(); bool drawPointerIfNeededLocked(); bool resizeSurfaceLocked(int32_t width, int32_t height); + + void handleMessage(const Message& message); + bool unfadeBeforeUpdateLocked(); + void startFadeLocked(); + void startInactivityFadeDelayLocked(); + void fadeStepLocked(); + bool isFadingLocked(); + nsecs_t getInactivityFadeDelayTimeLocked(); + void sendFadeStepMessageDelayedLocked(nsecs_t delayTime); }; } // namespace android diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index fac71bb..864241e 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -79,6 +79,12 @@ private: *outX = 0; *outY = 0; } + + virtual void fade() { + } + + virtual void unfade() { + } }; @@ -743,6 +749,9 @@ private: InputDevice* device, int32_t keyCode, int32_t scanCode) { return false; } + + virtual void fadePointer() { + } }; @@ -875,6 +884,9 @@ private: virtual int32_t getMetaState() { return mMetaState; } + + virtual void fadePointer() { + } }; diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java index 80a2a96..326eca7 100644 --- a/services/java/com/android/server/wm/InputManager.java +++ b/services/java/com/android/server/wm/InputManager.java @@ -19,7 +19,6 @@ package com.android.server.wm; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; import android.content.Context; import android.content.pm.PackageManager; @@ -31,6 +30,8 @@ import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Environment; +import android.os.Looper; +import android.os.MessageQueue; import android.os.SystemProperties; import android.util.Slog; import android.util.Xml; @@ -41,13 +42,10 @@ import android.view.KeyEvent; import android.view.Surface; import android.view.WindowManager; -import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; -import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; @@ -63,7 +61,7 @@ public class InputManager { private final Context mContext; private final WindowManagerService mWindowManagerService; - private static native void nativeInit(Callbacks callbacks); + private static native void nativeInit(Callbacks callbacks, MessageQueue messageQueue); private static native void nativeStart(); private static native void nativeSetDisplaySize(int displayId, int width, int height); private static native void nativeSetDisplayOrientation(int displayId, int rotation); @@ -83,6 +81,7 @@ public class InputManager { int injectorPid, int injectorUid, int syncMode, int timeoutMillis); private static native void nativeSetInputWindows(InputWindow[] windows); private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen); + private static native void nativeSetSystemUiVisibility(int visibility); private static native void nativeSetFocusedApplication(InputApplication application); private static native InputDevice nativeGetInputDevice(int deviceId); private static native void nativeGetInputConfiguration(Configuration configuration); @@ -120,15 +119,12 @@ public class InputManager { public InputManager(Context context, WindowManagerService windowManagerService) { this.mContext = context; this.mWindowManagerService = windowManagerService; - this.mCallbacks = new Callbacks(); - - init(); - } - - private void init() { + + Looper looper = windowManagerService.mH.getLooper(); + Slog.i(TAG, "Initializing input manager"); - nativeInit(mCallbacks); + nativeInit(mCallbacks, looper.getQueue()); } public void start() { @@ -338,7 +334,11 @@ public class InputManager { public void setInputDispatchMode(boolean enabled, boolean frozen) { nativeSetInputDispatchMode(enabled, frozen); } - + + public void setSystemUiVisibility(int visibility) { + nativeSetSystemUiVisibility(visibility); + } + /** * Atomically transfers touch focus from one window to another as identified by * their input channels. It is possible for multiple windows to have @@ -361,7 +361,7 @@ public class InputManager { } return nativeTransferTouchFocus(fromChannel, toChannel); } - + public void dump(PrintWriter pw) { String dumpStr = nativeDump(); if (dumpStr != null) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index e3218c8..15269dc 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -8412,6 +8412,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void statusBarVisibilityChanged(int visibility) { + mInputManager.setSystemUiVisibility(visibility); synchronized (mWindowMap) { final int N = mWindows.size(); for (int i = 0; i < N; i++) { diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 0a50ff8..5ef234a 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -31,11 +31,13 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> +#include <utils/Looper.h> #include <utils/threads.h> #include <input/InputManager.h> #include <input/PointerController.h> +#include <android_os_MessageQueue.h> #include <android_view_KeyEvent.h> #include <android_view_MotionEvent.h> #include <android_view_InputChannel.h> @@ -136,7 +138,7 @@ protected: virtual ~NativeInputManager(); public: - NativeInputManager(jobject callbacksObj); + NativeInputManager(jobject callbacksObj, const sp<Looper>& looper); inline sp<InputManager> getInputManager() const { return mInputManager; } @@ -152,6 +154,7 @@ public: void setInputWindows(JNIEnv* env, jobjectArray windowObjArray); void setFocusedApplication(JNIEnv* env, jobject applicationObj); void setInputDispatchMode(bool enabled, bool frozen); + void setSystemUiVisibility(int32_t visibility); /* --- InputReaderPolicyInterface implementation --- */ @@ -188,6 +191,7 @@ private: sp<InputManager> mInputManager; jobject mCallbacksObj; + sp<Looper> mLooper; // Cached filtering policies. int32_t mFilterTouchEvents; @@ -203,10 +207,15 @@ private: int32_t displayWidth, displayHeight; // -1 when initialized int32_t displayOrientation; + // System UI visibility. + int32_t systemUiVisibility; + // Pointer controller singleton, created and destroyed as needed. wp<PointerController> pointerController; } mLocked; + void updateInactivityFadeDelayLocked(const sp<PointerController>& controller); + // Power manager interactions. bool isScreenOn(); bool isScreenBright(); @@ -220,9 +229,10 @@ private: -NativeInputManager::NativeInputManager(jobject callbacksObj) : - mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1), - mMaxEventsPerSecond(-1) { +NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& looper) : + mLooper(looper), + mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1), + mMaxEventsPerSecond(-1) { JNIEnv* env = jniEnv(); mCallbacksObj = env->NewGlobalRef(callbacksObj); @@ -232,6 +242,8 @@ NativeInputManager::NativeInputManager(jobject callbacksObj) : mLocked.displayWidth = -1; mLocked.displayHeight = -1; mLocked.displayOrientation = ROTATION_0; + + mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; } sp<EventHub> eventHub = new EventHub(); @@ -408,7 +420,7 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 layer = -1; } - controller = new PointerController(layer); + controller = new PointerController(mLooper, layer); mLocked.pointerController = controller; controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight); @@ -428,6 +440,8 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 } env->DeleteLocalRef(iconObj); } + + updateInactivityFadeDelayLocked(controller); } return controller; } @@ -571,6 +585,26 @@ void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) { mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen); } +void NativeInputManager::setSystemUiVisibility(int32_t visibility) { + AutoMutex _l(mLock); + + if (mLocked.systemUiVisibility != visibility) { + mLocked.systemUiVisibility = visibility; + + sp<PointerController> controller = mLocked.pointerController.promote(); + if (controller != NULL) { + updateInactivityFadeDelayLocked(controller); + } + } +} + +void NativeInputManager::updateInactivityFadeDelayLocked(const sp<PointerController>& controller) { + bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; + controller->setInactivityFadeDelay(lightsOut + ? PointerController::INACTIVITY_FADE_DELAY_SHORT + : PointerController::INACTIVITY_FADE_DELAY_NORMAL); +} + bool NativeInputManager::isScreenOn() { return android_server_PowerManagerService_isScreenOn(); } @@ -751,9 +785,10 @@ static bool checkInputManagerUnitialized(JNIEnv* env) { } static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz, - jobject callbacks) { + jobject callbacks, jobject messageQueueObj) { if (gNativeInputManager == NULL) { - gNativeInputManager = new NativeInputManager(callbacks); + sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj); + gNativeInputManager = new NativeInputManager(callbacks, looper); } else { LOGE("Input manager already initialized."); jniThrowRuntimeException(env, "Input manager already initialized."); @@ -972,6 +1007,15 @@ static void android_server_InputManager_nativeSetInputDispatchMode(JNIEnv* env, gNativeInputManager->setInputDispatchMode(enabled, frozen); } +static void android_server_InputManager_nativeSetSystemUiVisibility(JNIEnv* env, + jclass clazz, jint visibility) { + if (checkInputManagerUnitialized(env)) { + return; + } + + gNativeInputManager->setSystemUiVisibility(visibility); +} + static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env, jclass clazz, jint deviceId) { if (checkInputManagerUnitialized(env)) { @@ -1079,7 +1123,7 @@ static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) static JNINativeMethod gInputManagerMethods[] = { /* name, signature, funcPtr */ - { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;)V", + { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V", (void*) android_server_InputManager_nativeInit }, { "nativeStart", "()V", (void*) android_server_InputManager_nativeStart }, @@ -1108,6 +1152,8 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeSetFocusedApplication }, { "nativeSetInputDispatchMode", "(ZZ)V", (void*) android_server_InputManager_nativeSetInputDispatchMode }, + { "nativeSetSystemUiVisibility", "(I)V", + (void*) android_server_InputManager_nativeSetSystemUiVisibility }, { "nativeGetInputDevice", "(I)Landroid/view/InputDevice;", (void*) android_server_InputManager_nativeGetInputDevice }, { "nativeGetInputDeviceIds", "()[I", |