blob: b46eb4bf073c2cd098c0b7ac23bede54437caa44 [file] [log] [blame]
Jeff Brownc28867a2013-03-26 15:42:39 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "InputEventSender"
18
19//#define LOG_NDEBUG 0
20
21// Log debug messages about the dispatch cycle.
22#define DEBUG_DISPATCH_CYCLE 0
23
24
25#include "JNIHelp.h"
26
27#include <android_runtime/AndroidRuntime.h>
28#include <utils/Log.h>
29#include <utils/Looper.h>
30#include <utils/threads.h>
31#include <utils/KeyedVector.h>
32#include <androidfw/InputTransport.h>
33#include "android_os_MessageQueue.h"
34#include "android_view_InputChannel.h"
35#include "android_view_KeyEvent.h"
36#include "android_view_MotionEvent.h"
37
Jeff Browna4ca8ea2013-04-02 18:01:38 -070038#include <ScopedLocalRef.h>
39
Jeff Brownc28867a2013-03-26 15:42:39 -070040namespace android {
41
42static struct {
43 jclass clazz;
44
45 jmethodID dispatchInputEventFinished;
46} gInputEventSenderClassInfo;
47
48
49class NativeInputEventSender : public LooperCallback {
50public:
51 NativeInputEventSender(JNIEnv* env,
Jeff Browna4ca8ea2013-04-02 18:01:38 -070052 jobject senderWeak, const sp<InputChannel>& inputChannel,
Jeff Brownc28867a2013-03-26 15:42:39 -070053 const sp<MessageQueue>& messageQueue);
54
55 status_t initialize();
56 void dispose();
57 status_t sendKeyEvent(uint32_t seq, const KeyEvent* event);
58 status_t sendMotionEvent(uint32_t seq, const MotionEvent* event);
59
60protected:
61 virtual ~NativeInputEventSender();
62
63private:
Jeff Browna4ca8ea2013-04-02 18:01:38 -070064 jobject mSenderWeakGlobal;
Jeff Brownc28867a2013-03-26 15:42:39 -070065 InputPublisher mInputPublisher;
66 sp<MessageQueue> mMessageQueue;
67 KeyedVector<uint32_t, uint32_t> mPublishedSeqMap;
68 uint32_t mNextPublishedSeq;
69
70 const char* getInputChannelName() {
71 return mInputPublisher.getChannel()->getName().string();
72 }
73
74 virtual int handleEvent(int receiveFd, int events, void* data);
75 status_t receiveFinishedSignals(JNIEnv* env);
76};
77
78
79NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
Jeff Browna4ca8ea2013-04-02 18:01:38 -070080 jobject senderWeak, const sp<InputChannel>& inputChannel,
Jeff Brownc28867a2013-03-26 15:42:39 -070081 const sp<MessageQueue>& messageQueue) :
Jeff Browna4ca8ea2013-04-02 18:01:38 -070082 mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
Jeff Brownc28867a2013-03-26 15:42:39 -070083 mInputPublisher(inputChannel), mMessageQueue(messageQueue),
Michael Wright10f9b092013-04-01 13:11:34 -070084 mNextPublishedSeq(1) {
Jeff Brownc28867a2013-03-26 15:42:39 -070085#if DEBUG_DISPATCH_CYCLE
86 ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName());
87#endif
88}
89
90NativeInputEventSender::~NativeInputEventSender() {
91 JNIEnv* env = AndroidRuntime::getJNIEnv();
Jeff Browna4ca8ea2013-04-02 18:01:38 -070092 env->DeleteGlobalRef(mSenderWeakGlobal);
Jeff Brownc28867a2013-03-26 15:42:39 -070093}
94
95status_t NativeInputEventSender::initialize() {
96 int receiveFd = mInputPublisher.getChannel()->getFd();
97 mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
98 return OK;
99}
100
101void NativeInputEventSender::dispose() {
102#if DEBUG_DISPATCH_CYCLE
103 ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName());
104#endif
105
106 mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
107}
108
109status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
110#if DEBUG_DISPATCH_CYCLE
111 ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq);
112#endif
113
114 uint32_t publishedSeq = mNextPublishedSeq++;
115 status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
116 event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
117 event->getKeyCode(), event->getScanCode(), event->getMetaState(),
118 event->getRepeatCount(), event->getDownTime(), event->getEventTime());
119 if (status) {
120 ALOGW("Failed to send key event on channel '%s'. status=%d",
121 getInputChannelName(), status);
122 return status;
123 }
124 mPublishedSeqMap.add(publishedSeq, seq);
125 return OK;
126}
127
128status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) {
129#if DEBUG_DISPATCH_CYCLE
130 ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName(), seq);
131#endif
132
133 uint32_t publishedSeq;
134 for (size_t i = 0; i <= event->getHistorySize(); i++) {
135 publishedSeq = mNextPublishedSeq++;
136 status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
137 event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
138 event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
139 event->getXOffset(), event->getYOffset(),
140 event->getXPrecision(), event->getYPrecision(),
141 event->getDownTime(), event->getHistoricalEventTime(i),
142 event->getPointerCount(), event->getPointerProperties(),
143 event->getHistoricalRawPointerCoords(0, i));
144 if (status) {
145 ALOGW("Failed to send motion event sample on channel '%s'. status=%d",
146 getInputChannelName(), status);
147 return status;
148 }
149 }
150 mPublishedSeqMap.add(publishedSeq, seq);
151 return OK;
152}
153
154int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) {
155 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
Jeff Brown44e13ef2013-03-27 12:34:30 -0700156#if DEBUG_DISPATCH_CYCLE
157 // This error typically occurs when the consumer has closed the input channel
158 // as part of finishing an IME session, in which case the publisher will
159 // soon be disposed as well.
160 ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred. "
Jeff Brownc28867a2013-03-26 15:42:39 -0700161 "events=0x%x", getInputChannelName(), events);
Jeff Brown44e13ef2013-03-27 12:34:30 -0700162#endif
Jeff Brownc28867a2013-03-26 15:42:39 -0700163 return 0; // remove the callback
164 }
165
166 if (!(events & ALOOPER_EVENT_INPUT)) {
167 ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
168 "events=0x%x", getInputChannelName(), events);
169 return 1;
170 }
171
172 JNIEnv* env = AndroidRuntime::getJNIEnv();
173 status_t status = receiveFinishedSignals(env);
174 mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
175 return status == OK || status == NO_MEMORY ? 1 : 0;
176}
177
178status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
179#if DEBUG_DISPATCH_CYCLE
180 ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName());
181#endif
182
Jeff Browna4ca8ea2013-04-02 18:01:38 -0700183 ScopedLocalRef<jobject> senderObj(env, NULL);
Jeff Brownc28867a2013-03-26 15:42:39 -0700184 bool skipCallbacks = false;
185 for (;;) {
186 uint32_t publishedSeq;
187 bool handled;
188 status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled);
189 if (status) {
190 if (status == WOULD_BLOCK) {
191 return OK;
192 }
193 ALOGE("channel '%s' ~ Failed to consume finished signals. status=%d",
194 getInputChannelName(), status);
195 return status;
196 }
197
198 ssize_t index = mPublishedSeqMap.indexOfKey(publishedSeq);
199 if (index >= 0) {
200 uint32_t seq = mPublishedSeqMap.valueAt(index);
201 mPublishedSeqMap.removeItemsAt(index);
202
203#if DEBUG_DISPATCH_CYCLE
204 ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
205 "pendingEvents=%u.",
206 getInputChannelName(), seq, handled ? "true" : "false",
207 mPublishedSeqMap.size());
208#endif
209
210 if (!skipCallbacks) {
Jeff Browna4ca8ea2013-04-02 18:01:38 -0700211 if (!senderObj.get()) {
212 senderObj.reset(jniGetReferent(env, mSenderWeakGlobal));
213 if (!senderObj.get()) {
214 ALOGW("channel '%s' ~ Sender object was finalized "
215 "without being disposed.", getInputChannelName());
216 return DEAD_OBJECT;
217 }
218 }
219
220 env->CallVoidMethod(senderObj.get(),
Jeff Brownc28867a2013-03-26 15:42:39 -0700221 gInputEventSenderClassInfo.dispatchInputEventFinished,
222 jint(seq), jboolean(handled));
223 if (env->ExceptionCheck()) {
224 ALOGE("Exception dispatching finished signal.");
225 skipCallbacks = true;
226 }
227 }
228 }
229 }
230}
231
232
Jeff Browna4ca8ea2013-04-02 18:01:38 -0700233static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
Jeff Brownc28867a2013-03-26 15:42:39 -0700234 jobject inputChannelObj, jobject messageQueueObj) {
235 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
236 inputChannelObj);
237 if (inputChannel == NULL) {
238 jniThrowRuntimeException(env, "InputChannel is not initialized.");
239 return 0;
240 }
241
242 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
243 if (messageQueue == NULL) {
244 jniThrowRuntimeException(env, "MessageQueue is not initialized.");
245 return 0;
246 }
247
248 sp<NativeInputEventSender> sender = new NativeInputEventSender(env,
Jeff Browna4ca8ea2013-04-02 18:01:38 -0700249 senderWeak, inputChannel, messageQueue);
Jeff Brownc28867a2013-03-26 15:42:39 -0700250 status_t status = sender->initialize();
251 if (status) {
252 String8 message;
253 message.appendFormat("Failed to initialize input event sender. status=%d", status);
254 jniThrowRuntimeException(env, message.string());
255 return 0;
256 }
257
258 sender->incStrong(gInputEventSenderClassInfo.clazz); // retain a reference for the object
259 return reinterpret_cast<jint>(sender.get());
260}
261
262static void nativeDispose(JNIEnv* env, jclass clazz, jint senderPtr) {
263 sp<NativeInputEventSender> sender =
264 reinterpret_cast<NativeInputEventSender*>(senderPtr);
265 sender->dispose();
266 sender->decStrong(gInputEventSenderClassInfo.clazz); // drop reference held by the object
267}
268
269static jboolean nativeSendKeyEvent(JNIEnv* env, jclass clazz, jint senderPtr,
270 jint seq, jobject eventObj) {
271 sp<NativeInputEventSender> sender =
272 reinterpret_cast<NativeInputEventSender*>(senderPtr);
273 KeyEvent event;
274 android_view_KeyEvent_toNative(env, eventObj, &event);
275 status_t status = sender->sendKeyEvent(seq, &event);
276 return !status;
277}
278
279static jboolean nativeSendMotionEvent(JNIEnv* env, jclass clazz, jint senderPtr,
280 jint seq, jobject eventObj) {
281 sp<NativeInputEventSender> sender =
282 reinterpret_cast<NativeInputEventSender*>(senderPtr);
283 MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
284 status_t status = sender->sendMotionEvent(seq, event);
285 return !status;
286}
287
288
289static JNINativeMethod gMethods[] = {
290 /* name, signature, funcPtr */
291 { "nativeInit",
Jeff Browna4ca8ea2013-04-02 18:01:38 -0700292 "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
Jeff Brownc28867a2013-03-26 15:42:39 -0700293 (void*)nativeInit },
294 { "nativeDispose", "(I)V",
295 (void*)nativeDispose },
296 { "nativeSendKeyEvent", "(IILandroid/view/KeyEvent;)Z",
297 (void*)nativeSendKeyEvent },
298 { "nativeSendMotionEvent", "(IILandroid/view/MotionEvent;)Z",
299 (void*)nativeSendMotionEvent },
300};
301
302#define FIND_CLASS(var, className) \
303 var = env->FindClass(className); \
304 LOG_FATAL_IF(! var, "Unable to find class " className); \
305 var = jclass(env->NewGlobalRef(var));
306
307#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
308 var = env->GetMethodID(clazz, methodName, methodDescriptor); \
309 LOG_FATAL_IF(! var, "Unable to find method " methodName);
310
311int register_android_view_InputEventSender(JNIEnv* env) {
312 int res = jniRegisterNativeMethods(env, "android/view/InputEventSender",
313 gMethods, NELEM(gMethods));
314 LOG_FATAL_IF(res < 0, "Unable to register native methods.");
315
316 FIND_CLASS(gInputEventSenderClassInfo.clazz, "android/view/InputEventSender");
317
318 GET_METHOD_ID(gInputEventSenderClassInfo.dispatchInputEventFinished,
319 gInputEventSenderClassInfo.clazz,
320 "dispatchInputEventFinished", "(IZ)V");
321 return 0;
322}
323
324} // namespace android