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 "InputQueue" 18 19 #include <fcntl.h> 20 #include <string.h> 21 #include <unistd.h> 22 23 #include <android/input.h> 24 #include <android_runtime/AndroidRuntime.h> 25 #include <android_runtime/android_view_InputQueue.h> 26 #include <input/Input.h> 27 #include <utils/Looper.h> 28 #include <utils/TypeHelpers.h> 29 #include <nativehelper/ScopedLocalRef.h> 30 31 #include <nativehelper/JNIHelp.h> 32 #include "android_os_MessageQueue.h" 33 #include "android_view_KeyEvent.h" 34 #include "android_view_MotionEvent.h" 35 36 #include "core_jni_helpers.h" 37 38 namespace android { 39 40 static struct { 41 jmethodID finishInputEvent; 42 } gInputQueueClassInfo; 43 44 enum { 45 MSG_FINISH_INPUT = 1, 46 }; 47 48 InputQueue::InputQueue(jobject inputQueueObj, const sp<Looper>& looper, 49 int dispatchReadFd, int dispatchWriteFd) : 50 mDispatchReadFd(dispatchReadFd), mDispatchWriteFd(dispatchWriteFd), 51 mDispatchLooper(looper), mHandler(new WeakMessageHandler(this)) { 52 JNIEnv* env = AndroidRuntime::getJNIEnv(); 53 mInputQueueWeakGlobal = env->NewGlobalRef(inputQueueObj); 54 } 55 56 InputQueue::~InputQueue() { 57 mDispatchLooper->removeMessages(mHandler); 58 JNIEnv* env = AndroidRuntime::getJNIEnv(); 59 env->DeleteGlobalRef(mInputQueueWeakGlobal); 60 close(mDispatchReadFd); 61 close(mDispatchWriteFd); 62 } 63 64 void InputQueue::attachLooper(Looper* looper, int ident, 65 ALooper_callbackFunc callback, void* data) { 66 Mutex::Autolock _l(mLock); 67 for (size_t i = 0; i < mAppLoopers.size(); i++) { 68 if (looper == mAppLoopers[i]) { 69 return; 70 } 71 } 72 mAppLoopers.push(looper); 73 looper->addFd(mDispatchReadFd, ident, ALOOPER_EVENT_INPUT, callback, data); 74 } 75 76 void InputQueue::detachLooper() { 77 Mutex::Autolock _l(mLock); 78 detachLooperLocked(); 79 } 80 81 void InputQueue::detachLooperLocked() { 82 for (size_t i = 0; i < mAppLoopers.size(); i++) { 83 mAppLoopers[i]->removeFd(mDispatchReadFd); 84 } 85 mAppLoopers.clear(); 86 } 87 88 bool InputQueue::hasEvents() { 89 Mutex::Autolock _l(mLock); 90 return mPendingEvents.size() > 0; 91 } 92 93 status_t InputQueue::getEvent(InputEvent** outEvent) { 94 Mutex::Autolock _l(mLock); 95 *outEvent = NULL; 96 if (!mPendingEvents.isEmpty()) { 97 *outEvent = mPendingEvents[0]; 98 mPendingEvents.removeAt(0); 99 } 100 101 if (mPendingEvents.isEmpty()) { 102 char byteread[16]; 103 ssize_t nRead; 104 do { 105 nRead = TEMP_FAILURE_RETRY(read(mDispatchReadFd, &byteread, sizeof(byteread))); 106 if (nRead < 0 && errno != EAGAIN) { 107 ALOGW("Failed to read from native dispatch pipe: %s", strerror(errno)); 108 } 109 } while (nRead > 0); 110 } 111 112 return *outEvent != NULL ? OK : WOULD_BLOCK; 113 } 114 115 bool InputQueue::preDispatchEvent(InputEvent* e) { 116 if (e->getType() == AINPUT_EVENT_TYPE_KEY) { 117 KeyEvent* keyEvent = static_cast<KeyEvent*>(e); 118 if (keyEvent->getFlags() & AKEY_EVENT_FLAG_PREDISPATCH) { 119 finishEvent(e, false); 120 return true; 121 } 122 } 123 return false; 124 } 125 126 void InputQueue::finishEvent(InputEvent* event, bool handled) { 127 Mutex::Autolock _l(mLock); 128 mFinishedEvents.push(key_value_pair_t<InputEvent*, bool>(event, handled)); 129 if (mFinishedEvents.size() == 1) { 130 mDispatchLooper->sendMessage(this, Message(MSG_FINISH_INPUT)); 131 } 132 } 133 134 void InputQueue::handleMessage(const Message& message) { 135 switch(message.what) { 136 case MSG_FINISH_INPUT: 137 JNIEnv* env = AndroidRuntime::getJNIEnv(); 138 ScopedLocalRef<jobject> inputQueueObj(env, jniGetReferent(env, mInputQueueWeakGlobal)); 139 if (!inputQueueObj.get()) { 140 ALOGW("InputQueue was finalized without being disposed"); 141 return; 142 } 143 while (true) { 144 InputEvent* event; 145 bool handled; 146 { 147 Mutex::Autolock _l(mLock); 148 if (mFinishedEvents.isEmpty()) { 149 break; 150 } 151 event = mFinishedEvents[0].getKey(); 152 handled = mFinishedEvents[0].getValue(); 153 mFinishedEvents.removeAt(0); 154 } 155 env->CallVoidMethod(inputQueueObj.get(), gInputQueueClassInfo.finishInputEvent, 156 reinterpret_cast<jlong>(event), handled); 157 recycleInputEvent(event); 158 } 159 break; 160 } 161 } 162 163 void InputQueue::recycleInputEvent(InputEvent* event) { 164 mPooledInputEventFactory.recycle(event); 165 } 166 167 KeyEvent* InputQueue::createKeyEvent() { 168 return mPooledInputEventFactory.createKeyEvent(); 169 } 170 171 MotionEvent* InputQueue::createMotionEvent() { 172 return mPooledInputEventFactory.createMotionEvent(); 173 } 174 175 void InputQueue::enqueueEvent(InputEvent* event) { 176 Mutex::Autolock _l(mLock); 177 mPendingEvents.push(event); 178 if (mPendingEvents.size() == 1) { 179 char payload = '\0'; 180 int res = TEMP_FAILURE_RETRY(write(mDispatchWriteFd, &payload, sizeof(payload))); 181 if (res < 0 && errno != EAGAIN) { 182 ALOGW("Failed writing to dispatch fd: %s", strerror(errno)); 183 } 184 } 185 } 186 187 InputQueue* InputQueue::createQueue(jobject inputQueueObj, const sp<Looper>& looper) { 188 int pipeFds[2]; 189 if (pipe(pipeFds)) { 190 ALOGW("Could not create native input dispatching pipe: %s", strerror(errno)); 191 return NULL; 192 } 193 fcntl(pipeFds[0], F_SETFL, O_NONBLOCK); 194 fcntl(pipeFds[1], F_SETFL, O_NONBLOCK); 195 return new InputQueue(inputQueueObj, looper, pipeFds[0], pipeFds[1]); 196 } 197 198 static jlong nativeInit(JNIEnv* env, jobject clazz, jobject queueWeak, jobject jMsgQueue) { 199 sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, jMsgQueue); 200 if (messageQueue == NULL) { 201 jniThrowRuntimeException(env, "MessageQueue is not initialized."); 202 return 0; 203 } 204 sp<InputQueue> queue = InputQueue::createQueue(queueWeak, messageQueue->getLooper()); 205 if (!queue.get()) { 206 jniThrowRuntimeException(env, "InputQueue failed to initialize"); 207 return 0; 208 } 209 queue->incStrong(&gInputQueueClassInfo); 210 return reinterpret_cast<jlong>(queue.get()); 211 } 212 213 static void nativeDispose(JNIEnv* env, jobject clazz, jlong ptr) { 214 sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr); 215 queue->detachLooper(); 216 queue->decStrong(&gInputQueueClassInfo); 217 } 218 219 static jlong nativeSendKeyEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj, 220 jboolean predispatch) { 221 InputQueue* queue = reinterpret_cast<InputQueue*>(ptr); 222 KeyEvent* event = queue->createKeyEvent(); 223 status_t status = android_view_KeyEvent_toNative(env, eventObj, event); 224 if (status) { 225 queue->recycleInputEvent(event); 226 jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); 227 return -1; 228 } 229 230 if (predispatch) { 231 event->setFlags(event->getFlags() | AKEY_EVENT_FLAG_PREDISPATCH); 232 } 233 234 queue->enqueueEvent(event); 235 return reinterpret_cast<jlong>(event); 236 } 237 238 static jlong nativeSendMotionEvent(JNIEnv* env, jobject clazz, jlong ptr, jobject eventObj) { 239 sp<InputQueue> queue = reinterpret_cast<InputQueue*>(ptr); 240 MotionEvent* originalEvent = android_view_MotionEvent_getNativePtr(env, eventObj); 241 if (!originalEvent) { 242 jniThrowRuntimeException(env, "Could not obtain MotionEvent pointer."); 243 return -1; 244 } 245 MotionEvent* event = queue->createMotionEvent(); 246 event->copyFrom(originalEvent, true /* keepHistory */); 247 queue->enqueueEvent(event); 248 return reinterpret_cast<jlong>(event); 249 } 250 251 static const JNINativeMethod g_methods[] = { 252 { "nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;)J", 253 (void*) nativeInit }, 254 { "nativeDispose", "(J)V", (void*) nativeDispose }, 255 { "nativeSendKeyEvent", "(JLandroid/view/KeyEvent;Z)J", (void*) nativeSendKeyEvent }, 256 { "nativeSendMotionEvent", "(JLandroid/view/MotionEvent;)J", (void*) nativeSendMotionEvent }, 257 }; 258 259 static const char* const kInputQueuePathName = "android/view/InputQueue"; 260 261 int register_android_view_InputQueue(JNIEnv* env) 262 { 263 jclass clazz = FindClassOrDie(env, kInputQueuePathName); 264 gInputQueueClassInfo.finishInputEvent = GetMethodIDOrDie(env, clazz, "finishInputEvent", 265 "(JZ)V"); 266 267 return RegisterMethodsOrDie(env, kInputQueuePathName, g_methods, NELEM(g_methods)); 268 } 269 270 } // namespace android 271