1 /*
2 * Copyright 2016, 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 "TvRemote-native-uiBridge"
18
19 #include "com_android_server_tv_GamepadKeys.h"
20 #include "com_android_server_tv_TvKeys.h"
21
22 #include "jni.h"
23 #include <android_runtime/AndroidRuntime.h>
24 #include <nativehelper/ScopedUtfChars.h>
25 #include <android/keycodes.h>
26
27 #include <utils/BitSet.h>
28 #include <utils/Errors.h>
29 #include <utils/misc.h>
30 #include <utils/Log.h>
31 #include <utils/String8.h>
32
33 #include <ctype.h>
34 #include <fcntl.h>
35 #include <linux/input.h>
36 #include <linux/uinput.h>
37 #include <signal.h>
38 #include <stdint.h>
39 #include <sys/inotify.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42 #include <sys/types.h>
43 #include <time.h>
44 #include <unistd.h>
45 #include <unordered_map>
46
47 #define SLOT_UNKNOWN -1
48
49 namespace android {
50
51 #define GOOGLE_VENDOR_ID 0x18d1
52
53 #define GOOGLE_VIRTUAL_REMOTE_PRODUCT_ID 0x0100
54 #define GOOGLE_VIRTUAL_GAMEPAD_PROUCT_ID 0x0200
55
56 static std::unordered_map<int32_t, int> keysMap;
57 static std::unordered_map<int32_t, int32_t> slotsMap;
58 static BitSet32 mtSlots;
59
60 // Maps android key code to linux key code.
61 static std::unordered_map<int32_t, int> gamepadAndroidToLinuxKeyMap;
62
63 // Maps an android gamepad axis to the index within the GAMEPAD_AXES array.
64 static std::unordered_map<int32_t, int> gamepadAndroidAxisToIndexMap;
65
initKeysMap()66 static void initKeysMap() {
67 if (keysMap.empty()) {
68 for (size_t i = 0; i < NELEM(KEYS); i++) {
69 keysMap[KEYS[i].androidKeyCode] = KEYS[i].linuxKeyCode;
70 }
71 }
72 }
73
initGamepadKeyMap()74 static void initGamepadKeyMap() {
75 if (gamepadAndroidToLinuxKeyMap.empty()) {
76 for (size_t i = 0; i < NELEM(GAMEPAD_KEYS); i++) {
77 gamepadAndroidToLinuxKeyMap[GAMEPAD_KEYS[i].androidKeyCode] =
78 GAMEPAD_KEYS[i].linuxUinputKeyCode;
79 }
80 }
81
82 if (gamepadAndroidAxisToIndexMap.empty()) {
83 for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) {
84 gamepadAndroidAxisToIndexMap[GAMEPAD_AXES[i].androidAxis] = i;
85 }
86 }
87 }
88
getLinuxKeyCode(int32_t androidKeyCode)89 static int32_t getLinuxKeyCode(int32_t androidKeyCode) {
90 std::unordered_map<int, int>::iterator it = keysMap.find(androidKeyCode);
91 if (it != keysMap.end()) {
92 return it->second;
93 }
94 return KEY_UNKNOWN;
95 }
96
getGamepadkeyCode(int32_t androidKeyCode)97 static int getGamepadkeyCode(int32_t androidKeyCode) {
98 std::unordered_map<int32_t, int>::iterator it =
99 gamepadAndroidToLinuxKeyMap.find(androidKeyCode);
100 if (it != gamepadAndroidToLinuxKeyMap.end()) {
101 return it->second;
102 }
103 return KEY_UNKNOWN;
104 }
105
getGamepadAxis(int32_t androidAxisCode)106 static const GamepadAxis* getGamepadAxis(int32_t androidAxisCode) {
107 std::unordered_map<int32_t, int>::iterator it =
108 gamepadAndroidAxisToIndexMap.find(androidAxisCode);
109 if (it == gamepadAndroidAxisToIndexMap.end()) {
110 return nullptr;
111 }
112 return &GAMEPAD_AXES[it->second];
113 }
114
findSlot(int32_t pointerId)115 static int findSlot(int32_t pointerId) {
116 std::unordered_map<int, int>::iterator it = slotsMap.find(pointerId);
117 if (it != slotsMap.end()) {
118 return it->second;
119 }
120 return SLOT_UNKNOWN;
121 }
122
assignSlot(int32_t pointerId)123 static int assignSlot(int32_t pointerId) {
124 if (!mtSlots.isFull()) {
125 uint32_t slot = mtSlots.markFirstUnmarkedBit();
126 slotsMap[pointerId] = slot;
127 return slot;
128 }
129 return SLOT_UNKNOWN;
130 }
131
unassignSlot(int32_t pointerId)132 static void unassignSlot(int32_t pointerId) {
133 int slot = findSlot(pointerId);
134 if (slot != SLOT_UNKNOWN) {
135 mtSlots.clearBit(slot);
136 slotsMap.erase(pointerId);
137 }
138 }
139
140 static const int kInvalidFileDescriptor = -1;
141
142 // Convenience class to manage an opened /dev/uinput device
143 class UInputDescriptor {
144 public:
UInputDescriptor()145 UInputDescriptor() : mFd(kInvalidFileDescriptor) {
146 memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor));
147 }
148
149 // Auto-closes any open /dev/uinput descriptor unless detached.
150 ~UInputDescriptor();
151
152 // Open /dev/uinput and prepare to register
153 // the device with the given name and unique Id
154 bool Open(const char* name, const char* uniqueId, uint16_t product);
155
156 // Checks if the current file descriptor is valid
IsValid() const157 bool IsValid() const { return mFd != kInvalidFileDescriptor; }
158
159 void EnableKey(int keyCode);
160
161 void EnableAxesEvents();
162 void EnableAxis(int axis, int rangeMin, int rangeMax);
163
164 bool Create();
165
166 // Detaches from the current file descriptor
167 // Returns the file descriptor for /dev/uniput
168 int Detach();
169
170 private:
171 int mFd;
172 struct uinput_user_dev mUinputDescriptor;
173 };
174
~UInputDescriptor()175 UInputDescriptor::~UInputDescriptor() {
176 if (mFd != kInvalidFileDescriptor) {
177 close(mFd);
178 mFd = kInvalidFileDescriptor;
179 }
180 }
181
Detach()182 int UInputDescriptor::Detach() {
183 int fd = mFd;
184 mFd = kInvalidFileDescriptor;
185 return fd;
186 }
187
Open(const char * name,const char * uniqueId,uint16_t product)188 bool UInputDescriptor::Open(const char* name, const char* uniqueId, uint16_t product) {
189 if (IsValid()) {
190 ALOGE("UInput device already open");
191 return false;
192 }
193
194 mFd = ::open("/dev/uinput", O_WRONLY | O_NDELAY);
195 if (mFd < 0) {
196 ALOGE("Cannot open /dev/uinput: %s.", strerror(errno));
197 mFd = kInvalidFileDescriptor;
198 return false;
199 }
200
201 // write device unique id to the phys property
202 ioctl(mFd, UI_SET_PHYS, uniqueId);
203
204 memset(&mUinputDescriptor, 0, sizeof(mUinputDescriptor));
205 strlcpy(mUinputDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
206 mUinputDescriptor.id.version = 1;
207 mUinputDescriptor.id.bustype = BUS_VIRTUAL;
208 mUinputDescriptor.id.vendor = GOOGLE_VENDOR_ID;
209 mUinputDescriptor.id.product = product;
210
211 // All UInput devices we use process keys
212 ioctl(mFd, UI_SET_EVBIT, EV_KEY);
213
214 return true;
215 }
216
EnableKey(int keyCode)217 void UInputDescriptor::EnableKey(int keyCode) {
218 ioctl(mFd, UI_SET_KEYBIT, keyCode);
219 }
220
EnableAxesEvents()221 void UInputDescriptor::EnableAxesEvents() {
222 ioctl(mFd, UI_SET_EVBIT, EV_ABS);
223 }
224
EnableAxis(int axis,int rangeMin,int rangeMax)225 void UInputDescriptor::EnableAxis(int axis, int rangeMin, int rangeMax) {
226 if ((axis < 0) || (axis >= NELEM(mUinputDescriptor.absmin))) {
227 ALOGE("Invalid axis number: %d", axis);
228 return;
229 }
230
231 if (ioctl(mFd, UI_SET_ABSBIT, axis) != 0) {
232 ALOGE("Failed to set absbit for %d", axis);
233 }
234
235 mUinputDescriptor.absmin[axis] = rangeMin;
236 mUinputDescriptor.absmax[axis] = rangeMax;
237 mUinputDescriptor.absfuzz[axis] = 0;
238 mUinputDescriptor.absflat[axis] = 0;
239 }
240
Create()241 bool UInputDescriptor::Create() {
242 // register the input device
243 if (write(mFd, &mUinputDescriptor, sizeof(mUinputDescriptor)) != sizeof(mUinputDescriptor)) {
244 ALOGE("Cannot write uinput_user_dev to fd %d: %s.", mFd, strerror(errno));
245 return false;
246 }
247
248 if (ioctl(mFd, UI_DEV_CREATE) != 0) {
249 ALOGE("Unable to create uinput device: %s.", strerror(errno));
250 return false;
251 }
252
253 ALOGV("Created uinput device, fd=%d.", mFd);
254
255 return true;
256 }
257
258 class NativeConnection {
259 public:
260 enum class ConnectionType {
261 kRemoteDevice,
262 kGamepadDevice,
263 };
264
265 ~NativeConnection();
266
267 static NativeConnection* open(const char* name, const char* uniqueId,
268 int32_t width, int32_t height, int32_t maxPointerId);
269
270 static NativeConnection* openGamepad(const char* name, const char* uniqueId);
271
272 void sendEvent(int32_t type, int32_t code, int32_t value);
273
getMaxPointers() const274 int32_t getMaxPointers() const { return mMaxPointers; }
275
getType() const276 ConnectionType getType() const { return mType; }
277
IsGamepad() const278 bool IsGamepad() const { return getType() == ConnectionType::kGamepadDevice; }
279
IsRemote() const280 bool IsRemote() const { return getType() == ConnectionType::kRemoteDevice; }
281
282 private:
283 NativeConnection(int fd, int32_t maxPointers, ConnectionType type);
284
285 const int mFd;
286 const int32_t mMaxPointers;
287 const ConnectionType mType;
288 };
289
NativeConnection(int fd,int32_t maxPointers,ConnectionType type)290 NativeConnection::NativeConnection(int fd, int32_t maxPointers, ConnectionType type)
291 : mFd(fd), mMaxPointers(maxPointers), mType(type) {}
292
~NativeConnection()293 NativeConnection::~NativeConnection() {
294 ALOGI("Un-Registering uinput device %d.", mFd);
295 ioctl(mFd, UI_DEV_DESTROY);
296 close(mFd);
297 }
298
open(const char * name,const char * uniqueId,int32_t width,int32_t height,int32_t maxPointers)299 NativeConnection* NativeConnection::open(const char* name, const char* uniqueId,
300 int32_t width, int32_t height, int32_t maxPointers) {
301 ALOGI("Registering uinput device %s: touch pad size %dx%d, "
302 "max pointers %d.", name, width, height, maxPointers);
303
304 initKeysMap();
305
306 UInputDescriptor descriptor;
307 if (!descriptor.Open(name, uniqueId, GOOGLE_VIRTUAL_REMOTE_PRODUCT_ID)) {
308 return nullptr;
309 }
310
311 // set the keys mapped
312 for (size_t i = 0; i < NELEM(KEYS); i++) {
313 descriptor.EnableKey(KEYS[i].linuxKeyCode);
314 }
315
316 if (!descriptor.Create()) {
317 return nullptr;
318 }
319
320 return new NativeConnection(descriptor.Detach(), maxPointers, ConnectionType::kRemoteDevice);
321 }
322
openGamepad(const char * name,const char * uniqueId)323 NativeConnection* NativeConnection::openGamepad(const char* name, const char* uniqueId) {
324 ALOGI("Registering uinput device %s: gamepad", name);
325
326 initGamepadKeyMap();
327
328 UInputDescriptor descriptor;
329 if (!descriptor.Open(name, uniqueId, GOOGLE_VIRTUAL_GAMEPAD_PROUCT_ID)) {
330 return nullptr;
331 }
332
333 // set the keys mapped for gamepads
334 for (size_t i = 0; i < NELEM(GAMEPAD_KEYS); i++) {
335 descriptor.EnableKey(GAMEPAD_KEYS[i].linuxUinputKeyCode);
336 }
337
338 // define the axes that are required
339 descriptor.EnableAxesEvents();
340 for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) {
341 const GamepadAxis& axis = GAMEPAD_AXES[i];
342 descriptor.EnableAxis(axis.linuxUinputAxis, axis.linuxUinputRangeMin,
343 axis.linuxUinputRangeMax);
344 }
345
346 if (!descriptor.Create()) {
347 return nullptr;
348 }
349
350 return new NativeConnection(descriptor.Detach(), 0, ConnectionType::kGamepadDevice);
351 }
352
sendEvent(int32_t type,int32_t code,int32_t value)353 void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) {
354 struct input_event iev;
355 memset(&iev, 0, sizeof(iev));
356 iev.type = type;
357 iev.code = code;
358 iev.value = value;
359 write(mFd, &iev, sizeof(iev));
360 }
361
nativeOpen(JNIEnv * env,jclass clazz,jstring nameStr,jstring uniqueIdStr,jint width,jint height,jint maxPointers)362 static jlong nativeOpen(JNIEnv* env, jclass clazz,
363 jstring nameStr, jstring uniqueIdStr,
364 jint width, jint height, jint maxPointers) {
365 ScopedUtfChars name(env, nameStr);
366 ScopedUtfChars uniqueId(env, uniqueIdStr);
367
368 NativeConnection* connection = NativeConnection::open(name.c_str(), uniqueId.c_str(),
369 width, height, maxPointers);
370 return reinterpret_cast<jlong>(connection);
371 }
372
nativeGamepadOpen(JNIEnv * env,jclass clazz,jstring nameStr,jstring uniqueIdStr)373 static jlong nativeGamepadOpen(JNIEnv* env, jclass clazz, jstring nameStr, jstring uniqueIdStr) {
374 ScopedUtfChars name(env, nameStr);
375 ScopedUtfChars uniqueId(env, uniqueIdStr);
376
377 NativeConnection* connection = NativeConnection::openGamepad(name.c_str(), uniqueId.c_str());
378 return reinterpret_cast<jlong>(connection);
379 }
380
nativeClose(JNIEnv * env,jclass clazz,jlong ptr)381 static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
382 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
383 delete connection;
384 }
385
nativeSendKey(JNIEnv * env,jclass clazz,jlong ptr,jint keyCode,jboolean down)386 static void nativeSendKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode, jboolean down) {
387 int32_t code = getLinuxKeyCode(keyCode);
388 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
389
390 if (connection->IsGamepad()) {
391 ALOGE("Invalid key even for a gamepad - need to send gamepad events");
392 return;
393 }
394
395 if (code != KEY_UNKNOWN) {
396 connection->sendEvent(EV_KEY, code, down ? 1 : 0);
397 } else {
398 ALOGE("Received an unknown keycode of %d.", keyCode);
399 }
400 }
401
nativeSendGamepadKey(JNIEnv * env,jclass clazz,jlong ptr,jint keyCode,jboolean down)402 static void nativeSendGamepadKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode,
403 jboolean down) {
404 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
405
406 if (!connection->IsGamepad()) {
407 ALOGE("Invalid gamepad key for non-gamepad device");
408 return;
409 }
410
411 int linuxKeyCode = getGamepadkeyCode(keyCode);
412 if (linuxKeyCode == KEY_UNKNOWN) {
413 ALOGE("Gamepad: received an unknown keycode of %d.", keyCode);
414 return;
415 }
416 connection->sendEvent(EV_KEY, linuxKeyCode, down ? 1 : 0);
417 }
418
nativeSendGamepadAxisValue(JNIEnv * env,jclass clazz,jlong ptr,jint axis,jfloat value)419 static void nativeSendGamepadAxisValue(JNIEnv* env, jclass clazz, jlong ptr, jint axis,
420 jfloat value) {
421 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
422
423 if (!connection->IsGamepad()) {
424 ALOGE("Invalid axis send for non-gamepad device");
425 return;
426 }
427
428 const GamepadAxis* axisInfo = getGamepadAxis(axis);
429 if (axisInfo == nullptr) {
430 ALOGE("Invalid axis: %d", axis);
431 return;
432 }
433
434 if (value > axisInfo->androidRangeMax) {
435 value = axisInfo->androidRangeMax;
436 } else if (value < axisInfo->androidRangeMin) {
437 value = axisInfo->androidRangeMin;
438 }
439
440 // Converts the android range into the device range
441 float movementPercent = (value - axisInfo->androidRangeMin) /
442 (axisInfo->androidRangeMax - axisInfo->androidRangeMin);
443 int axisRawValue = axisInfo->linuxUinputRangeMin +
444 movementPercent * (axisInfo->linuxUinputRangeMax - axisInfo->linuxUinputRangeMin);
445
446 connection->sendEvent(EV_ABS, axisInfo->linuxUinputAxis, axisRawValue);
447 }
448
nativeSendPointerDown(JNIEnv * env,jclass clazz,jlong ptr,jint pointerId,jint x,jint y)449 static void nativeSendPointerDown(JNIEnv* env, jclass clazz, jlong ptr,
450 jint pointerId, jint x, jint y) {
451 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
452
453 if (connection->IsGamepad()) {
454 ALOGE("Invalid pointer down event for a gamepad.");
455 return;
456 }
457
458 int32_t slot = findSlot(pointerId);
459 if (slot == SLOT_UNKNOWN) {
460 slot = assignSlot(pointerId);
461 }
462 if (slot != SLOT_UNKNOWN) {
463 connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
464 connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, pointerId);
465 connection->sendEvent(EV_ABS, ABS_MT_POSITION_X, x);
466 connection->sendEvent(EV_ABS, ABS_MT_POSITION_Y, y);
467 }
468 }
469
nativeSendPointerUp(JNIEnv * env,jclass clazz,jlong ptr,jint pointerId)470 static void nativeSendPointerUp(JNIEnv* env, jclass clazz, jlong ptr,
471 jint pointerId) {
472 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
473
474 if (connection->IsGamepad()) {
475 ALOGE("Invalid pointer up event for a gamepad.");
476 return;
477 }
478
479 int32_t slot = findSlot(pointerId);
480 if (slot != SLOT_UNKNOWN) {
481 connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
482 connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
483 unassignSlot(pointerId);
484 }
485 }
486
nativeSendPointerSync(JNIEnv * env,jclass clazz,jlong ptr)487 static void nativeSendPointerSync(JNIEnv* env, jclass clazz, jlong ptr) {
488 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
489 connection->sendEvent(EV_SYN, SYN_REPORT, 0);
490 }
491
nativeClear(JNIEnv * env,jclass clazz,jlong ptr)492 static void nativeClear(JNIEnv* env, jclass clazz, jlong ptr) {
493 NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
494
495 // Clear keys.
496 if (connection->IsRemote()) {
497 for (size_t i = 0; i < NELEM(KEYS); i++) {
498 connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0);
499 }
500
501 // Clear pointers.
502 int32_t slot = SLOT_UNKNOWN;
503 for (int32_t i = 0; i < connection->getMaxPointers(); i++) {
504 slot = findSlot(i);
505 if (slot != SLOT_UNKNOWN) {
506 connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
507 connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
508 }
509 }
510 } else {
511 for (size_t i = 0; i < NELEM(GAMEPAD_KEYS); i++) {
512 connection->sendEvent(EV_KEY, GAMEPAD_KEYS[i].linuxUinputKeyCode, 0);
513 }
514
515 for (size_t i = 0; i < NELEM(GAMEPAD_AXES); i++) {
516 const GamepadAxis& axis = GAMEPAD_AXES[i];
517
518 if ((axis.linuxUinputAxis == ABS_Z) || (axis.linuxUinputAxis == ABS_RZ)) {
519 // Mark triggers unpressed
520 connection->sendEvent(EV_ABS, axis.linuxUinputAxis, axis.linuxUinputRangeMin);
521 } else {
522 // Joysticks and dpad rests on center
523 connection->sendEvent(EV_ABS, axis.linuxUinputAxis,
524 (axis.linuxUinputRangeMin + axis.linuxUinputRangeMax) / 2);
525 }
526 }
527 }
528
529 // Sync pointer events
530 connection->sendEvent(EV_SYN, SYN_REPORT, 0);
531 }
532
533 /*
534 * JNI registration
535 */
536
537 static JNINativeMethod gUinputBridgeMethods[] = {
538 {"nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J", (void*)nativeOpen},
539 {"nativeGamepadOpen", "(Ljava/lang/String;Ljava/lang/String;)J", (void*)nativeGamepadOpen},
540 {"nativeClose", "(J)V", (void*)nativeClose},
541 {"nativeSendKey", "(JIZ)V", (void*)nativeSendKey},
542 {"nativeSendPointerDown", "(JIII)V", (void*)nativeSendPointerDown},
543 {"nativeSendPointerUp", "(JI)V", (void*)nativeSendPointerUp},
544 {"nativeClear", "(J)V", (void*)nativeClear},
545 {"nativeSendPointerSync", "(J)V", (void*)nativeSendPointerSync},
546 {"nativeSendGamepadKey", "(JIZ)V", (void*)nativeSendGamepadKey},
547 {"nativeSendGamepadAxisValue", "(JIF)V", (void*)nativeSendGamepadAxisValue},
548 };
549
register_android_server_tv_TvUinputBridge(JNIEnv * env)550 int register_android_server_tv_TvUinputBridge(JNIEnv* env) {
551 int res = jniRegisterNativeMethods(env, "com/android/server/tv/UinputBridge",
552 gUinputBridgeMethods, NELEM(gUinputBridgeMethods));
553
554 LOG_FATAL_IF(res < 0, "Unable to register native methods.");
555 (void)res; // Don't complain about unused variable in the LOG_NDEBUG case
556
557 return 0;
558 }
559
560 } // namespace android
561