• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <jni.h>
30 #include <JNIHelp.h>
31 #include <ScopedPrimitiveArray.h>
32 #include <ScopedUtfChars.h>
33 #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__)
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         LOGE("An exception was thrown by callback '%s'.", methodName);
60         env->ExceptionClear();
61     }
62 }
63 
DeviceCallback(JNIEnv * env,jobject callback)64 DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) :
65     mCallbackObject(env->NewGlobalRef(callback)) {
66     env->GetJavaVM(&mJavaVM);
67  }
68 
~DeviceCallback()69 DeviceCallback::~DeviceCallback() {
70     JNIEnv* env = getJNIEnv();
71     env->DeleteGlobalRef(mCallbackObject);
72 }
73 
onDeviceError()74 void DeviceCallback::onDeviceError() {
75     JNIEnv* env = getJNIEnv();
76     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
77     checkAndClearException(env, "onDeviceError");
78 }
79 
onDeviceOpen()80 void DeviceCallback::onDeviceOpen() {
81     JNIEnv* env = getJNIEnv();
82     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen);
83     checkAndClearException(env, "onDeviceOpen");
84 }
85 
getJNIEnv()86 JNIEnv* DeviceCallback::getJNIEnv() {
87     JNIEnv* env;
88     mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
89     return env;
90 }
91 
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)92 Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
93         std::unique_ptr<uint8_t[]> descriptor, size_t descriptorSize,
94         std::unique_ptr<DeviceCallback> callback) {
95 
96     int fd = ::open(UHID_PATH, O_RDWR | O_CLOEXEC);
97     if (fd < 0) {
98         LOGE("Failed to open uhid: %s", strerror(errno));
99         return nullptr;
100     }
101 
102     struct uhid_event ev;
103     memset(&ev, 0, sizeof(ev));
104     ev.type = UHID_CREATE;
105     strncpy((char*)ev.u.create.name, name, UHID_MAX_NAME_LENGTH);
106     ev.u.create.rd_data = descriptor.get();
107     ev.u.create.rd_size = descriptorSize;
108     ev.u.create.bus = BUS_BLUETOOTH;
109     ev.u.create.vendor = vid;
110     ev.u.create.product = pid;
111     ev.u.create.version = 0;
112     ev.u.create.country = 0;
113 
114     errno = 0;
115     ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
116     if (ret < 0 || ret != sizeof(ev)) {
117         ::close(fd);
118         LOGE("Failed to create uhid node: %s", strerror(errno));
119         return nullptr;
120     }
121 
122     // Wait for the device to actually be created.
123     ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev)));
124     if (ret < 0 || ev.type != UHID_START) {
125         ::close(fd);
126         LOGE("uhid node failed to start: %s", strerror(errno));
127         return nullptr;
128     }
129     return new Device(id, fd, std::move(callback));
130 }
131 
Device(int32_t id,int fd,std::unique_ptr<DeviceCallback> callback)132 Device::Device(int32_t id, int fd, std::unique_ptr<DeviceCallback> callback) :
133             mId(id), mFd(fd), mDeviceCallback(std::move(callback)) {
134     ALooper* aLooper = ALooper_forThread();
135     if (aLooper == NULL) {
136         LOGE("Could not get ALooper, ALooper_forThread returned NULL");
137         aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
138     }
139     ALooper_addFd(aLooper, fd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents,
140                   reinterpret_cast<void*>(this));
141 }
142 
~Device()143 Device::~Device() {
144     ALooper* looper = ALooper_forThread();
145     if (looper != NULL) {
146         ALooper_removeFd(looper, mFd);
147     } else {
148         LOGE("Could not remove fd, ALooper_forThread() returned NULL!");
149     }
150     struct uhid_event ev;
151     memset(&ev, 0, sizeof(ev));
152     ev.type = UHID_DESTROY;
153     TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
154     ::close(mFd);
155     mFd = -1;
156 }
157 
sendReport(uint8_t * report,size_t reportSize)158 void Device::sendReport(uint8_t* report, size_t reportSize) {
159     struct uhid_event ev;
160     memset(&ev, 0, sizeof(ev));
161     ev.type = UHID_INPUT;
162     ev.u.input.size = reportSize;
163     memcpy(&ev.u.input.data, report, reportSize);
164     ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
165     if (ret < 0 || ret != sizeof(ev)) {
166         LOGE("Failed to send hid event: %s", strerror(errno));
167     }
168 }
169 
handleEvents(int events)170 int Device::handleEvents(int events) {
171     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
172         LOGE("uhid node was closed or an error occurred. events=0x%x", events);
173         mDeviceCallback->onDeviceError();
174         return 0;
175     }
176     struct uhid_event ev;
177     ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev)));
178     if (ret < 0) {
179         LOGE("Failed to read from uhid node: %s", strerror(errno));
180         mDeviceCallback->onDeviceError();
181         return 0;
182     }
183 
184     if (ev.type == UHID_OPEN) {
185         mDeviceCallback->onDeviceOpen();
186     }
187 
188     return 1;
189 }
190 
191 } // namespace uhid
192 
getData(JNIEnv * env,jbyteArray javaArray,size_t & outSize)193 std::unique_ptr<uint8_t[]> getData(JNIEnv* env, jbyteArray javaArray, size_t& outSize) {
194     ScopedByteArrayRO scopedArray(env, javaArray);
195     outSize = scopedArray.size();
196     std::unique_ptr<uint8_t[]> data(new uint8_t[outSize]);
197     for (size_t i = 0; i < outSize; i++) {
198         data[i] = static_cast<uint8_t>(scopedArray[i]);
199     }
200     return data;
201 }
202 
openDevice(JNIEnv * env,jclass,jstring rawName,jint id,jint vid,jint pid,jbyteArray rawDescriptor,jobject callback)203 static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid,
204         jbyteArray rawDescriptor, jobject callback) {
205     ScopedUtfChars name(env, rawName);
206     if (name.c_str() == nullptr) {
207         return 0;
208     }
209 
210     size_t size;
211     std::unique_ptr<uint8_t[]> desc = getData(env, rawDescriptor, size);
212 
213     std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
214 
215     uhid::Device* d = uhid::Device::open(
216             id, reinterpret_cast<const char*>(name.c_str()), vid, pid,
217             std::move(desc), size, std::move(cb));
218     return reinterpret_cast<jlong>(d);
219 }
220 
sendReport(JNIEnv * env,jclass,jlong ptr,jbyteArray rawReport)221 static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) {
222     size_t size;
223     std::unique_ptr<uint8_t[]> report = getData(env, rawReport, size);
224     uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
225     if (d) {
226         d->sendReport(report.get(), size);
227     } else {
228         LOGE("Could not send report, Device* is null!");
229     }
230 }
231 
closeDevice(JNIEnv *,jclass,jlong ptr)232 static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
233     uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
234     if (d) {
235         delete d;
236     }
237 }
238 
239 static JNINativeMethod sMethods[] = {
240     { "nativeOpenDevice",
241             "(Ljava/lang/String;III[B"
242             "Lcom/android/commands/hid/Device$DeviceCallback;)J",
243             reinterpret_cast<void*>(openDevice) },
244     { "nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport) },
245     { "nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice) },
246 };
247 
register_com_android_commands_hid_Device(JNIEnv * env)248 int register_com_android_commands_hid_Device(JNIEnv* env) {
249     jclass clazz = env->FindClass("com/android/commands/hid/Device$DeviceCallback");
250     if (clazz == NULL) {
251         LOGE("Unable to find class 'DeviceCallback'");
252         return JNI_ERR;
253     }
254     uhid::gDeviceCallbackClassInfo.onDeviceOpen =
255             env->GetMethodID(clazz, "onDeviceOpen", "()V");
256     uhid::gDeviceCallbackClassInfo.onDeviceError =
257             env->GetMethodID(clazz, "onDeviceError", "()V");
258     if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
259             uhid::gDeviceCallbackClassInfo.onDeviceError == NULL) {
260         LOGE("Unable to obtain onDeviceOpen or onDeviceError methods");
261         return JNI_ERR;
262     }
263 
264     return jniRegisterNativeMethods(env, "com/android/commands/hid/Device",
265             sMethods, NELEM(sMethods));
266 }
267 
268 } // namespace android
269 
JNI_OnLoad(JavaVM * jvm,void *)270 jint JNI_OnLoad(JavaVM* jvm, void*) {
271     JNIEnv *env = NULL;
272     if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
273         return JNI_ERR;
274     }
275 
276     if (android::register_com_android_commands_hid_Device(env) < 0 ){
277         return JNI_ERR;
278     }
279 
280     return JNI_VERSION_1_6;
281 }
282