1/*
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#include <nativehelper/JNIHelp.h>
22
23#include <android_runtime/AndroidRuntime.h>
24#include <utils/Log.h>
25#include <utils/Looper.h>
26#include <utils/threads.h>
27#include <utils/KeyedVector.h>
28#include <input/InputTransport.h>
29#include "android_os_MessageQueue.h"
30#include "android_view_InputChannel.h"
31#include "android_view_KeyEvent.h"
32#include "android_view_MotionEvent.h"
33
34#include <nativehelper/ScopedLocalRef.h>
35
36#include "core_jni_helpers.h"
37
38namespace android {
39
40// Log debug messages about the dispatch cycle.
41static const bool kDebugDispatchCycle = false;
42// Display id for default(primary) display.
43static const int32_t kDefaultDisplayId = 0;
44
45static struct {
46    jclass clazz;
47
48    jmethodID dispatchInputEventFinished;
49} gInputEventSenderClassInfo;
50
51
52class NativeInputEventSender : public LooperCallback {
53public:
54    NativeInputEventSender(JNIEnv* env,
55            jobject senderWeak, const sp<InputChannel>& inputChannel,
56            const sp<MessageQueue>& messageQueue);
57
58    status_t initialize();
59    void dispose();
60    status_t sendKeyEvent(uint32_t seq, const KeyEvent* event);
61    status_t sendMotionEvent(uint32_t seq, const MotionEvent* event);
62
63protected:
64    virtual ~NativeInputEventSender();
65
66private:
67    jobject mSenderWeakGlobal;
68    InputPublisher mInputPublisher;
69    sp<MessageQueue> mMessageQueue;
70    KeyedVector<uint32_t, uint32_t> mPublishedSeqMap;
71    uint32_t mNextPublishedSeq;
72
73    const std::string getInputChannelName() {
74        return mInputPublisher.getChannel()->getName();
75    }
76
77    virtual int handleEvent(int receiveFd, int events, void* data);
78    status_t receiveFinishedSignals(JNIEnv* env);
79};
80
81
82NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
83        jobject senderWeak, const sp<InputChannel>& inputChannel,
84        const sp<MessageQueue>& messageQueue) :
85        mSenderWeakGlobal(env->NewGlobalRef(senderWeak)),
86        mInputPublisher(inputChannel), mMessageQueue(messageQueue),
87        mNextPublishedSeq(1) {
88    if (kDebugDispatchCycle) {
89        ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName().c_str());
90    }
91}
92
93NativeInputEventSender::~NativeInputEventSender() {
94    JNIEnv* env = AndroidRuntime::getJNIEnv();
95    env->DeleteGlobalRef(mSenderWeakGlobal);
96}
97
98status_t NativeInputEventSender::initialize() {
99    int receiveFd = mInputPublisher.getChannel()->getFd();
100    mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
101    return OK;
102}
103
104void NativeInputEventSender::dispose() {
105    if (kDebugDispatchCycle) {
106        ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName().c_str());
107    }
108
109    mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
110}
111
112status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
113    if (kDebugDispatchCycle) {
114        ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName().c_str(), seq);
115    }
116
117    uint32_t publishedSeq = mNextPublishedSeq++;
118    status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
119            event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
120            event->getKeyCode(), event->getScanCode(), event->getMetaState(),
121            event->getRepeatCount(), event->getDownTime(), event->getEventTime());
122    if (status) {
123        ALOGW("Failed to send key event on channel '%s'.  status=%d",
124                getInputChannelName().c_str(), status);
125        return status;
126    }
127    mPublishedSeqMap.add(publishedSeq, seq);
128    return OK;
129}
130
131status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) {
132    if (kDebugDispatchCycle) {
133        ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName().c_str(), seq);
134    }
135
136    uint32_t publishedSeq;
137    for (size_t i = 0; i <= event->getHistorySize(); i++) {
138        publishedSeq = mNextPublishedSeq++;
139        status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
140                event->getDeviceId(), event->getSource(),
141                kDefaultDisplayId /* TODO(multi-display): propagate display id */,
142                event->getAction(), event->getActionButton(), event->getFlags(),
143                event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
144                event->getXOffset(), event->getYOffset(),
145                event->getXPrecision(), event->getYPrecision(),
146                event->getDownTime(), event->getHistoricalEventTime(i),
147                event->getPointerCount(), event->getPointerProperties(),
148                event->getHistoricalRawPointerCoords(0, i));
149        if (status) {
150            ALOGW("Failed to send motion event sample on channel '%s'.  status=%d",
151                    getInputChannelName().c_str(), status);
152            return status;
153        }
154    }
155    mPublishedSeqMap.add(publishedSeq, seq);
156    return OK;
157}
158
159int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) {
160    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
161        // This error typically occurs when the consumer has closed the input channel
162        // as part of finishing an IME session, in which case the publisher will
163        // soon be disposed as well.
164        if (kDebugDispatchCycle) {
165            ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred.  "
166                    "events=0x%x", getInputChannelName().c_str(), events);
167        }
168
169        return 0; // remove the callback
170    }
171
172    if (!(events & ALOOPER_EVENT_INPUT)) {
173        ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
174                "events=0x%x", getInputChannelName().c_str(), events);
175        return 1;
176    }
177
178    JNIEnv* env = AndroidRuntime::getJNIEnv();
179    status_t status = receiveFinishedSignals(env);
180    mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
181    return status == OK || status == NO_MEMORY ? 1 : 0;
182}
183
184status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
185    if (kDebugDispatchCycle) {
186        ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName().c_str());
187    }
188
189    ScopedLocalRef<jobject> senderObj(env, NULL);
190    bool skipCallbacks = false;
191    for (;;) {
192        uint32_t publishedSeq;
193        bool handled;
194        status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled);
195        if (status) {
196            if (status == WOULD_BLOCK) {
197                return OK;
198            }
199            ALOGE("channel '%s' ~ Failed to consume finished signals.  status=%d",
200                    getInputChannelName().c_str(), status);
201            return status;
202        }
203
204        ssize_t index = mPublishedSeqMap.indexOfKey(publishedSeq);
205        if (index >= 0) {
206            uint32_t seq = mPublishedSeqMap.valueAt(index);
207            mPublishedSeqMap.removeItemsAt(index);
208
209            if (kDebugDispatchCycle) {
210                ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
211                        "pendingEvents=%zu.",
212                        getInputChannelName().c_str(), seq, handled ? "true" : "false",
213                        mPublishedSeqMap.size());
214            }
215
216            if (!skipCallbacks) {
217                if (!senderObj.get()) {
218                    senderObj.reset(jniGetReferent(env, mSenderWeakGlobal));
219                    if (!senderObj.get()) {
220                        ALOGW("channel '%s' ~ Sender object was finalized "
221                                "without being disposed.", getInputChannelName().c_str());
222                        return DEAD_OBJECT;
223                    }
224                }
225
226                env->CallVoidMethod(senderObj.get(),
227                        gInputEventSenderClassInfo.dispatchInputEventFinished,
228                        jint(seq), jboolean(handled));
229                if (env->ExceptionCheck()) {
230                    ALOGE("Exception dispatching finished signal.");
231                    skipCallbacks = true;
232                }
233            }
234        }
235    }
236}
237
238
239static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
240        jobject inputChannelObj, jobject messageQueueObj) {
241    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
242            inputChannelObj);
243    if (inputChannel == NULL) {
244        jniThrowRuntimeException(env, "InputChannel is not initialized.");
245        return 0;
246    }
247
248    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
249    if (messageQueue == NULL) {
250        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
251        return 0;
252    }
253
254    sp<NativeInputEventSender> sender = new NativeInputEventSender(env,
255            senderWeak, inputChannel, messageQueue);
256    status_t status = sender->initialize();
257    if (status) {
258        String8 message;
259        message.appendFormat("Failed to initialize input event sender.  status=%d", status);
260        jniThrowRuntimeException(env, message.string());
261        return 0;
262    }
263
264    sender->incStrong(gInputEventSenderClassInfo.clazz); // retain a reference for the object
265    return reinterpret_cast<jlong>(sender.get());
266}
267
268static void nativeDispose(JNIEnv* env, jclass clazz, jlong senderPtr) {
269    sp<NativeInputEventSender> sender =
270            reinterpret_cast<NativeInputEventSender*>(senderPtr);
271    sender->dispose();
272    sender->decStrong(gInputEventSenderClassInfo.clazz); // drop reference held by the object
273}
274
275static jboolean nativeSendKeyEvent(JNIEnv* env, jclass clazz, jlong senderPtr,
276        jint seq, jobject eventObj) {
277    sp<NativeInputEventSender> sender =
278            reinterpret_cast<NativeInputEventSender*>(senderPtr);
279    KeyEvent event;
280    android_view_KeyEvent_toNative(env, eventObj, &event);
281    status_t status = sender->sendKeyEvent(seq, &event);
282    return !status;
283}
284
285static jboolean nativeSendMotionEvent(JNIEnv* env, jclass clazz, jlong senderPtr,
286        jint seq, jobject eventObj) {
287    sp<NativeInputEventSender> sender =
288            reinterpret_cast<NativeInputEventSender*>(senderPtr);
289    MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
290    status_t status = sender->sendMotionEvent(seq, event);
291    return !status;
292}
293
294
295static const JNINativeMethod gMethods[] = {
296    /* name, signature, funcPtr */
297    { "nativeInit",
298            "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
299            (void*)nativeInit },
300    { "nativeDispose", "(J)V",
301            (void*)nativeDispose },
302    { "nativeSendKeyEvent", "(JILandroid/view/KeyEvent;)Z",
303            (void*)nativeSendKeyEvent },
304    { "nativeSendMotionEvent", "(JILandroid/view/MotionEvent;)Z",
305            (void*)nativeSendMotionEvent },
306};
307
308int register_android_view_InputEventSender(JNIEnv* env) {
309    int res = RegisterMethodsOrDie(env, "android/view/InputEventSender", gMethods, NELEM(gMethods));
310
311    jclass clazz = FindClassOrDie(env, "android/view/InputEventSender");
312    gInputEventSenderClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
313
314    gInputEventSenderClassInfo.dispatchInputEventFinished = GetMethodIDOrDie(
315            env, gInputEventSenderClassInfo.clazz, "dispatchInputEventFinished", "(IZ)V");
316
317    return res;
318}
319
320} // namespace android
321