blob: 5cc4fc4c16b22153d12a6c2454038328cb80efea [file] [log] [blame]
Michael Wright1f2c7682015-06-03 13:46:48 +01001/*
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
Michael Wright1f2c7682015-06-03 13:46:48 +010029#include <jni.h>
Steven Morelandc95dca82017-08-01 10:18:40 -070030#include <nativehelper/JNIHelp.h>
31#include <nativehelper/ScopedPrimitiveArray.h>
Steven Moreland65e2ca22017-08-10 15:55:12 -070032#include <nativehelper/ScopedUtfChars.h>
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070033#include <android/looper.h>
34#include <android/log.h>
35
36#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
37#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
38#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
39#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
Michael Wright1f2c7682015-06-03 13:46:48 +010040
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
Aurimas Liutikas1c8cbb52016-02-19 13:44:25 -080052static int handleLooperEvents(int /* fd */, int events, void* data) {
Michael Wright1f2c7682015-06-03 13:46:48 +010053 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()) {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070059 LOGE("An exception was thrown by callback '%s'.", methodName);
Michael Wright1f2c7682015-06-03 13:46:48 +010060 env->ExceptionClear();
61 }
62}
63
64DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) :
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070065 mCallbackObject(env->NewGlobalRef(callback)) {
66 env->GetJavaVM(&mJavaVM);
67 }
Michael Wright1f2c7682015-06-03 13:46:48 +010068
69DeviceCallback::~DeviceCallback() {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070070 JNIEnv* env = getJNIEnv();
Michael Wright1f2c7682015-06-03 13:46:48 +010071 env->DeleteGlobalRef(mCallbackObject);
72}
73
74void DeviceCallback::onDeviceError() {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070075 JNIEnv* env = getJNIEnv();
Michael Wright1f2c7682015-06-03 13:46:48 +010076 env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
77 checkAndClearException(env, "onDeviceError");
78}
79
80void DeviceCallback::onDeviceOpen() {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070081 JNIEnv* env = getJNIEnv();
Michael Wright1f2c7682015-06-03 13:46:48 +010082 env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen);
83 checkAndClearException(env, "onDeviceOpen");
84}
85
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070086JNIEnv* DeviceCallback::getJNIEnv() {
87 JNIEnv* env;
88 mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
89 return env;
90}
91
Michael Wright1f2c7682015-06-03 13:46:48 +010092Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
93 std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize,
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070094 std::unique_ptr<DeviceCallback> callback) {
Michael Wright1f2c7682015-06-03 13:46:48 +010095
96 int fd = ::open(UHID_PATH, O_RDWR | O_CLOEXEC);
97 if (fd < 0) {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -070098 LOGE("Failed to open uhid: %s", strerror(errno));
Michael Wright1f2c7682015-06-03 13:46:48 +010099 return nullptr;
100 }
101
102 struct uhid_event ev;
103 memset(&ev, 0, sizeof(ev));
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700104 ev.type = UHID_CREATE2;
105 strncpy((char*)ev.u.create2.name, name, UHID_MAX_NAME_LENGTH);
106 memcpy(&ev.u.create2.rd_data, descriptor.get(),
107 descriptorSize * sizeof(ev.u.create2.rd_data[0]));
108 ev.u.create2.rd_size = descriptorSize;
109 ev.u.create2.bus = BUS_BLUETOOTH;
110 ev.u.create2.vendor = vid;
111 ev.u.create2.product = pid;
112 ev.u.create2.version = 0;
113 ev.u.create2.country = 0;
Michael Wright1f2c7682015-06-03 13:46:48 +0100114
115 errno = 0;
116 ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
117 if (ret < 0 || ret != sizeof(ev)) {
118 ::close(fd);
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700119 LOGE("Failed to create uhid node: %s", strerror(errno));
Michael Wright1f2c7682015-06-03 13:46:48 +0100120 return nullptr;
121 }
122
123 // Wait for the device to actually be created.
124 ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev)));
125 if (ret < 0 || ev.type != UHID_START) {
126 ::close(fd);
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700127 LOGE("uhid node failed to start: %s", strerror(errno));
Michael Wright1f2c7682015-06-03 13:46:48 +0100128 return nullptr;
129 }
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700130 return new Device(id, fd, std::move(callback));
Michael Wright1f2c7682015-06-03 13:46:48 +0100131}
132
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700133Device::Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback) :
134 mId(id), mFd(fd), mDeviceCallback(std::move(callback)) {
135 ALooper* aLooper = ALooper_forThread();
136 if (aLooper == NULL) {
137 LOGE("Could not get ALooper, ALooper_forThread returned NULL");
138 aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
139 }
140 ALooper_addFd(aLooper, fd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents,
141 reinterpret_cast<void*>(this));
Michael Wright1f2c7682015-06-03 13:46:48 +0100142}
143
144Device::~Device() {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700145 ALooper* looper = ALooper_forThread();
146 if (looper != NULL) {
147 ALooper_removeFd(looper, mFd);
148 } else {
149 LOGE("Could not remove fd, ALooper_forThread() returned NULL!");
150 }
Michael Wright1f2c7682015-06-03 13:46:48 +0100151 struct uhid_event ev;
152 memset(&ev, 0, sizeof(ev));
153 ev.type = UHID_DESTROY;
154 TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
155 ::close(mFd);
156 mFd = -1;
157}
158
159void Device::sendReport(uint8_t* report, size_t reportSize) {
160 struct uhid_event ev;
161 memset(&ev, 0, sizeof(ev));
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700162 ev.type = UHID_INPUT2;
163 ev.u.input2.size = reportSize;
164 memcpy(&ev.u.input2.data, report, reportSize);
Michael Wright1f2c7682015-06-03 13:46:48 +0100165 ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
166 if (ret < 0 || ret != sizeof(ev)) {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700167 LOGE("Failed to send hid event: %s", strerror(errno));
Michael Wright1f2c7682015-06-03 13:46:48 +0100168 }
169}
170
171int Device::handleEvents(int events) {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700172 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
173 LOGE("uhid node was closed or an error occurred. events=0x%x", events);
Michael Wright1f2c7682015-06-03 13:46:48 +0100174 mDeviceCallback->onDeviceError();
175 return 0;
176 }
177 struct uhid_event ev;
178 ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev)));
179 if (ret < 0) {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700180 LOGE("Failed to read from uhid node: %s", strerror(errno));
Michael Wright1f2c7682015-06-03 13:46:48 +0100181 mDeviceCallback->onDeviceError();
182 return 0;
183 }
184
185 if (ev.type == UHID_OPEN) {
186 mDeviceCallback->onDeviceOpen();
187 }
188
189 return 1;
190}
191
192} // namespace uhid
193
194std::unique_ptr<uint8_t[]> getData(JNIEnv* env, jbyteArray javaArray, size_t& outSize) {
195 ScopedByteArrayRO scopedArray(env, javaArray);
196 outSize = scopedArray.size();
197 std::unique_ptr<uint8_t[]> data(new uint8_t[outSize]);
198 for (size_t i = 0; i < outSize; i++) {
199 data[i] = static_cast<uint8_t>(scopedArray[i]);
200 }
201 return data;
202}
203
Aurimas Liutikas1c8cbb52016-02-19 13:44:25 -0800204static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid,
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700205 jbyteArray rawDescriptor, jobject callback) {
Michael Wright1f2c7682015-06-03 13:46:48 +0100206 ScopedUtfChars name(env, rawName);
207 if (name.c_str() == nullptr) {
208 return 0;
209 }
210
211 size_t size;
212 std::unique_ptr<uint8_t[]> desc = getData(env, rawDescriptor, size);
213
214 std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
Michael Wright1f2c7682015-06-03 13:46:48 +0100215
216 uhid::Device* d = uhid::Device::open(
217 id, reinterpret_cast<const char*>(name.c_str()), vid, pid,
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700218 std::move(desc), size, std::move(cb));
Michael Wright1f2c7682015-06-03 13:46:48 +0100219 return reinterpret_cast<jlong>(d);
220}
221
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700222static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) {
Michael Wright1f2c7682015-06-03 13:46:48 +0100223 size_t size;
224 std::unique_ptr<uint8_t[]> report = getData(env, rawReport, size);
225 uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
226 if (d) {
227 d->sendReport(report.get(), size);
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700228 } else {
229 LOGE("Could not send report, Device* is null!");
Michael Wright1f2c7682015-06-03 13:46:48 +0100230 }
231}
232
Aurimas Liutikas1c8cbb52016-02-19 13:44:25 -0800233static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
Michael Wright1f2c7682015-06-03 13:46:48 +0100234 uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
235 if (d) {
236 delete d;
237 }
238}
239
240static JNINativeMethod sMethods[] = {
241 { "nativeOpenDevice",
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700242 "(Ljava/lang/String;III[B"
Michael Wright1f2c7682015-06-03 13:46:48 +0100243 "Lcom/android/commands/hid/Device$DeviceCallback;)J",
244 reinterpret_cast<void*>(openDevice) },
245 { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) },
246 { "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) },
247};
248
249int register_com_android_commands_hid_Device(JNIEnv* env) {
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700250 jclass clazz = env->FindClass("com/android/commands/hid/Device$DeviceCallback");
251 if (clazz == NULL) {
252 LOGE("Unable to find class 'DeviceCallback'");
253 return JNI_ERR;
254 }
Michael Wright1f2c7682015-06-03 13:46:48 +0100255 uhid::gDeviceCallbackClassInfo.onDeviceOpen =
Siarhei Vishniakou55656e42017-03-31 17:23:39 -0700256 env->GetMethodID(clazz, "onDeviceOpen", "()V");
257 uhid::gDeviceCallbackClassInfo.onDeviceError =
258 env->GetMethodID(clazz, "onDeviceError", "()V");
259 if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
260 uhid::gDeviceCallbackClassInfo.onDeviceError == NULL) {
261 LOGE("Unable to obtain onDeviceOpen or onDeviceError methods");
262 return JNI_ERR;
263 }
264
Michael Wright1f2c7682015-06-03 13:46:48 +0100265 return jniRegisterNativeMethods(env, "com/android/commands/hid/Device",
266 sMethods, NELEM(sMethods));
267}
268
269} // namespace android
270
271jint JNI_OnLoad(JavaVM* jvm, void*) {
272 JNIEnv *env = NULL;
273 if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
274 return JNI_ERR;
275 }
276
277 if (android::register_com_android_commands_hid_Device(env) < 0 ){
278 return JNI_ERR;
279 }
280
281 return JNI_VERSION_1_6;
282}