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 <nativehelper/JNIHelp.h> 31#include <nativehelper/ScopedPrimitiveArray.h> 32#include <nativehelper/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 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 LOGE("An exception was thrown by callback '%s'.", methodName); 60 env->ExceptionClear(); 61 } 62} 63 64DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback) : 65 mCallbackObject(env->NewGlobalRef(callback)) { 66 env->GetJavaVM(&mJavaVM); 67 } 68 69DeviceCallback::~DeviceCallback() { 70 JNIEnv* env = getJNIEnv(); 71 env->DeleteGlobalRef(mCallbackObject); 72} 73 74void DeviceCallback::onDeviceError() { 75 JNIEnv* env = getJNIEnv(); 76 env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError); 77 checkAndClearException(env, "onDeviceError"); 78} 79 80void DeviceCallback::onDeviceOpen() { 81 JNIEnv* env = getJNIEnv(); 82 env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceOpen); 83 checkAndClearException(env, "onDeviceOpen"); 84} 85 86JNIEnv* DeviceCallback::getJNIEnv() { 87 JNIEnv* env; 88 mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); 89 return env; 90} 91 92Device* 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_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; 114 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); 119 LOGE("Failed to create uhid node: %s", strerror(errno)); 120 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); 127 LOGE("uhid node failed to start: %s", strerror(errno)); 128 return nullptr; 129 } 130 return new Device(id, fd, std::move(callback)); 131} 132 133Device::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)); 142} 143 144Device::~Device() { 145 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 } 151 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)); 162 ev.type = UHID_INPUT2; 163 ev.u.input2.size = reportSize; 164 memcpy(&ev.u.input2.data, report, reportSize); 165 ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev))); 166 if (ret < 0 || ret != sizeof(ev)) { 167 LOGE("Failed to send hid event: %s", strerror(errno)); 168 } 169} 170 171int Device::handleEvents(int events) { 172 if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { 173 LOGE("uhid node was closed or an error occurred. events=0x%x", events); 174 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) { 180 LOGE("Failed to read from uhid node: %s", strerror(errno)); 181 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 204static jlong openDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid, jint pid, 205 jbyteArray rawDescriptor, jobject callback) { 206 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)); 215 216 uhid::Device* d = uhid::Device::open( 217 id, reinterpret_cast<const char*>(name.c_str()), vid, pid, 218 std::move(desc), size, std::move(cb)); 219 return reinterpret_cast<jlong>(d); 220} 221 222static void sendReport(JNIEnv* env, jclass /* clazz */, jlong ptr, jbyteArray rawReport) { 223 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); 228 } else { 229 LOGE("Could not send report, Device* is null!"); 230 } 231} 232 233static void closeDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) { 234 uhid::Device* d = reinterpret_cast<uhid::Device*>(ptr); 235 if (d) { 236 delete d; 237 } 238} 239 240static JNINativeMethod sMethods[] = { 241 { "nativeOpenDevice", 242 "(Ljava/lang/String;III[B" 243 "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) { 250 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 } 255 uhid::gDeviceCallbackClassInfo.onDeviceOpen = 256 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 265 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} 283