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
This commit is contained in:
parent
76048aeee7
commit
80f3e7cc15
@ -51,6 +51,14 @@ enum {
|
|||||||
AINPUT_SOURCE_SWITCH = 0x80000000,
|
AINPUT_SOURCE_SWITCH = 0x80000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
* Maximum number of pointers supported per motion event.
|
||||||
* Smallest number of pointers is 1.
|
* Smallest number of pointers is 1.
|
||||||
|
@ -127,6 +127,11 @@ extern const char* getAxisLabel(int32_t axisId);
|
|||||||
*/
|
*/
|
||||||
extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
|
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
|
} // namespace android
|
||||||
|
|
||||||
#endif // _UI_KEYBOARD_H
|
#endif // _UI_KEYBOARD_H
|
||||||
|
@ -44,6 +44,51 @@ struct ALooper {
|
|||||||
|
|
||||||
namespace android {
|
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
|
* A polling loop that supports monitoring file descriptor events, optionally
|
||||||
* using callbacks. The implementation uses epoll() internally.
|
* using callbacks. The implementation uses epoll() internally.
|
||||||
@ -165,6 +210,52 @@ public:
|
|||||||
*/
|
*/
|
||||||
int removeFd(int fd);
|
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.
|
* Prepares a looper associated with the calling thread, and returns it.
|
||||||
* If the thread already has a looper, it is returned. Otherwise, a new
|
* If the thread already has a looper, it is returned. Otherwise, a new
|
||||||
@ -201,12 +292,27 @@ private:
|
|||||||
Request request;
|
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
|
const bool mAllowNonCallbacks; // immutable
|
||||||
|
|
||||||
int mWakeReadPipeFd; // immutable
|
int mWakeReadPipeFd; // immutable
|
||||||
int mWakeWritePipeFd; // immutable
|
int mWakeWritePipeFd; // immutable
|
||||||
Mutex mLock;
|
Mutex mLock;
|
||||||
|
|
||||||
|
Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
|
||||||
|
bool mSendingMessage; // guarded by mLock
|
||||||
|
|
||||||
#ifdef LOOPER_USES_EPOLL
|
#ifdef LOOPER_USES_EPOLL
|
||||||
int mEpollFd; // immutable
|
int mEpollFd; // immutable
|
||||||
|
|
||||||
@ -256,6 +362,7 @@ private:
|
|||||||
// it runs on a single thread.
|
// it runs on a single thread.
|
||||||
Vector<Response> mResponses;
|
Vector<Response> mResponses;
|
||||||
size_t mResponseIndex;
|
size_t mResponseIndex;
|
||||||
|
nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
|
||||||
|
|
||||||
int pollInner(int timeoutMillis);
|
int pollInner(int timeoutMillis);
|
||||||
void awoken();
|
void awoken();
|
||||||
|
@ -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
|
} // namespace android
|
||||||
|
@ -19,10 +19,27 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
|
||||||
namespace android {
|
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
|
#ifdef LOOPER_USES_EPOLL
|
||||||
// Hint for number of file descriptors to be associated with the epoll instance.
|
// Hint for number of file descriptors to be associated with the epoll instance.
|
||||||
static const int EPOLL_SIZE_HINT = 8;
|
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;
|
static pthread_key_t gTLSKey = 0;
|
||||||
|
|
||||||
Looper::Looper(bool allowNonCallbacks) :
|
Looper::Looper(bool allowNonCallbacks) :
|
||||||
mAllowNonCallbacks(allowNonCallbacks),
|
mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
|
||||||
mResponseIndex(0) {
|
mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
|
||||||
int wakeFds[2];
|
int wakeFds[2];
|
||||||
int result = pipe(wakeFds);
|
int result = pipe(wakeFds);
|
||||||
LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno);
|
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 (;;) {
|
for (;;) {
|
||||||
while (mResponseIndex < mResponses.size()) {
|
while (mResponseIndex < mResponses.size()) {
|
||||||
const Response& response = mResponses.itemAt(mResponseIndex++);
|
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
|
#if DEBUG_POLL_AND_WAKE
|
||||||
LOGD("%p ~ pollOnce - returning signalled identifier %d: "
|
LOGD("%p ~ pollOnce - returning signalled identifier %d: "
|
||||||
"fd=%d, events=0x%x, data=%p", this,
|
"fd=%d, events=0x%x, data=%p",
|
||||||
response.request.ident, response.request.fd,
|
this, ident, fd, events, data);
|
||||||
response.events, response.request.data);
|
|
||||||
#endif
|
#endif
|
||||||
if (outFd != NULL) *outFd = response.request.fd;
|
if (outFd != NULL) *outFd = fd;
|
||||||
if (outEvents != NULL) *outEvents = response.events;
|
if (outEvents != NULL) *outEvents = events;
|
||||||
if (outData != NULL) *outData = response.request.data;
|
if (outData != NULL) *outData = data;
|
||||||
return response.request.ident;
|
return ident;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,6 +215,25 @@ int Looper::pollInner(int timeoutMillis) {
|
|||||||
LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
|
LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
|
||||||
#endif
|
#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;
|
int result = ALOOPER_POLL_WAKE;
|
||||||
mResponses.clear();
|
mResponses.clear();
|
||||||
mResponseIndex = 0;
|
mResponseIndex = 0;
|
||||||
@ -205,7 +245,6 @@ int Looper::pollInner(int timeoutMillis) {
|
|||||||
#ifdef LOOPER_USES_EPOLL
|
#ifdef LOOPER_USES_EPOLL
|
||||||
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
|
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
|
||||||
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
|
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
|
||||||
bool acquiredLock = false;
|
|
||||||
#else
|
#else
|
||||||
// Wait for wakeAndLock() waiters to run then set mPolling to true.
|
// Wait for wakeAndLock() waiters to run then set mPolling to true.
|
||||||
mLock.lock();
|
mLock.lock();
|
||||||
@ -219,16 +258,20 @@ int Looper::pollInner(int timeoutMillis) {
|
|||||||
int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
|
int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Acquire lock.
|
||||||
|
mLock.lock();
|
||||||
|
|
||||||
|
// Check for poll error.
|
||||||
if (eventCount < 0) {
|
if (eventCount < 0) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGW("Poll failed with an unexpected error, errno=%d", errno);
|
LOGW("Poll failed with an unexpected error, errno=%d", errno);
|
||||||
result = ALOOPER_POLL_ERROR;
|
result = ALOOPER_POLL_ERROR;
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for poll timeout.
|
||||||
if (eventCount == 0) {
|
if (eventCount == 0) {
|
||||||
#if DEBUG_POLL_AND_WAKE
|
#if DEBUG_POLL_AND_WAKE
|
||||||
LOGD("%p ~ pollOnce - timeout", this);
|
LOGD("%p ~ pollOnce - timeout", this);
|
||||||
@ -237,6 +280,7 @@ int Looper::pollInner(int timeoutMillis) {
|
|||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle all events.
|
||||||
#if DEBUG_POLL_AND_WAKE
|
#if DEBUG_POLL_AND_WAKE
|
||||||
LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
|
LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
|
||||||
#endif
|
#endif
|
||||||
@ -252,11 +296,6 @@ int Looper::pollInner(int timeoutMillis) {
|
|||||||
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
|
LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (! acquiredLock) {
|
|
||||||
mLock.lock();
|
|
||||||
acquiredLock = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t requestIndex = mRequests.indexOfKey(fd);
|
ssize_t requestIndex = mRequests.indexOfKey(fd);
|
||||||
if (requestIndex >= 0) {
|
if (requestIndex >= 0) {
|
||||||
int events = 0;
|
int events = 0;
|
||||||
@ -271,9 +310,6 @@ int Looper::pollInner(int timeoutMillis) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (acquiredLock) {
|
|
||||||
mLock.unlock();
|
|
||||||
}
|
|
||||||
Done: ;
|
Done: ;
|
||||||
#else
|
#else
|
||||||
for (size_t i = 0; i < requestedCount; i++) {
|
for (size_t i = 0; i < requestedCount; i++) {
|
||||||
@ -301,15 +337,12 @@ Done: ;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Done:
|
Done:
|
||||||
// Set mPolling to false and wake up the wakeAndLock() waiters.
|
// Set mPolling to false and wake up the wakeAndLock() waiters.
|
||||||
mLock.lock();
|
|
||||||
mPolling = false;
|
mPolling = false;
|
||||||
if (mWaiters != 0) {
|
if (mWaiters != 0) {
|
||||||
mAwake.broadcast();
|
mAwake.broadcast();
|
||||||
}
|
}
|
||||||
mLock.unlock();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LOOPER_STATISTICS
|
#ifdef LOOPER_STATISTICS
|
||||||
@ -335,19 +368,59 @@ Done:
|
|||||||
}
|
}
|
||||||
#endif
|
#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++) {
|
for (size_t i = 0; i < mResponses.size(); i++) {
|
||||||
const Response& response = mResponses.itemAt(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
|
#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
|
||||||
LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this,
|
LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
|
||||||
response.request.fd, response.events, response.request.data);
|
this, callback, fd, events, data);
|
||||||
#endif
|
#endif
|
||||||
int callbackResult = response.request.callback(
|
int callbackResult = callback(fd, events, data);
|
||||||
response.request.fd, response.events, response.request.data);
|
|
||||||
if (callbackResult == 0) {
|
if (callbackResult == 0) {
|
||||||
removeFd(response.request.fd);
|
removeFd(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ALOOPER_POLL_CALLBACK;
|
result = ALOOPER_POLL_CALLBACK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -593,4 +666,83 @@ void Looper::wakeAndLock() {
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
} // namespace android
|
||||||
|
@ -16,6 +16,13 @@
|
|||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MSG_TEST1 = 1,
|
||||||
|
MSG_TEST2 = 2,
|
||||||
|
MSG_TEST3 = 3,
|
||||||
|
MSG_TEST4 = 4,
|
||||||
|
};
|
||||||
|
|
||||||
class DelayedWake : public DelayedTask {
|
class DelayedWake : public DelayedTask {
|
||||||
sp<Looper> mLooper;
|
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 {
|
class LooperTest : public testing::Test {
|
||||||
protected:
|
protected:
|
||||||
sp<Looper> mLooper;
|
sp<Looper> mLooper;
|
||||||
@ -421,5 +437,257 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInv
|
|||||||
<< "replacement handler callback should be invoked";
|
<< "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
|
} // namespace android
|
||||||
|
Loading…
Reference in New Issue
Block a user