• 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 <inttypes.h>
25 #include <unistd.h>
26 #include <cstdio>
27 #include <cstring>
28 #include <memory>
29 
30 #include <android/looper.h>
31 #include <jni.h>
32 #include <log/log.h>
33 #include <nativehelper/JNIHelp.h>
34 #include <nativehelper/ScopedLocalRef.h>
35 #include <nativehelper/ScopedPrimitiveArray.h>
36 #include <nativehelper/ScopedUtfChars.h>
37 
38 #include <android-base/stringprintf.h>
39 
40 // Log debug messages about the output.
41 static constexpr bool DEBUG_OUTPUT = false;
42 
43 namespace android {
44 namespace uhid {
45 
46 static const char* UHID_PATH = "/dev/uhid";
47 
48 static struct {
49     jmethodID onDeviceOpen;
50     jmethodID onDeviceGetReport;
51     jmethodID onDeviceSetReport;
52     jmethodID onDeviceOutput;
53     jmethodID onDeviceError;
54 } gDeviceCallbackClassInfo;
55 
handleLooperEvents(int,int events,void * data)56 static int handleLooperEvents(int /* fd */, int events, void* data) {
57     Device* d = reinterpret_cast<Device*>(data);
58     return d->handleEvents(events);
59 }
60 
checkAndClearException(JNIEnv * env,const char * methodName)61 static void checkAndClearException(JNIEnv* env, const char* methodName) {
62     if (env->ExceptionCheck()) {
63         ALOGE("An exception was thrown by callback '%s'.", methodName);
64         env->ExceptionClear();
65     }
66 }
67 
toJbyteArray(JNIEnv * env,const std::vector<uint8_t> & vector)68 static ScopedLocalRef<jbyteArray> toJbyteArray(JNIEnv* env, const std::vector<uint8_t>& vector) {
69     ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(vector.size()));
70     if (array.get() == nullptr) {
71         jniThrowException(env, "java/lang/OutOfMemoryError", nullptr);
72         return array;
73     }
74     static_assert(sizeof(char) == sizeof(uint8_t));
75     env->SetByteArrayRegion(array.get(), 0, vector.size(),
76                             reinterpret_cast<const signed char*>(vector.data()));
77     return array;
78 }
79 
toString(const std::vector<uint8_t> & data)80 static std::string toString(const std::vector<uint8_t>& data) {
81     std::string s = "";
82     for (uint8_t b : data) {
83         s += android::base::StringPrintf("%x ", b);
84     }
85     return s;
86 }
87 
DeviceCallback(JNIEnv * env,jobject callback)88 DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) :
89     mCallbackObject(env->NewGlobalRef(callback)) {
90     env->GetJavaVM(&mJavaVM);
91  }
92 
~DeviceCallback()93 DeviceCallback::~DeviceCallback() {
94     JNIEnv* env = getJNIEnv();
95     env->DeleteGlobalRef(mCallbackObject);
96 }
97 
onDeviceError()98 void DeviceCallback::onDeviceError() {
99     JNIEnv* env = getJNIEnv();
100     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
101     checkAndClearException(env, "onDeviceError");
102 }
103 
onDeviceOpen()104 void DeviceCallback::onDeviceOpen() {
105     JNIEnv* env = getJNIEnv();
106     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen);
107     checkAndClearException(env, "onDeviceOpen");
108 }
109 
onDeviceGetReport(uint32_t requestId,uint8_t reportId)110 void DeviceCallback::onDeviceGetReport(uint32_t requestId, uint8_t reportId) {
111     JNIEnv* env = getJNIEnv();
112     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceGetReport,
113             requestId, reportId);
114     checkAndClearException(env, "onDeviceGetReport");
115 }
116 
onDeviceSetReport(uint8_t rType,const std::vector<uint8_t> & data)117 void DeviceCallback::onDeviceSetReport(uint8_t rType,
118                                     const std::vector<uint8_t>& data) {
119     JNIEnv* env = getJNIEnv();
120     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceSetReport, rType,
121                         toJbyteArray(env, data).get());
122     checkAndClearException(env, "onDeviceSetReport");
123 }
124 
onDeviceOutput(uint8_t rType,const std::vector<uint8_t> & data)125 void DeviceCallback::onDeviceOutput(uint8_t rType,
126                                     const std::vector<uint8_t>& data) {
127     JNIEnv* env = getJNIEnv();
128     env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOutput, rType,
129                         toJbyteArray(env, data).get());
130     checkAndClearException(env, "onDeviceOutput");
131 }
132 
getJNIEnv()133 JNIEnv* DeviceCallback::getJNIEnv() {
134     JNIEnv* env;
135     mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
136     return env;
137 }
138 
open(int32_t id,const char * name,int32_t vid,int32_t pid,uint16_t bus,const std::vector<uint8_t> & descriptor,std::unique_ptr<DeviceCallback> callback)139 std::unique_ptr<Device> Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
140                                      uint16_t bus, const std::vector<uint8_t>& descriptor,
141                                      std::unique_ptr<DeviceCallback> callback) {
142     size_t size = descriptor.size();
143     if (size > HID_MAX_DESCRIPTOR_SIZE) {
144         ALOGE("Received invalid hid report with descriptor size %zu, skipping", size);
145         return nullptr;
146     }
147 
148     android::base::unique_fd fd(::open(UHID_PATH, O_RDWR | O_CLOEXEC));
149     if (!fd.ok()) {
150         ALOGE("Failed to open uhid: %s", strerror(errno));
151         return nullptr;
152     }
153 
154     struct uhid_event ev = {};
155     ev.type = UHID_CREATE2;
156     strlcpy(reinterpret_cast<char*>(ev.u.create2.name), name, sizeof(ev.u.create2.name));
157     std::string uniq = android::base::StringPrintf("Id: %d", id);
158     strlcpy(reinterpret_cast<char*>(ev.u.create2.uniq), uniq.c_str(), sizeof(ev.u.create2.uniq));
159     memcpy(&ev.u.create2.rd_data, descriptor.data(), size * sizeof(ev.u.create2.rd_data[0]));
160     ev.u.create2.rd_size = size;
161     ev.u.create2.bus = bus;
162     ev.u.create2.vendor = vid;
163     ev.u.create2.product = pid;
164     ev.u.create2.version = 0;
165     ev.u.create2.country = 0;
166 
167     errno = 0;
168     ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
169     if (ret < 0 || ret != sizeof(ev)) {
170         ALOGE("Failed to create uhid node: %s", strerror(errno));
171         return nullptr;
172     }
173 
174     // Wait for the device to actually be created.
175     ret = TEMP_FAILURE_RETRY(::read(fd, &ev, sizeof(ev)));
176     if (ret < 0 || ev.type != UHID_START) {
177         ALOGE("uhid node failed to start: %s", strerror(errno));
178         return nullptr;
179     }
180     // using 'new' to access non-public constructor
181     return std::unique_ptr<Device>(new Device(id, std::move(fd), std::move(callback)));
182 }
183 
Device(int32_t id,android::base::unique_fd fd,std::unique_ptr<DeviceCallback> callback)184 Device::Device(int32_t id, android::base::unique_fd fd, std::unique_ptr<DeviceCallback> callback)
185       : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
186     ALooper* aLooper = ALooper_forThread();
187     if (aLooper == NULL) {
188         ALOGE("Could not get ALooper, ALooper_forThread returned NULL");
189         aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
190     }
191     ALooper_addFd(aLooper, mFd, 0, ALOOPER_EVENT_INPUT, handleLooperEvents,
192                   reinterpret_cast<void*>(this));
193 }
194 
~Device()195 Device::~Device() {
196     ALooper* looper = ALooper_forThread();
197     if (looper != NULL) {
198         ALooper_removeFd(looper, mFd);
199     } else {
200         ALOGE("Could not remove fd, ALooper_forThread() returned NULL!");
201     }
202     struct uhid_event ev = {};
203     ev.type = UHID_DESTROY;
204     TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
205 }
206 
207 // Send event over the fd.
writeEvent(int fd,struct uhid_event & ev,const char * messageType)208 static void writeEvent(int fd, struct uhid_event& ev, const char* messageType) {
209     ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
210     if (ret < 0 || ret != sizeof(ev)) {
211         ALOGE("Failed to send uhid_event %s: %s", messageType, strerror(errno));
212     }
213 }
214 
sendReport(const std::vector<uint8_t> & report) const215 void Device::sendReport(const std::vector<uint8_t>& report) const {
216     if (report.size() > UHID_DATA_MAX) {
217         ALOGE("Received invalid report of size %zu, skipping", report.size());
218         return;
219     }
220 
221     struct uhid_event ev = {};
222     ev.type = UHID_INPUT2;
223     ev.u.input2.size = report.size();
224     memcpy(&ev.u.input2.data, report.data(), report.size() * sizeof(ev.u.input2.data[0]));
225     writeEvent(mFd, ev, "UHID_INPUT2");
226 }
227 
sendGetFeatureReportReply(uint32_t id,const std::vector<uint8_t> & report) const228 void Device::sendGetFeatureReportReply(uint32_t id, const std::vector<uint8_t>& report) const {
229     struct uhid_event ev = {};
230     ev.type = UHID_GET_REPORT_REPLY;
231     ev.u.get_report_reply.id = id;
232     ev.u.get_report_reply.err = report.size() == 0 ? EIO : 0;
233     ev.u.get_report_reply.size = report.size();
234     memcpy(&ev.u.get_report_reply.data, report.data(),
235             report.size() * sizeof(ev.u.get_report_reply.data[0]));
236     writeEvent(mFd, ev, "UHID_GET_REPORT_REPLY");
237 }
238 
handleEvents(int events)239 int Device::handleEvents(int events) {
240     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
241         ALOGE("uhid node was closed or an error occurred. events=0x%x", events);
242         mDeviceCallback->onDeviceError();
243         return 0;
244     }
245     struct uhid_event ev;
246     ssize_t ret = TEMP_FAILURE_RETRY(::read(mFd, &ev, sizeof(ev)));
247     if (ret < 0) {
248         ALOGE("Failed to read from uhid node: %s", strerror(errno));
249         mDeviceCallback->onDeviceError();
250         return 0;
251     }
252 
253     switch (ev.type) {
254         case UHID_OPEN: {
255             mDeviceCallback->onDeviceOpen();
256             break;
257         }
258         case UHID_GET_REPORT: {
259             mDeviceCallback->onDeviceGetReport(ev.u.get_report.id, ev.u.get_report.rnum);
260             break;
261         }
262         case UHID_SET_REPORT: {
263             const struct uhid_set_report_req& set_report = ev.u.set_report;
264             if (set_report.size > UHID_DATA_MAX) {
265                 ALOGE("SET_REPORT contains too much data: size = %" PRIu16, set_report.size);
266                 return 0;
267             }
268 
269             std::vector<uint8_t> data(set_report.data, set_report.data + set_report.size);
270             if (DEBUG_OUTPUT) {
271                 ALOGD("Received SET_REPORT: id=%" PRIu32 " rnum=%" PRIu8 " data=%s", set_report.id,
272                       set_report.rnum, toString(data).c_str());
273             }
274             mDeviceCallback->onDeviceSetReport(set_report.rtype, data);
275             break;
276         }
277         case UHID_OUTPUT: {
278             struct uhid_output_req& output = ev.u.output;
279             std::vector<uint8_t> data(output.data, output.data + output.size);
280             if (DEBUG_OUTPUT) {
281                 ALOGD("UHID_OUTPUT rtype=%" PRIu8 " data=%s", output.rtype, toString(data).c_str());
282             }
283             mDeviceCallback->onDeviceOutput(output.rtype, data);
284             break;
285         }
286         default: {
287             ALOGI("Unhandled event type: %" PRIu32, ev.type);
288             break;
289         }
290     }
291 
292     return 1;
293 }
294 
295 } // namespace uhid
296 
getData(JNIEnv * env,jbyteArray javaArray)297 std::vector<uint8_t> getData(JNIEnv* env, jbyteArray javaArray) {
298     std::vector<uint8_t> data;
299     if (javaArray == nullptr) {
300         return data;
301     }
302 
303     ScopedByteArrayRO scopedArray(env, javaArray);
304     size_t size = scopedArray.size();
305     data.reserve(size);
306     for (size_t i = 0; i < size; i++) {
307         data.push_back(static_cast<uint8_t>(scopedArray[i]));
308     }
309     return data;
310 }
311 
openDevice(JNIEnv * env,jclass,jstring rawName,jint id,jint vid,jint pid,jint bus,jbyteArray rawDescriptor,jobject callback)312 static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
313                         jint pid, jint bus, jbyteArray rawDescriptor, jobject callback) {
314     ScopedUtfChars name(env, rawName);
315     if (name.c_str() == nullptr) {
316         return 0;
317     }
318 
319     std::vector<uint8_t> desc = getData(env, rawDescriptor);
320 
321     std::unique_ptr<uhid::DeviceCallback> cb(new uhid::DeviceCallback(env, callback));
322 
323     std::unique_ptr<uhid::Device> d =
324             uhid::Device::open(id, reinterpret_cast<const char*>(name.c_str()), vid, pid, bus, desc,
325                                std::move(cb));
326     return reinterpret_cast<jlong>(d.release());
327 }
328 
sendReport(JNIEnv * env,jclass,jlong ptr,jbyteArray rawReport)329 static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) {
330     std::vector<uint8_t> report = getData(env, rawReport);
331     uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
332     if (d) {
333         d->sendReport(report);
334     } else {
335         ALOGE("Could not send report, Device* is null!");
336     }
337 }
338 
sendGetFeatureReportReply(JNIEnv * env,jclass,jlong ptr,jint id,jbyteArray rawReport)339 static void sendGetFeatureReportReply(JNIEnv* env, jclass /* clazz */, jlong ptr, jint id,
340         jbyteArray rawReport) {
341     uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
342     if (d) {
343         std::vector<uint8_t> report = getData(env, rawReport);
344         d->sendGetFeatureReportReply(id, report);
345     } else {
346         ALOGE("Could not send get feature report reply, Device* is null!");
347     }
348 }
349 
closeDevice(JNIEnv *,jclass,jlong ptr)350 static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
351     uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr);
352     if (d) {
353         delete d;
354     }
355 }
356 
357 static JNINativeMethod sMethods[] = {
358         {"nativeOpenDevice",
359          "(Ljava/lang/String;IIII[B"
360          "Lcom/android/commands/hid/Device$DeviceCallback;)J",
361          reinterpret_cast<void*>(openDevice)},
362         {"nativeSendReport", "(J[B)V", reinterpret_cast<void*>(sendReport)},
363         {"nativeSendGetFeatureReportReply", "(JI[B)V",
364          reinterpret_cast<void*>(sendGetFeatureReportReply)},
365         {"nativeCloseDevice", "(J)V", reinterpret_cast<void*>(closeDevice)},
366 };
367 
register_com_android_commands_hid_Device(JNIEnv * env)368 int register_com_android_commands_hid_Device(JNIEnv* env) {
369     jclass clazz = env->FindClass("com/android/commands/hid/Device$DeviceCallback");
370     if (clazz == NULL) {
371         ALOGE("Unable to find class 'DeviceCallback'");
372         return JNI_ERR;
373     }
374     uhid::gDeviceCallbackClassInfo.onDeviceOpen =
375             env->GetMethodID(clazz, "onDeviceOpen", "()V");
376     uhid::gDeviceCallbackClassInfo.onDeviceGetReport =
377             env->GetMethodID(clazz, "onDeviceGetReport", "(II)V");
378     uhid::gDeviceCallbackClassInfo.onDeviceSetReport =
379             env->GetMethodID(clazz, "onDeviceSetReport", "(B[B)V");
380     uhid::gDeviceCallbackClassInfo.onDeviceOutput =
381             env->GetMethodID(clazz, "onDeviceOutput", "(B[B)V");
382     uhid::gDeviceCallbackClassInfo.onDeviceError =
383             env->GetMethodID(clazz, "onDeviceError", "()V");
384     if (uhid::gDeviceCallbackClassInfo.onDeviceOpen == NULL ||
385             uhid::gDeviceCallbackClassInfo.onDeviceError == NULL) {
386         ALOGE("Unable to obtain onDeviceOpen or onDeviceError methods");
387         return JNI_ERR;
388     }
389 
390     return jniRegisterNativeMethods(env, "com/android/commands/hid/Device",
391             sMethods, NELEM(sMethods));
392 }
393 
394 } // namespace android
395 
JNI_OnLoad(JavaVM * jvm,void *)396 jint JNI_OnLoad(JavaVM* jvm, void*) {
397     JNIEnv *env = NULL;
398     if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
399         return JNI_ERR;
400     }
401 
402     if (android::register_com_android_commands_hid_Device(env) < 0 ){
403         return JNI_ERR;
404     }
405 
406     return JNI_VERSION_1_6;
407 }
408