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