1/*
2 * Copyright (C) 2015 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 "HidCommandDevice"
18
19#include "com_android_commands_hid_Device.h"
20
21#include <linux/uhid.h>
22
23#include <fcntl.h>
24#include <cstdio>
25#include <cstring>
26#include <memory>
27#include <unistd.h>
28
29#include <android_runtime/AndroidRuntime.h>
30#include <android_runtime/Log.h>
31#include <android_os_MessageQueue.h>
32#include <core_jni_helpers.h>
33#include <jni.h>
34#include <JNIHelp.h>
35#include <ScopedPrimitiveArray.h>
36#include <ScopedUtfChars.h>
37#include <utils/Log.h>
38#include <utils/Looper.h>
39#include <utils/StrongPointer.h>
40
41namespace android {
42namespace uhid {
43
44static const char* UHID_PATH = "/dev/uhid";
45static const size_t UHID_MAX_NAME_LENGTH = 128;
46
47static struct {
48    jmethodID onDeviceOpen;
49    jmethodID onDeviceError;
50} gDeviceCallbackClassInfo;
51
52static int handleLooperEvents(int /* fd */, int events, void* data) {
53    Device* d = reinterpret_cast<Device*>(data);
54    return d->handleEvents(events);
55}
56
57static void checkAndClearException(JNIEnv* env, const char* methodName) {
58    if (env->ExceptionCheck()) {
59        ALOGE("An exception was thrown by callback '%s'.", methodName);
60        LOGE_EX(env);
61        env->ExceptionClear();
62    }
63}
64
65DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) :
66    mCallbackObject(env->NewGlobalRef(callback)) { }
67
68DeviceCallback::~DeviceCallback() {
69    JNIEnv* env = AndroidRuntime::getJNIEnv();
70    env->DeleteGlobalRef(mCallbackObject);
71}
72
73void DeviceCallback::onDeviceError() {
74    JNIEnv* env = AndroidRuntime::getJNIEnv();
75    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
76    checkAndClearException(env, "onDeviceError");
77}
78
79void DeviceCallback::onDeviceOpen() {
80    JNIEnv* env = AndroidRuntime::getJNIEnv();
81    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen);
82    checkAndClearException(env, "onDeviceOpen");
83}
84
85Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
86        std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize,
87        std::unique_ptr<DeviceCallback> callback, sp<Looper> looper) {
88
89    int fd = ::open(UHID_PATH, O_RDWR | O_CLOEXEC);
90    if (fd < 0) {
91        ALOGE("Failed to open uhid: %s", strerror(errno));
92        return nullptr;
93    }
94
95    struct uhid_event ev;
96    memset(&ev, 0, sizeof(ev));
97    ev.type = UHID_CREATE;
98    strncpy((char*)ev.u.create.name, name, UHID_MAX_NAME_LENGTH);
99    ev.u.create.rd_data = descriptor.get();
100    ev.u.create.rd_size = descriptorSize;
101    ev.u.create.bus = BUS_BLUETOOTH;
102    ev.u.create.vendor = vid;
103    ev.u.create.product = pid;
104    ev.u.create.version = 0;
105    ev.u.create.country = 0;
106
107    errno = 0;
108    ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
109    if (ret < 0 || ret != sizeof(ev)) {
110        ::close(fd);
111        ALOGE("Failed to create uhid node: %s", strerror(errno));
112        return nullptr;
113    }
114
115    // Wait for the device to actually be created.
116    ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev)));
117    if (ret < 0 || ev.type != UHID_START) {
118        ::close(fd);
119        ALOGE("uhid node failed to start: %s", strerror(errno));
120        return nullptr;
121    }
122
123    return new Device(id, fd, std::move(callback), looper);
124}
125
126Device::Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback, sp<Looper> looper) :
127            mId(id), mFd(fd), mDeviceCallback(std::move(callback)), mLooper(looper) {
128    looper->addFd(fd, 0, Looper::EVENT_INPUT, handleLooperEvents, reinterpret_cast<void*>(this));
129}
130
131Device::~Device() {
132    mLooper->removeFd(mFd);
133    struct uhid_event ev;
134    memset(&ev, 0, sizeof(ev));
135    ev.type = UHID_DESTROY;
136    TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
137    ::close(mFd);
138    mFd = -1;
139}
140
141void Device::sendReport(uint8_t* report, size_t reportSize) {
142    struct uhid_event ev;
143    memset(&ev, 0, sizeof(ev));
144    ev.type = UHID_INPUT;
145    ev.u.input.size = reportSize;
146    memcpy(&ev.u.input.data, report, reportSize);
147    ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
148    if (ret < 0 || ret != sizeof(ev)) {
149        ALOGE("Failed to send hid event: %s", strerror(errno));
150    }
151}
152
153int Device::handleEvents(int events) {
154    if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
155        ALOGE("uhid node was closed or an error occurred. events=0x%x", events);
156        mDeviceCallback->onDeviceError();
157        return 0;
158    }
159    struct uhid_event ev;
160    ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev)));
161    if (ret < 0) {
162        ALOGE("Failed to read from uhid node: %s", strerror(errno));
163        mDeviceCallback->onDeviceError();
164        return 0;
165    }
166
167    if (ev.type == UHID_OPEN) {
168        mDeviceCallback->onDeviceOpen();
169    }
170
171    return 1;
172}
173
174} // namespace uhid
175
176std::unique_ptr<uint8_t[]> getData(JNIEnv* env, jbyteArray javaArray, size_t& outSize) {
177    ScopedByteArrayRO scopedArray(env, javaArray);
178    outSize = scopedArray.size();
179    std::unique_ptr<uint8_t[]> data(new uint8_t[outSize]);
180    for (size_t i = 0; i < outSize; i++) {
181        data[i] = static_cast<uint8_t>(scopedArray[i]);
182    }
183    return data;
184}
185
186static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid,
187        jbyteArray rawDescriptor, jobject queue, jobject callback) {
188    ScopedUtfChars name(env, rawName);
189    if (name.c_str() == nullptr) {
190        return 0;
191    }
192
193    size_t size;
194    std::unique_ptr<uint8_t[]> desc = getData(env, rawDescriptor, size);
195
196    std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
197    sp<Looper> looper = android_os_MessageQueue_getMessageQueue(env, queue)->getLooper();
198
199    uhid::Device* d = uhid::Device::open(
200            id, reinterpret_cast<const char*>(name.c_str()), vid, pid,
201            std::move(desc), size, std::move(cb), std::move(looper));
202    return reinterpret_cast<jlong>(d);
203}
204
205static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr,jbyteArray rawReport) {
206    size_t size;
207    std::unique_ptr<uint8_t[]> report = getData(env, rawReport, size);
208    uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
209    if (d) {
210        d->sendReport(report.get(), size);
211    }
212}
213
214static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
215    uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
216    if (d) {
217        delete d;
218    }
219}
220
221static JNINativeMethod sMethods[] = {
222    { "nativeOpenDevice",
223            "(Ljava/lang/String;III[BLandroid/os/MessageQueue;"
224            "Lcom/android/commands/hid/Device$DeviceCallback;)J",
225            reinterpret_cast<void*>(openDevice) },
226    { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) },
227    { "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) },
228};
229
230int register_com_android_commands_hid_Device(JNIEnv* env) {
231    jclass clazz = FindClassOrDie(env, "com/android/commands/hid/Device$DeviceCallback");
232    uhid::gDeviceCallbackClassInfo.onDeviceOpen =
233            GetMethodIDOrDie(env, clazz, "onDeviceOpen", "()V");
234    uhid::gDeviceCallbackClassInfo.onDeviceError=
235            GetMethodIDOrDie(env, clazz, "onDeviceError", "()V");
236    return jniRegisterNativeMethods(env, "com/android/commands/hid/Device",
237            sMethods, NELEM(sMethods));
238}
239
240} // namespace android
241
242jint JNI_OnLoad(JavaVM* jvm, void*) {
243    JNIEnv *env = NULL;
244    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
245        return JNI_ERR;
246    }
247
248    if (android::register_com_android_commands_hid_Device(env) < 0 ){
249        return JNI_ERR;
250    }
251
252    return JNI_VERSION_1_6;
253}
254