summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-03-02 14:41:58 -0800
committerJeff Brown <jeffbrown@google.com>2011-03-02 15:37:57 -0800
commit05dc66ada6b61a6bdf806ffaa62617ac5394695d (patch)
treea7e395476241706de31498185a5c2ccb979bcb54
parente43111fad31ad8e36a66df52a8c6002799996413 (diff)
downloadframeworks_base-05dc66ada6b61a6bdf806ffaa62617ac5394695d.zip
frameworks_base-05dc66ada6b61a6bdf806ffaa62617ac5394695d.tar.gz
frameworks_base-05dc66ada6b61a6bdf806ffaa62617ac5394695d.tar.bz2
Fade out the mouse pointer after inactivity or other events.
Fades out the mouse pointer: - after 15 seconds of inactivity normally - after 3 seconds of inactivity in lights out mode - after a non-modifier key down - after a touch down Extended the native Looper to support enqueuing time delayed messages. This is used by the PointerController to control pointer fade timing. Change-Id: I87792fea7dbe2d9376c78cf354fe3189a484d9da
-rw-r--r--core/java/android/view/View.java2
-rw-r--r--include/ui/Input.h8
-rw-r--r--include/ui/Keyboard.h5
-rw-r--r--include/utils/Looper.h107
-rw-r--r--libs/ui/Keyboard.cpp21
-rw-r--r--libs/utils/Looper.cpp212
-rw-r--r--libs/utils/tests/Looper_test.cpp268
-rw-r--r--native/include/android/looper.h3
-rw-r--r--services/input/InputReader.cpp44
-rw-r--r--services/input/InputReader.h10
-rw-r--r--services/input/PointerController.cpp118
-rw-r--r--services/input/PointerController.h39
-rw-r--r--services/input/tests/InputReader_test.cpp12
-rw-r--r--services/java/com/android/server/wm/InputManager.java28
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java1
-rw-r--r--services/jni/com_android_server_InputManager.cpp62
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",