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 
41 namespace android {
42 namespace uhid {
43 
44 static const char* UHID_PATH = "/dev/uhid";
45 static const size_t UHID_MAX_NAME_LENGTH = 128;
46 
47 static struct {
48     jmethodID onDeviceOpen;
49     jmethodID onDeviceError;
50 } gDeviceCallbackClassInfo;
51 
handleLooperEvents(int,int events,void * data)52 static int handleLooperEvents(int /* fd */, int events, void* data) {
53     Device* d = reinterpret_cast<Device*>(data);
54     return d->handleEvents(events);
55 }
56 
checkAndClearException(JNIEnv * env,const char * methodName)57 static 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 
DeviceCallback(JNIEnv * env,jobject callback)65 DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) :
66     mCallbackObject(env->NewGlobalRef(callback)) { }
67 
~DeviceCallback()68 DeviceCallback::~DeviceCallback() {
69     JNIEnv* env = AndroidRuntime::getJNIEnv();
70     env->DeleteGlobalRef(mCallbackObject);
71 }
72 
onDeviceError()73 void DeviceCallback::onDeviceError() {
74     JNIEnv* env = AndroidRuntime::getJNIEnv();
75     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
76     checkAndClearException(env, "onDeviceError");
77 }
78 
onDeviceOpen()79 void DeviceCallback::onDeviceOpen() {
80     JNIEnv* env = AndroidRuntime::getJNIEnv();
81     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen);
82     checkAndClearException(env, "onDeviceOpen");
83 }
84 
open(int32_t id,const char * name,int32_t vid,int32_t pid,std::unique_ptr<uint8_t[]> descriptor,size_t descriptorSize,std::unique_ptr<DeviceCallback> callback,sp<Looper> looper)85 Device* 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 
Device(int32_t id,int fd,std::unique_ptr<DeviceCallback> callback,sp<Looper> looper)126 Device::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 
~Device()131 Device::~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 
sendReport(uint8_t * report,size_t reportSize)141 void 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 
handleEvents(int events)153 int 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 
getData(JNIEnv * env,jbyteArray javaArray,size_t & outSize)176 std::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 
openDevice(JNIEnv * env,jclass,jstring rawName,jint id,jint vid,jint pid,jbyteArray rawDescriptor,jobject queue,jobject callback)186 static 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 
sendReport(JNIEnv * env,jclass,jlong ptr,jbyteArray rawReport)205 static 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 
closeDevice(JNIEnv *,jclass,jlong ptr)214 static 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 
221 static 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 
register_com_android_commands_hid_Device(JNIEnv * env)230 int 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 
JNI_OnLoad(JavaVM * jvm,void *)242 jint 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