1/* 2 * Copyright (C) 2011 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 "UsbDeviceConnectionJNI" 18 19#include "utils/Log.h" 20 21#include "jni.h" 22#include "JNIHelp.h" 23#include "android_runtime/AndroidRuntime.h" 24 25#include <usbhost/usbhost.h> 26 27#include <stdio.h> 28#include <sys/types.h> 29#include <sys/stat.h> 30#include <fcntl.h> 31 32using namespace android; 33 34static jfieldID field_context; 35 36struct usb_device* get_device_from_object(JNIEnv* env, jobject connection) 37{ 38 return (struct usb_device*)env->GetIntField(connection, field_context); 39} 40 41static jboolean 42android_hardware_UsbDeviceConnection_open(JNIEnv *env, jobject thiz, jstring deviceName, 43 jobject fileDescriptor) 44{ 45 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 46 // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy 47 fd = dup(fd); 48 if (fd < 0) 49 return false; 50 51 const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); 52 struct usb_device* device = usb_device_new(deviceNameStr, fd); 53 if (device) { 54 env->SetIntField(thiz, field_context, (int)device); 55 } else { 56 ALOGE("usb_device_open failed for %s", deviceNameStr); 57 close(fd); 58 } 59 60 env->ReleaseStringUTFChars(deviceName, deviceNameStr); 61 return (device != NULL); 62} 63 64static void 65android_hardware_UsbDeviceConnection_close(JNIEnv *env, jobject thiz) 66{ 67 ALOGD("close\n"); 68 struct usb_device* device = get_device_from_object(env, thiz); 69 if (device) { 70 usb_device_close(device); 71 env->SetIntField(thiz, field_context, 0); 72 } 73} 74 75static jint 76android_hardware_UsbDeviceConnection_get_fd(JNIEnv *env, jobject thiz) 77{ 78 struct usb_device* device = get_device_from_object(env, thiz); 79 if (!device) { 80 ALOGE("device is closed in native_get_fd"); 81 return -1; 82 } 83 return usb_device_get_fd(device); 84} 85 86static jbyteArray 87android_hardware_UsbDeviceConnection_get_desc(JNIEnv *env, jobject thiz) 88{ 89 char buffer[16384]; 90 int fd = android_hardware_UsbDeviceConnection_get_fd(env, thiz); 91 if (fd < 0) return NULL; 92 lseek(fd, 0, SEEK_SET); 93 int length = read(fd, buffer, sizeof(buffer)); 94 if (length < 0) return NULL; 95 96 jbyteArray ret = env->NewByteArray(length); 97 if (ret) { 98 jbyte* bytes = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0); 99 if (bytes) { 100 memcpy(bytes, buffer, length); 101 env->ReleasePrimitiveArrayCritical(ret, bytes, 0); 102 } 103 } 104 return ret; 105} 106 107static jboolean 108android_hardware_UsbDeviceConnection_claim_interface(JNIEnv *env, jobject thiz, 109 int interfaceID, jboolean force) 110{ 111 struct usb_device* device = get_device_from_object(env, thiz); 112 if (!device) { 113 ALOGE("device is closed in native_claim_interface"); 114 return -1; 115 } 116 117 int ret = usb_device_claim_interface(device, interfaceID); 118 if (ret && force && errno == EBUSY) { 119 // disconnect kernel driver and try again 120 usb_device_connect_kernel_driver(device, interfaceID, false); 121 ret = usb_device_claim_interface(device, interfaceID); 122 } 123 return ret == 0; 124} 125 126static jint 127android_hardware_UsbDeviceConnection_release_interface(JNIEnv *env, jobject thiz, int interfaceID) 128{ 129 struct usb_device* device = get_device_from_object(env, thiz); 130 if (!device) { 131 ALOGE("device is closed in native_release_interface"); 132 return -1; 133 } 134 int ret = usb_device_release_interface(device, interfaceID); 135 if (ret == 0) { 136 // allow kernel to reconnect its driver 137 usb_device_connect_kernel_driver(device, interfaceID, true); 138 } 139 return ret; 140} 141 142static jint 143android_hardware_UsbDeviceConnection_control_request(JNIEnv *env, jobject thiz, 144 jint requestType, jint request, jint value, jint index, 145 jbyteArray buffer, jint length, jint timeout) 146{ 147 struct usb_device* device = get_device_from_object(env, thiz); 148 if (!device) { 149 ALOGE("device is closed in native_control_request"); 150 return -1; 151 } 152 153 jbyte* bufferBytes = NULL; 154 if (buffer) { 155 if (env->GetArrayLength(buffer) < length) { 156 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 157 return -1; 158 } 159 bufferBytes = env->GetByteArrayElements(buffer, 0); 160 } 161 162 jint result = usb_device_control_transfer(device, requestType, request, 163 value, index, bufferBytes, length, timeout); 164 165 if (bufferBytes) 166 env->ReleaseByteArrayElements(buffer, bufferBytes, 0); 167 168 return result; 169} 170 171static jint 172android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz, 173 jint endpoint, jbyteArray buffer, jint length, jint timeout) 174{ 175 struct usb_device* device = get_device_from_object(env, thiz); 176 if (!device) { 177 ALOGE("device is closed in native_control_request"); 178 return -1; 179 } 180 181 jbyte* bufferBytes = NULL; 182 if (buffer) { 183 if (env->GetArrayLength(buffer) < length) { 184 jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 185 return -1; 186 } 187 bufferBytes = env->GetByteArrayElements(buffer, 0); 188 } 189 190 jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes, length, timeout); 191 192 if (bufferBytes) 193 env->ReleaseByteArrayElements(buffer, bufferBytes, 0); 194 195 return result; 196} 197 198static jobject 199android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz) 200{ 201 struct usb_device* device = get_device_from_object(env, thiz); 202 if (!device) { 203 ALOGE("device is closed in native_request_wait"); 204 return NULL; 205 } 206 207 struct usb_request* request = usb_request_wait(device); 208 if (request) 209 return (jobject)request->client_data; 210 else 211 return NULL; 212} 213 214static jstring 215android_hardware_UsbDeviceConnection_get_serial(JNIEnv *env, jobject thiz) 216{ 217 struct usb_device* device = get_device_from_object(env, thiz); 218 if (!device) { 219 ALOGE("device is closed in native_request_wait"); 220 return NULL; 221 } 222 char* serial = usb_device_get_serial(device); 223 if (!serial) 224 return NULL; 225 jstring result = env->NewStringUTF(serial); 226 free(serial); 227 return result; 228} 229 230static JNINativeMethod method_table[] = { 231 {"native_open", "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z", 232 (void *)android_hardware_UsbDeviceConnection_open}, 233 {"native_close", "()V", (void *)android_hardware_UsbDeviceConnection_close}, 234 {"native_get_fd", "()I", (void *)android_hardware_UsbDeviceConnection_get_fd}, 235 {"native_get_desc", "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc}, 236 {"native_claim_interface", "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface}, 237 {"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface}, 238 {"native_control_request", "(IIII[BII)I", 239 (void *)android_hardware_UsbDeviceConnection_control_request}, 240 {"native_bulk_request", "(I[BII)I", 241 (void *)android_hardware_UsbDeviceConnection_bulk_request}, 242 {"native_request_wait", "()Landroid/hardware/usb/UsbRequest;", 243 (void *)android_hardware_UsbDeviceConnection_request_wait}, 244 { "native_get_serial", "()Ljava/lang/String;", 245 (void*)android_hardware_UsbDeviceConnection_get_serial }, 246}; 247 248int register_android_hardware_UsbDeviceConnection(JNIEnv *env) 249{ 250 jclass clazz = env->FindClass("android/hardware/usb/UsbDeviceConnection"); 251 if (clazz == NULL) { 252 ALOGE("Can't find android/hardware/usb/UsbDeviceConnection"); 253 return -1; 254 } 255 field_context = env->GetFieldID(clazz, "mNativeContext", "I"); 256 if (field_context == NULL) { 257 ALOGE("Can't find UsbDeviceConnection.mNativeContext"); 258 return -1; 259 } 260 261 return AndroidRuntime::registerNativeMethods(env, "android/hardware/usb/UsbDeviceConnection", 262 method_table, NELEM(method_table)); 263} 264