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_TvKeys.h"
20
21#include "jni.h"
22#include <android_runtime/AndroidRuntime.h>
23#include <ScopedUtfChars.h>
24#include <android/keycodes.h>
25
26#include <utils/BitSet.h>
27#include <utils/Errors.h>
28#include <utils/misc.h>
29#include <utils/Log.h>
30#include <utils/String8.h>
31
32#include <ctype.h>
33#include <linux/input.h>
34#include <unistd.h>
35#include <sys/time.h>
36#include <time.h>
37#include <stdint.h>
38#include <map>
39#include <fcntl.h>
40#include <linux/uinput.h>
41#include <signal.h>
42#include <sys/inotify.h>
43#include <sys/stat.h>
44#include <sys/types.h>
45
46// Refer to EventHub.h
47#define MSC_ANDROID_TIME_SEC 0x6
48#define MSC_ANDROID_TIME_USEC 0x7
49
50#define SLOT_UNKNOWN -1
51
52namespace android {
53
54static std::map<int32_t,int> keysMap;
55static std::map<int32_t,int32_t> slotsMap;
56static BitSet32 mtSlots;
57
58static void initKeysMap() {
59    if (keysMap.empty()) {
60        for (size_t i = 0; i < NELEM(KEYS); i++) {
61            keysMap[KEYS[i].androidKeyCode] = KEYS[i].linuxKeyCode;
62        }
63    }
64}
65
66static int32_t getLinuxKeyCode(int32_t androidKeyCode) {
67    std::map<int,int>::iterator it = keysMap.find(androidKeyCode);
68    if (it != keysMap.end()) {
69        return it->second;
70    }
71    return KEY_UNKNOWN;
72}
73
74static int findSlot(int32_t pointerId) {
75    std::map<int,int>::iterator it = slotsMap.find(pointerId);
76    if (it != slotsMap.end()) {
77        return it->second;
78    }
79    return SLOT_UNKNOWN;
80}
81
82static int assignSlot(int32_t pointerId) {
83    if (!mtSlots.isFull()) {
84        uint32_t slot = mtSlots.markFirstUnmarkedBit();
85        slotsMap[pointerId] = slot;
86        return slot;
87    }
88    return SLOT_UNKNOWN;
89}
90
91static void unassignSlot(int32_t pointerId) {
92    int slot = findSlot(pointerId);
93    if (slot != SLOT_UNKNOWN) {
94        mtSlots.clearBit(slot);
95        slotsMap.erase(pointerId);
96    }
97}
98
99class NativeConnection {
100public:
101    ~NativeConnection();
102
103    static NativeConnection* open(const char* name, const char* uniqueId,
104            int32_t width, int32_t height, int32_t maxPointerId);
105
106    void sendEvent(int32_t type, int32_t code, int32_t value);
107
108    int32_t getMaxPointers() const { return mMaxPointers; }
109
110private:
111    NativeConnection(int fd, int32_t maxPointers);
112
113    const int mFd;
114    const int32_t mMaxPointers;
115};
116
117NativeConnection::NativeConnection(int fd, int32_t maxPointers) :
118        mFd(fd), mMaxPointers(maxPointers) {
119}
120
121NativeConnection::~NativeConnection() {
122    ALOGI("Un-Registering uinput device %d.", mFd);
123    ioctl(mFd, UI_DEV_DESTROY);
124    close(mFd);
125}
126
127NativeConnection* NativeConnection::open(const char* name, const char* uniqueId,
128        int32_t width, int32_t height, int32_t maxPointers) {
129    ALOGI("Registering uinput device %s: touch pad size %dx%d, "
130            "max pointers %d.", name, width, height, maxPointers);
131
132    int fd = ::open("/dev/uinput", O_WRONLY | O_NDELAY);
133    if (fd < 0) {
134        ALOGE("Cannot open /dev/uinput: %s.", strerror(errno));
135        return nullptr;
136    }
137
138    struct uinput_user_dev uinp;
139    memset(&uinp, 0, sizeof(struct uinput_user_dev));
140    strlcpy(uinp.name, name, UINPUT_MAX_NAME_SIZE);
141    uinp.id.version = 1;
142    uinp.id.bustype = BUS_VIRTUAL;
143
144    // initialize keymap
145    initKeysMap();
146
147    // write device unique id to the phys property
148    ioctl(fd, UI_SET_PHYS, uniqueId);
149
150    // set the keys mapped
151    ioctl(fd, UI_SET_EVBIT, EV_KEY);
152    for (size_t i = 0; i < NELEM(KEYS); i++) {
153        ioctl(fd, UI_SET_KEYBIT, KEYS[i].linuxKeyCode);
154    }
155
156    // set the misc events maps
157    ioctl(fd, UI_SET_EVBIT, EV_MSC);
158    ioctl(fd, UI_SET_MSCBIT, MSC_ANDROID_TIME_SEC);
159    ioctl(fd, UI_SET_MSCBIT, MSC_ANDROID_TIME_USEC);
160
161    // register the input device
162    if (write(fd, &uinp, sizeof(uinp)) != sizeof(uinp)) {
163        ALOGE("Cannot write uinput_user_dev to fd %d: %s.", fd, strerror(errno));
164        close(fd);
165        return NULL;
166    }
167    if (ioctl(fd, UI_DEV_CREATE) != 0) {
168        ALOGE("Unable to create uinput device: %s.", strerror(errno));
169        close(fd);
170        return nullptr;
171    }
172
173    ALOGV("Created uinput device, fd=%d.", fd);
174    return new NativeConnection(fd, maxPointers);
175}
176
177void NativeConnection::sendEvent(int32_t type, int32_t code, int32_t value) {
178    struct input_event iev;
179    memset(&iev, 0, sizeof(iev));
180    iev.type = type;
181    iev.code = code;
182    iev.value = value;
183    write(mFd, &iev, sizeof(iev));
184}
185
186
187static jlong nativeOpen(JNIEnv* env, jclass clazz,
188        jstring nameStr, jstring uniqueIdStr,
189        jint width, jint height, jint maxPointers) {
190    ScopedUtfChars name(env, nameStr);
191    ScopedUtfChars uniqueId(env, uniqueIdStr);
192
193    NativeConnection* connection = NativeConnection::open(name.c_str(), uniqueId.c_str(),
194            width, height, maxPointers);
195    return reinterpret_cast<jlong>(connection);
196}
197
198static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
199    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
200    delete connection;
201}
202
203static void nativeSendTimestamp(JNIEnv* env, jclass clazz, jlong ptr, jlong timestamp) {
204    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
205
206    connection->sendEvent(EV_MSC, MSC_ANDROID_TIME_SEC, timestamp / 1000L);
207    connection->sendEvent(EV_MSC, MSC_ANDROID_TIME_USEC, (timestamp % 1000L) * 1000L);
208}
209
210static void nativeSendKey(JNIEnv* env, jclass clazz, jlong ptr, jint keyCode, jboolean down) {
211    int32_t code = getLinuxKeyCode(keyCode);
212    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
213    if (code != KEY_UNKNOWN) {
214        connection->sendEvent(EV_KEY, code, down ? 1 : 0);
215    } else {
216        ALOGE("Received an unknown keycode of %d.", keyCode);
217    }
218}
219
220static void nativeSendPointerDown(JNIEnv* env, jclass clazz, jlong ptr,
221        jint pointerId, jint x, jint y) {
222    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
223
224    int32_t slot = findSlot(pointerId);
225    if (slot == SLOT_UNKNOWN) {
226        slot = assignSlot(pointerId);
227    }
228    if (slot != SLOT_UNKNOWN) {
229        connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
230        connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, pointerId);
231        connection->sendEvent(EV_ABS, ABS_MT_POSITION_X, x);
232        connection->sendEvent(EV_ABS, ABS_MT_POSITION_Y, y);
233    }
234}
235
236static void nativeSendPointerUp(JNIEnv* env, jclass clazz, jlong ptr,
237        jint pointerId) {
238    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
239
240    int32_t slot = findSlot(pointerId);
241    if (slot != SLOT_UNKNOWN) {
242        connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
243        connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
244        unassignSlot(pointerId);
245    }
246}
247
248static void nativeSendPointerSync(JNIEnv* env, jclass clazz, jlong ptr) {
249    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
250    connection->sendEvent(EV_SYN, SYN_REPORT, 0);
251}
252
253static void nativeClear(JNIEnv* env, jclass clazz, jlong ptr) {
254    NativeConnection* connection = reinterpret_cast<NativeConnection*>(ptr);
255
256    // Clear keys.
257    for (size_t i = 0; i < NELEM(KEYS); i++) {
258        connection->sendEvent(EV_KEY, KEYS[i].linuxKeyCode, 0);
259    }
260
261    // Clear pointers.
262    int32_t slot = SLOT_UNKNOWN;
263    for (int32_t i = 0; i < connection->getMaxPointers(); i++) {
264        slot = findSlot(i);
265        if (slot != SLOT_UNKNOWN) {
266            connection->sendEvent(EV_ABS, ABS_MT_SLOT, slot);
267            connection->sendEvent(EV_ABS, ABS_MT_TRACKING_ID, -1);
268        }
269    }
270
271    // Sync pointer events
272    connection->sendEvent(EV_SYN, SYN_REPORT, 0);
273}
274
275/*
276 * JNI registration
277 */
278
279static JNINativeMethod gUinputBridgeMethods[] = {
280    { "nativeOpen", "(Ljava/lang/String;Ljava/lang/String;III)J",
281        (void*)nativeOpen },
282    { "nativeClose", "(J)V",
283        (void*)nativeClose },
284    { "nativeSendTimestamp", "(JJ)V",
285        (void*)nativeSendTimestamp },
286    { "nativeSendKey", "(JIZ)V",
287        (void*)nativeSendKey },
288    { "nativeSendPointerDown", "(JIII)V",
289        (void*)nativeSendPointerDown },
290    { "nativeSendPointerUp", "(JI)V",
291        (void*)nativeSendPointerUp },
292    { "nativeClear", "(J)V",
293        (void*)nativeClear },
294    { "nativeSendPointerSync", "(J)V",
295        (void*)nativeSendPointerSync },
296};
297
298int register_android_server_tv_TvUinputBridge(JNIEnv* env) {
299    int res = jniRegisterNativeMethods(env, "com/android/server/tv/UinputBridge",
300              gUinputBridgeMethods, NELEM(gUinputBridgeMethods));
301
302    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
303    (void)res; // Don't complain about unused variable in the LOG_NDEBUG case
304
305    return 0;
306}
307
308} // namespace android
309