blob: 523baf19fa8ab575a8ba6558ab82e4fe3e64ade4 [file] [log] [blame]
Jeff Brown32cbc38552011-12-01 14:01:49 -08001/*
2 * Copyright (C) 2011 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 "InputEventReceiver"
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>
Jeff Brown32cbc38552011-12-01 14:01:49 -080030#include <utils/threads.h>
31#include <ui/InputTransport.h>
32#include "android_os_MessageQueue.h"
33#include "android_view_InputChannel.h"
34#include "android_view_KeyEvent.h"
35#include "android_view_MotionEvent.h"
36
37namespace android {
38
39static struct {
40 jclass clazz;
41
42 jmethodID dispatchInputEvent;
43} gInputEventReceiverClassInfo;
44
45
46class NativeInputEventReceiver : public RefBase {
47public:
48 NativeInputEventReceiver(JNIEnv* env,
49 jobject receiverObj, const sp<InputChannel>& inputChannel,
50 const sp<Looper>& looper);
51
52 status_t initialize();
53 status_t finishInputEvent(bool handled);
54 static int handleReceiveCallback(int receiveFd, int events, void* data);
55
56protected:
57 virtual ~NativeInputEventReceiver();
58
59private:
60 jobject mReceiverObjGlobal;
61 InputConsumer mInputConsumer;
62 sp<Looper> mLooper;
63 bool mEventInProgress;
64 PreallocatedInputEventFactory mInputEventFactory;
65
66 const char* getInputChannelName() {
67 return mInputConsumer.getChannel()->getName().string();
68 }
69};
70
71
72NativeInputEventReceiver::NativeInputEventReceiver(JNIEnv* env,
73 jobject receiverObj, const sp<InputChannel>& inputChannel, const sp<Looper>& looper) :
74 mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
75 mInputConsumer(inputChannel), mLooper(looper), mEventInProgress(false) {
76#if DEBUG_DISPATCH_CYCLE
77 LOGD("channel '%s' ~ Initializing input event receiver.", getInputChannelName());
78#endif
79}
80
81NativeInputEventReceiver::~NativeInputEventReceiver() {
82#if DEBUG_DISPATCH_CYCLE
83 LOGD("channel '%s' ~ Disposing input event receiver.", getInputChannelName());
84#endif
85
86 mLooper->removeFd(mInputConsumer.getChannel()->getReceivePipeFd());
87 if (mEventInProgress) {
88 mInputConsumer.sendFinishedSignal(false); // ignoring result
89 }
90
91 JNIEnv* env = AndroidRuntime::getJNIEnv();
92 env->DeleteGlobalRef(mReceiverObjGlobal);
93}
94
95status_t NativeInputEventReceiver::initialize() {
96 status_t result = mInputConsumer.initialize();
97 if (result) {
98 LOGW("Failed to initialize input consumer for input channel '%s', status=%d",
99 getInputChannelName(), result);
100 return result;
101 }
102
103 int32_t receiveFd = mInputConsumer.getChannel()->getReceivePipeFd();
104 mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
105 return OK;
106}
107
108status_t NativeInputEventReceiver::finishInputEvent(bool handled) {
109 if (mEventInProgress) {
110#if DEBUG_DISPATCH_CYCLE
111 LOGD("channel '%s' ~ Finished input event.", getInputChannelName());
112#endif
113 mEventInProgress = false;
114
115 status_t status = mInputConsumer.sendFinishedSignal(handled);
116 if (status) {
117 LOGW("Failed to send finished signal on channel '%s'. status=%d",
118 getInputChannelName(), status);
119 }
120 return status;
121 } else {
122 LOGW("Ignoring attempt to finish input event while no event is in progress.");
123 return OK;
124 }
125}
126
127int NativeInputEventReceiver::handleReceiveCallback(int receiveFd, int events, void* data) {
128 sp<NativeInputEventReceiver> r = static_cast<NativeInputEventReceiver*>(data);
129
130 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
131 LOGE("channel '%s' ~ Publisher closed input channel or an error occurred. "
132 "events=0x%x", r->getInputChannelName(), events);
133 return 0; // remove the callback
134 }
135
136 if (!(events & ALOOPER_EVENT_INPUT)) {
137 LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. "
138 "events=0x%x", r->getInputChannelName(), events);
139 return 1;
140 }
141
142 status_t status = r->mInputConsumer.receiveDispatchSignal();
143 if (status) {
144 LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d",
145 r->getInputChannelName(), status);
146 return 0; // remove the callback
147 }
148
149 if (r->mEventInProgress) {
150 LOGW("channel '%s' ~ Publisher sent spurious dispatch signal.",
151 r->getInputChannelName());
152 return 1;
153 }
154
155 InputEvent* inputEvent;
156 status = r->mInputConsumer.consume(&r->mInputEventFactory, &inputEvent);
157 if (status) {
158 LOGW("channel '%s' ~ Failed to consume input event. status=%d",
159 r->getInputChannelName(), status);
160 r->mInputConsumer.sendFinishedSignal(false);
161 return 1;
162 }
163
164 JNIEnv* env = AndroidRuntime::getJNIEnv();
165 jobject inputEventObj;
166 switch (inputEvent->getType()) {
167 case AINPUT_EVENT_TYPE_KEY:
168#if DEBUG_DISPATCH_CYCLE
169 LOGD("channel '%s' ~ Received key event.",
170 r->getInputChannelName());
171#endif
172 inputEventObj = android_view_KeyEvent_fromNative(env,
173 static_cast<KeyEvent*>(inputEvent));
174 break;
175
176 case AINPUT_EVENT_TYPE_MOTION:
177#if DEBUG_DISPATCH_CYCLE
178 LOGD("channel '%s' ~ Received motion event.",
179 r->getInputChannelName());
180#endif
181 inputEventObj = android_view_MotionEvent_obtainAsCopy(env,
182 static_cast<MotionEvent*>(inputEvent));
183 break;
184
185 default:
186 assert(false); // InputConsumer should prevent this from ever happening
187 inputEventObj = NULL;
188 }
189
190 if (!inputEventObj) {
191 LOGW("channel '%s' ~ Failed to obtain event object.",
192 r->getInputChannelName());
193 r->mInputConsumer.sendFinishedSignal(false);
194 return 1;
195 }
196
197 r->mEventInProgress = true;
198
199#if DEBUG_DISPATCH_CYCLE
200 LOGD("channel '%s' ~ Invoking input handler.", r->getInputChannelName());
201#endif
202 env->CallVoidMethod(r->mReceiverObjGlobal,
203 gInputEventReceiverClassInfo.dispatchInputEvent, inputEventObj);
204#if DEBUG_DISPATCH_CYCLE
205 LOGD("channel '%s' ~ Returned from input handler.", r->getInputChannelName());
206#endif
207
208 if (env->ExceptionCheck()) {
209 LOGE("channel '%s' ~ An exception occurred while dispatching an event.",
210 r->getInputChannelName());
211 LOGE_EX(env);
212 env->ExceptionClear();
213
214 if (r->mEventInProgress) {
215 r->mInputConsumer.sendFinishedSignal(false);
216 r->mEventInProgress = false;
217 }
218 }
219
220 env->DeleteLocalRef(inputEventObj);
221 return 1;
222}
223
224
225static jint nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
226 jobject inputChannelObj, jobject messageQueueObj) {
227 sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
228 inputChannelObj);
229 if (inputChannel == NULL) {
230 jniThrowRuntimeException(env, "InputChannel is not initialized.");
231 return 0;
232 }
233
234 sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
235 if (looper == NULL) {
236 jniThrowRuntimeException(env, "MessageQueue is not initialized.");
237 return 0;
238 }
239
240 sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
241 receiverObj, inputChannel, looper);
242 status_t status = receiver->initialize();
243 if (status) {
244 String8 message;
245 message.appendFormat("Failed to initialize input event receiver. status=%d", status);
246 jniThrowRuntimeException(env, message.string());
247 return 0;
248 }
249
250 receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
251 return reinterpret_cast<jint>(receiver.get());
252}
253
254static void nativeDispose(JNIEnv* env, jclass clazz, jint receiverPtr) {
255 sp<NativeInputEventReceiver> receiver =
256 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
257 receiver->decStrong(gInputEventReceiverClassInfo.clazz); // drop reference held by the object
258}
259
260static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, jboolean handled) {
261 sp<NativeInputEventReceiver> receiver =
262 reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
263 status_t status = receiver->finishInputEvent(handled);
264 if (status) {
265 String8 message;
266 message.appendFormat("Failed to finish input event. status=%d", status);
267 jniThrowRuntimeException(env, message.string());
268 }
269}
270
271
272static JNINativeMethod gMethods[] = {
273 /* name, signature, funcPtr */
274 { "nativeInit",
275 "(Landroid/view/InputEventReceiver;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
276 (void*)nativeInit },
277 { "nativeDispose",
278 "(I)V",
279 (void*)nativeDispose },
280 { "nativeFinishInputEvent", "(IZ)V",
281 (void*)nativeFinishInputEvent }
282};
283
284#define FIND_CLASS(var, className) \
285 var = env->FindClass(className); \
286 LOG_FATAL_IF(! var, "Unable to find class " className); \
287 var = jclass(env->NewGlobalRef(var));
288
289#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
290 var = env->GetMethodID(clazz, methodName, methodDescriptor); \
291 LOG_FATAL_IF(! var, "Unable to find method " methodName);
292
293int register_android_view_InputEventReceiver(JNIEnv* env) {
294 int res = jniRegisterNativeMethods(env, "android/view/InputEventReceiver",
295 gMethods, NELEM(gMethods));
296 LOG_FATAL_IF(res < 0, "Unable to register native methods.");
297
298 FIND_CLASS(gInputEventReceiverClassInfo.clazz, "android/view/InputEventReceiver");
299
300 GET_METHOD_ID(gInputEventReceiverClassInfo.dispatchInputEvent,
301 gInputEventReceiverClassInfo.clazz,
302 "dispatchInputEvent", "(Landroid/view/InputEvent;)V");
303 return 0;
304}
305
306} // namespace android