Loading core/java/android/app/NativeActivity.java +33 −0 Original line number Diff line number Diff line package android.app; import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; import dalvik.system.PathClassLoader; import android.content.Context; Loading @@ -25,6 +28,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.inputmethod.InputMethodManager; import java.io.File; import java.lang.ref.WeakReference; /** * Convenience for implementing an activity that will be implemented Loading @@ -36,6 +40,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, private NativeContentView mNativeContentView; private InputMethodManager mIMM; private InputMethodCallback mInputMethodCallback; private int mNativeHandle; Loading Loading @@ -73,6 +78,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, private native void onInputChannelDestroyedNative(int handle, InputChannel channel); private native void onContentRectChangedNative(int handle, int x, int y, int w, int h); private native void dispatchKeyEventNative(int handle, KeyEvent event); private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled); static class NativeContentView extends View { NativeActivity mActivity; Loading @@ -86,12 +92,34 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } } static class InputMethodCallback extends IInputMethodCallback.Stub { WeakReference<NativeActivity> mNa; InputMethodCallback(NativeActivity na) { mNa = new WeakReference<NativeActivity>(na); } @Override public void finishedEvent(int seq, boolean handled) { NativeActivity na = mNa.get(); if (na != null) { na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled); } } @Override public void sessionCreated(IInputMethodSession session) { // Stub -- not for use in the client. } } @Override protected void onCreate(Bundle savedInstanceState) { String libname = "main"; ActivityInfo ai; mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); mInputMethodCallback = new InputMethodCallback(this); getWindow().takeSurface(this); getWindow().takeInputQueue(this); Loading Loading @@ -292,6 +320,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } } void preDispatchKeyEvent(KeyEvent event, int seq) { mIMM.dispatchKeyEvent(this, seq, event, mInputMethodCallback); } void setWindowFlags(int flags, int mask) { getWindow().setFlags(flags, mask); } Loading core/jni/android_app_NativeActivity.cpp +208 −44 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ static struct { jclass clazz; jmethodID dispatchUnhandledKeyEvent; jmethodID preDispatchKeyEvent; jmethodID setWindowFlags; jmethodID setWindowFormat; jmethodID showIme; Loading Loading @@ -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)); Loading Loading @@ -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) { Loading @@ -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; } } Loading @@ -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); Loading @@ -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; } } if (needFinished) { 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(); LOGW("finishEvent called for unknown event: %p", event); } void AInputQueue::dispatchEvent(android::KeyEvent* event) { Loading @@ -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(); Loading @@ -259,14 +316,101 @@ KeyEvent* AInputQueue::consumeUnhandledEvent() { return event; } void AInputQueue::doDefaultKey(KeyEvent* keyEvent) { KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) { KeyEvent* event = NULL; mLock.lock(); 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); } mUnhandledKeys.add(keyEvent); mLock.unlock(); } bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) { mLock.lock(); LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { 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); } mPendingKeys.add(keyEvent); 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 { Loading Loading @@ -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: { Loading Loading @@ -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 }, Loading @@ -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"; Loading @@ -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, Loading include/android_runtime/android_app_NativeActivity.h +63 −9 Original line number Diff line number Diff line Loading @@ -42,8 +42,26 @@ extern void android_NativeActivity_hideSoftInput( /* * NDK input queue API. * * Here is the event flow: * 1. Event arrives in input consumer, and is returned by getEvent(). * 2. Application calls preDispatchEvent(): * a. Event is assigned a sequence ID and enqueued in mPreDispatchingKeys. * b. Main thread picks up event, hands to input method. * c. Input method eventually returns sequence # and whether it was handled. * d. finishPreDispatch() is called to enqueue the information. * e. next getEvent() call will: * - finish any pre-dispatch events that the input method handled * - return the next pre-dispatched event that the input method didn't handle. * f. (A preDispatchEvent() call on this event will now return false). * 3. Application calls finishEvent() with whether it was handled. * - If handled is true, the event is finished. * - If handled is false, the event is put on mUnhandledKeys, and: * a. Main thread receives event from consumeUnhandledEvent(). * b. Java sends event through default key handler. * c. event is finished. */ struct AInputQueue { struct AInputQueue : public android::InputEventFactoryInterface { public: /* Creates a consumer associated with an input channel. */ explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite); Loading @@ -59,8 +77,9 @@ public: int32_t getEvent(AInputEvent** outEvent); void finishEvent(AInputEvent* event, bool handled); bool preDispatchEvent(AInputEvent* event); void finishEvent(AInputEvent* event, bool handled); // ---------------------------------------------------------- Loading @@ -68,28 +87,63 @@ public: void dispatchEvent(android::KeyEvent* event); void finishPreDispatch(int seq, bool handled); android::KeyEvent* consumeUnhandledEvent(); android::KeyEvent* consumePreDispatchingEvent(int* outSeq); virtual android::KeyEvent* createKeyEvent(); virtual android::MotionEvent* createMotionEvent(); int mWorkWrite; private: void doDefaultKey(android::KeyEvent* keyEvent); void doUnhandledKey(android::KeyEvent* keyEvent); bool preDispatchKey(android::KeyEvent* keyEvent); void wakeupDispatch(); android::InputConsumer mConsumer; android::PreallocatedInputEventFactory mInputEventFactory; android::sp<android::PollLoop> mPollLoop; int mDispatchKeyRead; int mDispatchKeyWrite; // This is only touched by the event reader thread. It is the current // key events that came out of the mDispatchingKeys list and are now //Êdelivered to the app. android::Vector<android::KeyEvent*> mDeliveringKeys; struct in_flight_event { android::InputEvent* event; int seq; bool doFinish; }; struct finish_pre_dispatch { int seq; bool handled; }; android::Mutex mLock; android::Vector<android::KeyEvent*> mPendingKeys; int mSeq; // Cache of previously allocated key events. android::Vector<android::KeyEvent*> mAvailKeyEvents; // Cache of previously allocated motion events. android::Vector<android::MotionEvent*> mAvailMotionEvents; // All input events that are actively being processed. android::Vector<in_flight_event> mInFlightEvents; // Key events that the app didn't handle, and are pending for // delivery to the activity's default key handling. android::Vector<android::KeyEvent*> mUnhandledKeys; // Keys that arrived in the Java framework and need to be // dispatched to the app. android::Vector<android::KeyEvent*> mDispatchingKeys; // Key events that are pending to be pre-dispatched to the IME. android::Vector<in_flight_event> mPreDispatchingKeys; // Event sequence numbers that we have finished pre-dispatching. android::Vector<finish_pre_dispatch> mFinishPreDispatches; }; #endif // _ANDROID_APP_NATIVEACTIVITY_H include/ui/Input.h +2 −0 Original line number Diff line number Diff line Loading @@ -152,6 +152,7 @@ public: protected: void initialize(int32_t deviceId, int32_t source); void initialize(const InputEvent& from); private: int32_t mDeviceId; Loading Loading @@ -202,6 +203,7 @@ public: int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); void initialize(const KeyEvent& from); private: int32_t mAction; Loading libs/ui/Input.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,11 @@ void InputEvent::initialize(int32_t deviceId, int32_t source) { mSource = source; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; } // class KeyEvent bool KeyEvent::hasDefaultAction(int32_t keyCode) { Loading Loading @@ -106,6 +111,18 @@ void KeyEvent::initialize( mEventTime = eventTime; } void KeyEvent::initialize(const KeyEvent& from) { InputEvent::initialize(from); mAction = from.mAction; mFlags = from.mFlags; mKeyCode = from.mKeyCode; mScanCode = from.mScanCode; mMetaState = from.mMetaState; mRepeatCount = from.mRepeatCount; mDownTime = from.mDownTime; mEventTime = from.mEventTime; } // class MotionEvent void MotionEvent::initialize( Loading Loading
core/java/android/app/NativeActivity.java +33 −0 Original line number Diff line number Diff line package android.app; import com.android.internal.view.IInputMethodCallback; import com.android.internal.view.IInputMethodSession; import dalvik.system.PathClassLoader; import android.content.Context; Loading @@ -25,6 +28,7 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.inputmethod.InputMethodManager; import java.io.File; import java.lang.ref.WeakReference; /** * Convenience for implementing an activity that will be implemented Loading @@ -36,6 +40,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, private NativeContentView mNativeContentView; private InputMethodManager mIMM; private InputMethodCallback mInputMethodCallback; private int mNativeHandle; Loading Loading @@ -73,6 +78,7 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, private native void onInputChannelDestroyedNative(int handle, InputChannel channel); private native void onContentRectChangedNative(int handle, int x, int y, int w, int h); private native void dispatchKeyEventNative(int handle, KeyEvent event); private native void finishPreDispatchKeyEventNative(int handle, int seq, boolean handled); static class NativeContentView extends View { NativeActivity mActivity; Loading @@ -86,12 +92,34 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } } static class InputMethodCallback extends IInputMethodCallback.Stub { WeakReference<NativeActivity> mNa; InputMethodCallback(NativeActivity na) { mNa = new WeakReference<NativeActivity>(na); } @Override public void finishedEvent(int seq, boolean handled) { NativeActivity na = mNa.get(); if (na != null) { na.finishPreDispatchKeyEventNative(na.mNativeHandle, seq, handled); } } @Override public void sessionCreated(IInputMethodSession session) { // Stub -- not for use in the client. } } @Override protected void onCreate(Bundle savedInstanceState) { String libname = "main"; ActivityInfo ai; mIMM = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); mInputMethodCallback = new InputMethodCallback(this); getWindow().takeSurface(this); getWindow().takeInputQueue(this); Loading Loading @@ -292,6 +320,11 @@ public class NativeActivity extends Activity implements SurfaceHolder.Callback2, } } void preDispatchKeyEvent(KeyEvent event, int seq) { mIMM.dispatchKeyEvent(this, seq, event, mInputMethodCallback); } void setWindowFlags(int flags, int mask) { getWindow().setFlags(flags, mask); } Loading
core/jni/android_app_NativeActivity.cpp +208 −44 Original line number Diff line number Diff line Loading @@ -45,6 +45,7 @@ static struct { jclass clazz; jmethodID dispatchUnhandledKeyEvent; jmethodID preDispatchKeyEvent; jmethodID setWindowFlags; jmethodID setWindowFormat; jmethodID showIme; Loading Loading @@ -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)); Loading Loading @@ -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) { Loading @@ -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; } } Loading @@ -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); Loading @@ -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; } } if (needFinished) { 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(); LOGW("finishEvent called for unknown event: %p", event); } void AInputQueue::dispatchEvent(android::KeyEvent* event) { Loading @@ -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(); Loading @@ -259,14 +316,101 @@ KeyEvent* AInputQueue::consumeUnhandledEvent() { return event; } void AInputQueue::doDefaultKey(KeyEvent* keyEvent) { KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) { KeyEvent* event = NULL; mLock.lock(); 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); } mUnhandledKeys.add(keyEvent); mLock.unlock(); } bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) { mLock.lock(); LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { 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); } mPendingKeys.add(keyEvent); 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 { Loading Loading @@ -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: { Loading Loading @@ -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 }, Loading @@ -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"; Loading @@ -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, Loading
include/android_runtime/android_app_NativeActivity.h +63 −9 Original line number Diff line number Diff line Loading @@ -42,8 +42,26 @@ extern void android_NativeActivity_hideSoftInput( /* * NDK input queue API. * * Here is the event flow: * 1. Event arrives in input consumer, and is returned by getEvent(). * 2. Application calls preDispatchEvent(): * a. Event is assigned a sequence ID and enqueued in mPreDispatchingKeys. * b. Main thread picks up event, hands to input method. * c. Input method eventually returns sequence # and whether it was handled. * d. finishPreDispatch() is called to enqueue the information. * e. next getEvent() call will: * - finish any pre-dispatch events that the input method handled * - return the next pre-dispatched event that the input method didn't handle. * f. (A preDispatchEvent() call on this event will now return false). * 3. Application calls finishEvent() with whether it was handled. * - If handled is true, the event is finished. * - If handled is false, the event is put on mUnhandledKeys, and: * a. Main thread receives event from consumeUnhandledEvent(). * b. Java sends event through default key handler. * c. event is finished. */ struct AInputQueue { struct AInputQueue : public android::InputEventFactoryInterface { public: /* Creates a consumer associated with an input channel. */ explicit AInputQueue(const android::sp<android::InputChannel>& channel, int workWrite); Loading @@ -59,8 +77,9 @@ public: int32_t getEvent(AInputEvent** outEvent); void finishEvent(AInputEvent* event, bool handled); bool preDispatchEvent(AInputEvent* event); void finishEvent(AInputEvent* event, bool handled); // ---------------------------------------------------------- Loading @@ -68,28 +87,63 @@ public: void dispatchEvent(android::KeyEvent* event); void finishPreDispatch(int seq, bool handled); android::KeyEvent* consumeUnhandledEvent(); android::KeyEvent* consumePreDispatchingEvent(int* outSeq); virtual android::KeyEvent* createKeyEvent(); virtual android::MotionEvent* createMotionEvent(); int mWorkWrite; private: void doDefaultKey(android::KeyEvent* keyEvent); void doUnhandledKey(android::KeyEvent* keyEvent); bool preDispatchKey(android::KeyEvent* keyEvent); void wakeupDispatch(); android::InputConsumer mConsumer; android::PreallocatedInputEventFactory mInputEventFactory; android::sp<android::PollLoop> mPollLoop; int mDispatchKeyRead; int mDispatchKeyWrite; // This is only touched by the event reader thread. It is the current // key events that came out of the mDispatchingKeys list and are now //Êdelivered to the app. android::Vector<android::KeyEvent*> mDeliveringKeys; struct in_flight_event { android::InputEvent* event; int seq; bool doFinish; }; struct finish_pre_dispatch { int seq; bool handled; }; android::Mutex mLock; android::Vector<android::KeyEvent*> mPendingKeys; int mSeq; // Cache of previously allocated key events. android::Vector<android::KeyEvent*> mAvailKeyEvents; // Cache of previously allocated motion events. android::Vector<android::MotionEvent*> mAvailMotionEvents; // All input events that are actively being processed. android::Vector<in_flight_event> mInFlightEvents; // Key events that the app didn't handle, and are pending for // delivery to the activity's default key handling. android::Vector<android::KeyEvent*> mUnhandledKeys; // Keys that arrived in the Java framework and need to be // dispatched to the app. android::Vector<android::KeyEvent*> mDispatchingKeys; // Key events that are pending to be pre-dispatched to the IME. android::Vector<in_flight_event> mPreDispatchingKeys; // Event sequence numbers that we have finished pre-dispatching. android::Vector<finish_pre_dispatch> mFinishPreDispatches; }; #endif // _ANDROID_APP_NATIVEACTIVITY_H
include/ui/Input.h +2 −0 Original line number Diff line number Diff line Loading @@ -152,6 +152,7 @@ public: protected: void initialize(int32_t deviceId, int32_t source); void initialize(const InputEvent& from); private: int32_t mDeviceId; Loading Loading @@ -202,6 +203,7 @@ public: int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime); void initialize(const KeyEvent& from); private: int32_t mAction; Loading
libs/ui/Input.cpp +17 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,11 @@ void InputEvent::initialize(int32_t deviceId, int32_t source) { mSource = source; } void InputEvent::initialize(const InputEvent& from) { mDeviceId = from.mDeviceId; mSource = from.mSource; } // class KeyEvent bool KeyEvent::hasDefaultAction(int32_t keyCode) { Loading Loading @@ -106,6 +111,18 @@ void KeyEvent::initialize( mEventTime = eventTime; } void KeyEvent::initialize(const KeyEvent& from) { InputEvent::initialize(from); mAction = from.mAction; mFlags = from.mFlags; mKeyCode = from.mKeyCode; mScanCode = from.mScanCode; mMetaState = from.mMetaState; mRepeatCount = from.mRepeatCount; mDownTime = from.mDownTime; mEventTime = from.mEventTime; } // class MotionEvent void MotionEvent::initialize( Loading