summaryrefslogtreecommitdiffstats
path: root/core/jni/android_app_NativeActivity.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'core/jni/android_app_NativeActivity.cpp')
-rw-r--r--core/jni/android_app_NativeActivity.cpp252
1 files changed, 208 insertions, 44 deletions
diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp
index c17b504..1feb3b3 100644
--- a/core/jni/android_app_NativeActivity.cpp
+++ b/core/jni/android_app_NativeActivity.cpp
@@ -45,6 +45,7 @@ static struct {
jclass clazz;
jmethodID dispatchUnhandledKeyEvent;
+ jmethodID preDispatchKeyEvent;
jmethodID setWindowFlags;
jmethodID setWindowFormat;
jmethodID showIme;
@@ -104,7 +105,7 @@ static bool read_work(int fd, ActivityWork* outWork) {
using namespace android;
AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) :
- mWorkWrite(workWrite), mConsumer(channel) {
+ mWorkWrite(workWrite), mConsumer(channel), mSeq(0) {
int msgpipe[2];
if (pipe(msgpipe)) {
LOGW("could not create pipe: %s", strerror(errno));
@@ -157,6 +158,8 @@ int32_t AInputQueue::hasEvents() {
int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
*outEvent = NULL;
+ bool finishNow = false;
+
char byteread;
ssize_t nRead = read(mDispatchKeyRead, &byteread, 1);
if (nRead == 1) {
@@ -165,10 +168,34 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
KeyEvent* kevent = mDispatchingKeys[0];
*outEvent = kevent;
mDispatchingKeys.removeAt(0);
- mDeliveringKeys.add(kevent);
+ in_flight_event inflight;
+ inflight.event = kevent;
+ inflight.seq = -1;
+ inflight.doFinish = false;
+ mInFlightEvents.push(inflight);
+ }
+ if (mFinishPreDispatches.size() > 0) {
+ finish_pre_dispatch finish(mFinishPreDispatches[0]);
+ mFinishPreDispatches.removeAt(0);
+ const size_t N = mInFlightEvents.size();
+ for (size_t i=0; i<N; i++) {
+ const in_flight_event& inflight(mInFlightEvents[i]);
+ if (inflight.seq == finish.seq) {
+ *outEvent = inflight.event;
+ finishNow = finish.handled;
+ }
+ }
+ if (*outEvent == NULL) {
+ LOGW("getEvent couldn't find inflight for seq %d", finish.seq);
+ }
}
mLock.unlock();
- if (*outEvent != NULL) {
+
+ if (finishNow) {
+ finishEvent(*outEvent, true);
+ *outEvent = NULL;
+ return -1;
+ } else if (*outEvent != NULL) {
return 0;
}
}
@@ -181,7 +208,7 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
}
InputEvent* myEvent = NULL;
- res = mConsumer.consume(&mInputEventFactory, &myEvent);
+ res = mConsumer.consume(this, &myEvent);
if (res != android::OK) {
LOGW("channel '%s' ~ Failed to consume input event. status=%d",
mConsumer.getChannel()->getName().string(), res);
@@ -189,39 +216,69 @@ int32_t AInputQueue::getEvent(AInputEvent** outEvent) {
return -1;
}
+ in_flight_event inflight;
+ inflight.event = myEvent;
+ inflight.seq = -1;
+ inflight.doFinish = true;
+ mInFlightEvents.push(inflight);
+
*outEvent = myEvent;
return 0;
}
+bool AInputQueue::preDispatchEvent(AInputEvent* event) {
+ if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) {
+ // The IME only cares about key events.
+ return false;
+ }
+
+ // For now we only send system keys to the IME... this avoids having
+ // critical keys like DPAD go through this path. We really need to have
+ // the IME report which keys it wants.
+ if (!((KeyEvent*)event)->isSystemKey()) {
+ return false;
+ }
+
+ return preDispatchKey((KeyEvent*)event);
+}
+
void AInputQueue::finishEvent(AInputEvent* event, bool handled) {
- bool needFinished = true;
+ LOG_TRACE("finishEvent: %p handled=%d", event, handled ? 1 : 0);
if (!handled && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY
&& ((KeyEvent*)event)->hasDefaultAction()) {
// The app didn't handle this, but it may have a default action
// associated with it. We need to hand this back to Java to be
// executed.
- doDefaultKey((KeyEvent*)event);
- needFinished = false;
+ doUnhandledKey((KeyEvent*)event);
+ return;
}
- const size_t N = mDeliveringKeys.size();
+ mLock.lock();
+ const size_t N = mInFlightEvents.size();
for (size_t i=0; i<N; i++) {
- if (mDeliveringKeys[i] == event) {
- delete event;
- mDeliveringKeys.removeAt(i);
- needFinished = false;
- break;
+ const in_flight_event& inflight(mInFlightEvents[i]);
+ if (inflight.event == event) {
+ if (inflight.doFinish) {
+ int32_t res = mConsumer.sendFinishedSignal();
+ if (res != android::OK) {
+ LOGW("Failed to send finished signal on channel '%s'. status=%d",
+ mConsumer.getChannel()->getName().string(), res);
+ }
+ }
+ if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) {
+ mAvailKeyEvents.push(static_cast<KeyEvent*>(event));
+ } else {
+ mAvailMotionEvents.push(static_cast<MotionEvent*>(event));
+ }
+ mInFlightEvents.removeAt(i);
+ mLock.unlock();
+ return;
}
}
+ mLock.unlock();
- if (needFinished) {
- int32_t res = mConsumer.sendFinishedSignal();
- if (res != android::OK) {
- LOGW("Failed to send finished signal on channel '%s'. status=%d",
- mConsumer.getChannel()->getName().string(), res);
- }
- }
+ LOGW("finishEvent called for unknown event: %p", event);
}
void AInputQueue::dispatchEvent(android::KeyEvent* event) {
@@ -229,28 +286,28 @@ void AInputQueue::dispatchEvent(android::KeyEvent* event) {
LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(),
mDispatchKeyWrite);
mDispatchingKeys.add(event);
+ wakeupDispatch();
mLock.unlock();
-
-restart:
- char dummy = 0;
- int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
- if (res < 0 && errno == EINTR) {
- goto restart;
- }
-
- if (res == sizeof(dummy)) return;
+}
- if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
- else LOGW("Truncated writing to dispatch fd: %d", res);
+void AInputQueue::finishPreDispatch(int seq, bool handled) {
+ mLock.lock();
+ LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0);
+ finish_pre_dispatch finish;
+ finish.seq = seq;
+ finish.handled = handled;
+ mFinishPreDispatches.add(finish);
+ wakeupDispatch();
+ mLock.unlock();
}
KeyEvent* AInputQueue::consumeUnhandledEvent() {
KeyEvent* event = NULL;
mLock.lock();
- if (mPendingKeys.size() > 0) {
- event = mPendingKeys[0];
- mPendingKeys.removeAt(0);
+ if (mUnhandledKeys.size() > 0) {
+ event = mUnhandledKeys[0];
+ mUnhandledKeys.removeAt(0);
}
mLock.unlock();
@@ -259,16 +316,103 @@ KeyEvent* AInputQueue::consumeUnhandledEvent() {
return event;
}
-void AInputQueue::doDefaultKey(KeyEvent* keyEvent) {
+KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) {
+ KeyEvent* event = NULL;
+
mLock.lock();
- LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite);
- if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) {
+ if (mPreDispatchingKeys.size() > 0) {
+ const in_flight_event& inflight(mPreDispatchingKeys[0]);
+ event = static_cast<KeyEvent*>(inflight.event);
+ *outSeq = inflight.seq;
+ mPreDispatchingKeys.removeAt(0);
+ }
+ mLock.unlock();
+
+ LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event);
+
+ return event;
+}
+
+KeyEvent* AInputQueue::createKeyEvent() {
+ mLock.lock();
+ KeyEvent* event;
+ if (mAvailKeyEvents.size() <= 0) {
+ event = new KeyEvent();
+ } else {
+ event = mAvailKeyEvents.top();
+ mAvailKeyEvents.pop();
+ }
+ mLock.unlock();
+ return event;
+}
+
+MotionEvent* AInputQueue::createMotionEvent() {
+ mLock.lock();
+ MotionEvent* event;
+ if (mAvailMotionEvents.size() <= 0) {
+ event = new MotionEvent();
+ } else {
+ event = mAvailMotionEvents.top();
+ mAvailMotionEvents.pop();
+ }
+ mLock.unlock();
+ return event;
+}
+
+void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) {
+ mLock.lock();
+ LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite);
+ if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) {
write_work(mWorkWrite, CMD_DEF_KEY);
}
- mPendingKeys.add(keyEvent);
+ mUnhandledKeys.add(keyEvent);
mLock.unlock();
}
+bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) {
+ mLock.lock();
+ LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite);
+ const size_t N = mInFlightEvents.size();
+ for (size_t i=0; i<N; i++) {
+ in_flight_event& inflight(mInFlightEvents.editItemAt(i));
+ if (inflight.event == keyEvent) {
+ if (inflight.seq >= 0) {
+ // This event has already been pre-dispatched!
+ LOG_TRACE("Event already pre-dispatched!");
+ mLock.unlock();
+ return false;
+ }
+ mSeq++;
+ if (mSeq < 0) mSeq = 1;
+ inflight.seq = mSeq;
+
+ if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) {
+ write_work(mWorkWrite, CMD_DEF_KEY);
+ }
+ mPreDispatchingKeys.add(inflight);
+ mLock.unlock();
+ return true;
+ }
+ }
+
+ LOGW("preDispatchKey called for unknown event: %p", keyEvent);
+ return false;
+}
+
+void AInputQueue::wakeupDispatch() {
+restart:
+ char dummy = 0;
+ int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy));
+ if (res < 0 && errno == EINTR) {
+ goto restart;
+ }
+
+ if (res == sizeof(dummy)) return;
+
+ if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno));
+ else LOGW("Truncated writing to dispatch fd: %d", res);
+}
+
namespace android {
// ------------------------------------------------------------------------
@@ -417,11 +561,14 @@ static bool mainWorkCallback(int fd, int events, void* data) {
code->env, keyEvent);
code->env->CallVoidMethod(code->clazz,
gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj);
- int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal();
- if (res != OK) {
- LOGW("Failed to send finished signal on channel '%s'. status=%d",
- code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res);
- }
+ code->nativeInputQueue->finishEvent(keyEvent, true);
+ }
+ int seq;
+ while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) {
+ jobject inputEventObj = android_view_KeyEvent_fromNative(
+ code->env, keyEvent);
+ code->env->CallVoidMethod(code->clazz,
+ gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq);
}
} break;
case CMD_SET_WINDOW_FORMAT: {
@@ -766,13 +913,26 @@ dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventOb
if (handle != 0) {
NativeCode* code = (NativeCode*)handle;
if (code->nativeInputQueue != NULL) {
- KeyEvent* event = new KeyEvent();
+ KeyEvent* event = code->nativeInputQueue->createKeyEvent();
android_view_KeyEvent_toNative(env, eventObj, event);
code->nativeInputQueue->dispatchEvent(event);
}
}
}
+static void
+finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle,
+ jint seq, jboolean handled)
+{
+ LOG_TRACE("finishPreDispatchKeyEvent_native");
+ if (handle != 0) {
+ NativeCode* code = (NativeCode*)handle;
+ if (code->nativeInputQueue != NULL) {
+ code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false);
+ }
+ }
+}
+
static const JNINativeMethod g_methods[] = {
{ "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I",
(void*)loadNativeCode_native },
@@ -792,6 +952,7 @@ static const JNINativeMethod g_methods[] = {
{ "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native },
{ "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native },
{ "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native },
+ { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native },
};
static const char* const kNativeActivityPathName = "android/app/NativeActivity";
@@ -814,6 +975,9 @@ int register_android_app_NativeActivity(JNIEnv* env)
GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent,
gNativeActivityClassInfo.clazz,
"dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V");
+ GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent,
+ gNativeActivityClassInfo.clazz,
+ "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V");
GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags,
gNativeActivityClassInfo.clazz,