1/* 2 * Copyright (C) 2010 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 "UsbHostManagerJNI" 18#include "utils/Log.h" 19 20#include "jni.h" 21#include "JNIHelp.h" 22#include "android_runtime/AndroidRuntime.h" 23#include "android_runtime/Log.h" 24 25#include <usbhost/usbhost.h> 26 27#include <stdio.h> 28#include <asm/byteorder.h> 29#include <sys/types.h> 30#include <sys/stat.h> 31#include <fcntl.h> 32#include <sys/ioctl.h> 33 34namespace android 35{ 36 37static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200; 38 39static struct parcel_file_descriptor_offsets_t 40{ 41 jclass mClass; 42 jmethodID mConstructor; 43} gParcelFileDescriptorOffsets; 44 45static jmethodID method_beginUsbDeviceAdded; 46static jmethodID method_addUsbConfiguration; 47static jmethodID method_addUsbInterface; 48static jmethodID method_addUsbEndpoint; 49static jmethodID method_endUsbDeviceAdded; 50static jmethodID method_usbDeviceRemoved; 51 52static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { 53 if (env->ExceptionCheck()) { 54 ALOGE("An exception was thrown by callback '%s'.", methodName); 55 LOGE_EX(env); 56 env->ExceptionClear(); 57 } 58} 59 60static int usb_device_added(const char *devname, void* client_data) { 61 struct usb_descriptor_header* desc; 62 struct usb_descriptor_iter iter; 63 64 struct usb_device *device = usb_device_open(devname); 65 if (!device) { 66 ALOGE("usb_device_open failed\n"); 67 return 0; 68 } 69 70 JNIEnv* env = AndroidRuntime::getJNIEnv(); 71 jobject thiz = (jobject)client_data; 72 const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device); 73 74 char *manufacturer = usb_device_get_manufacturer_name(device, 75 USB_CONTROL_TRANSFER_TIMEOUT_MS); 76 char *product = usb_device_get_product_name(device, 77 USB_CONTROL_TRANSFER_TIMEOUT_MS); 78 int version = usb_device_get_version(device); 79 char *serial = usb_device_get_serial(device, 80 USB_CONTROL_TRANSFER_TIMEOUT_MS); 81 82 jstring deviceName = env->NewStringUTF(devname); 83 jstring manufacturerName = AndroidRuntime::NewStringLatin1(env, manufacturer); 84 jstring productName = AndroidRuntime::NewStringLatin1(env, product); 85 jstring serialNumber = AndroidRuntime::NewStringLatin1(env, serial); 86 87 jboolean result = env->CallBooleanMethod(thiz, method_beginUsbDeviceAdded, 88 deviceName, usb_device_get_vendor_id(device), usb_device_get_product_id(device), 89 deviceDesc->bDeviceClass, deviceDesc->bDeviceSubClass, deviceDesc->bDeviceProtocol, 90 manufacturerName, productName, version, serialNumber); 91 92 env->DeleteLocalRef(serialNumber); 93 env->DeleteLocalRef(productName); 94 env->DeleteLocalRef(manufacturerName); 95 env->DeleteLocalRef(deviceName); 96 free(manufacturer); 97 free(product); 98 free(serial); 99 100 if (!result) goto fail; 101 102 usb_descriptor_iter_init(device, &iter); 103 104 while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { 105 if (desc->bDescriptorType == USB_DT_CONFIG) { 106 struct usb_config_descriptor *config = (struct usb_config_descriptor *)desc; 107 char *name = usb_device_get_string(device, config->iConfiguration, 108 USB_CONTROL_TRANSFER_TIMEOUT_MS); 109 jstring configName = AndroidRuntime::NewStringLatin1(env, name); 110 111 env->CallVoidMethod(thiz, method_addUsbConfiguration, 112 config->bConfigurationValue, configName, config->bmAttributes, 113 config->bMaxPower); 114 115 env->DeleteLocalRef(configName); 116 free(name); 117 } else if (desc->bDescriptorType == USB_DT_INTERFACE) { 118 struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; 119 char *name = usb_device_get_string(device, interface->iInterface, 120 USB_CONTROL_TRANSFER_TIMEOUT_MS); 121 jstring interfaceName = AndroidRuntime::NewStringLatin1(env, name); 122 123 env->CallVoidMethod(thiz, method_addUsbInterface, 124 interface->bInterfaceNumber, interfaceName, interface->bAlternateSetting, 125 interface->bInterfaceClass, interface->bInterfaceSubClass, 126 interface->bInterfaceProtocol); 127 128 env->DeleteLocalRef(interfaceName); 129 free(name); 130 } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { 131 struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc; 132 133 env->CallVoidMethod(thiz, method_addUsbEndpoint, 134 endpoint->bEndpointAddress, endpoint->bmAttributes, 135 __le16_to_cpu(endpoint->wMaxPacketSize), endpoint->bInterval); 136 } 137 } 138 139 env->CallVoidMethod(thiz, method_endUsbDeviceAdded); 140 141fail: 142 usb_device_close(device); 143 checkAndClearExceptionFromCallback(env, __FUNCTION__); 144 145 return 0; 146} 147 148static int usb_device_removed(const char *devname, void* client_data) { 149 JNIEnv* env = AndroidRuntime::getJNIEnv(); 150 jobject thiz = (jobject)client_data; 151 152 jstring deviceName = env->NewStringUTF(devname); 153 env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName); 154 env->DeleteLocalRef(deviceName); 155 checkAndClearExceptionFromCallback(env, __FUNCTION__); 156 return 0; 157} 158 159static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz) 160{ 161 struct usb_host_context* context = usb_host_init(); 162 if (!context) { 163 ALOGE("usb_host_init failed"); 164 return; 165 } 166 // this will never return so it is safe to pass thiz directly 167 usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz); 168} 169 170static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */, 171 jstring deviceName) 172{ 173 const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); 174 struct usb_device* device = usb_device_open(deviceNameStr); 175 env->ReleaseStringUTFChars(deviceName, deviceNameStr); 176 177 if (!device) 178 return NULL; 179 180 int fd = usb_device_get_fd(device); 181 if (fd < 0) { 182 usb_device_close(device); 183 return NULL; 184 } 185 int newFD = dup(fd); 186 usb_device_close(device); 187 188 jobject fileDescriptor = jniCreateFileDescriptor(env, newFD); 189 if (fileDescriptor == NULL) { 190 return NULL; 191 } 192 return env->NewObject(gParcelFileDescriptorOffsets.mClass, 193 gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); 194} 195 196static const JNINativeMethod method_table[] = { 197 { "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus }, 198 { "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", 199 (void*)android_server_UsbHostManager_openDevice }, 200}; 201 202int register_android_server_UsbHostManager(JNIEnv *env) 203{ 204 jclass clazz = env->FindClass("com/android/server/usb/UsbHostManager"); 205 if (clazz == NULL) { 206 ALOGE("Can't find com/android/server/usb/UsbHostManager"); 207 return -1; 208 } 209 method_beginUsbDeviceAdded = env->GetMethodID(clazz, "beginUsbDeviceAdded", 210 "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;ILjava/lang/String;)Z"); 211 if (method_beginUsbDeviceAdded == NULL) { 212 ALOGE("Can't find beginUsbDeviceAdded"); 213 return -1; 214 } 215 method_addUsbConfiguration = env->GetMethodID(clazz, "addUsbConfiguration", 216 "(ILjava/lang/String;II)V"); 217 if (method_addUsbConfiguration == NULL) { 218 ALOGE("Can't find addUsbConfiguration"); 219 return -1; 220 } 221 method_addUsbInterface = env->GetMethodID(clazz, "addUsbInterface", 222 "(ILjava/lang/String;IIII)V"); 223 if (method_addUsbInterface == NULL) { 224 ALOGE("Can't find addUsbInterface"); 225 return -1; 226 } 227 method_addUsbEndpoint = env->GetMethodID(clazz, "addUsbEndpoint", "(IIII)V"); 228 if (method_addUsbEndpoint == NULL) { 229 ALOGE("Can't find addUsbEndpoint"); 230 return -1; 231 } 232 method_endUsbDeviceAdded = env->GetMethodID(clazz, "endUsbDeviceAdded", "()V"); 233 if (method_endUsbDeviceAdded == NULL) { 234 ALOGE("Can't find endUsbDeviceAdded"); 235 return -1; 236 } 237 method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", 238 "(Ljava/lang/String;)V"); 239 if (method_usbDeviceRemoved == NULL) { 240 ALOGE("Can't find usbDeviceRemoved"); 241 return -1; 242 } 243 244 clazz = env->FindClass("android/os/ParcelFileDescriptor"); 245 LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); 246 gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); 247 gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", 248 "(Ljava/io/FileDescriptor;)V"); 249 LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, 250 "Unable to find constructor for android.os.ParcelFileDescriptor"); 251 252 return jniRegisterNativeMethods(env, "com/android/server/usb/UsbHostManager", 253 method_table, NELEM(method_table)); 254} 255 256}; 257