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 <nativehelper/JNIPlatformHelp.h>
22 #include "android_runtime/AndroidRuntime.h"
23 #include "android_runtime/Log.h"
24 
25 #include <stdio.h>
26 #include <asm/byteorder.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <sys/ioctl.h>
31 
32 #include <usbhost/usbhost.h>
33 
34 #define MAX_DESCRIPTORS_LENGTH 4096
35 
36 namespace android
37 {
38 
39 static struct parcel_file_descriptor_offsets_t
40 {
41     jclass mClass;
42     jmethodID mConstructor;
43 } gParcelFileDescriptorOffsets;
44 
45 static jmethodID method_usbDeviceAdded;
46 static jmethodID method_usbDeviceRemoved;
47 
checkAndClearExceptionFromCallback(JNIEnv * env,const char * methodName)48 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
49     if (env->ExceptionCheck()) {
50         ALOGE("An exception was thrown by callback '%s'.", methodName);
51         LOGE_EX(env);
52         env->ExceptionClear();
53     }
54 }
55 
usb_device_added(const char * devAddress,void * clientData)56 static int usb_device_added(const char *devAddress, void* clientData) {
57     struct usb_device *device = usb_device_open(devAddress);
58     if (!device) {
59         ALOGE("usb_device_open failed\n");
60         return 0;
61     }
62 
63     const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device);
64     int classID = deviceDesc->bDeviceClass;
65     int subClassID = deviceDesc->bDeviceSubClass;
66 
67     // get the raw descriptors
68     int numBytes = usb_device_get_descriptors_length(device);
69     if (numBytes > 0) {
70         JNIEnv* env = AndroidRuntime::getJNIEnv();
71         jobject thiz = (jobject)clientData;
72         jstring deviceAddress = env->NewStringUTF(devAddress);
73 
74         jbyteArray descriptorsArray = env->NewByteArray(numBytes);
75         const jbyte* rawDescriptors = (const jbyte*)usb_device_get_raw_descriptors(device);
76         env->SetByteArrayRegion(descriptorsArray, 0, numBytes, rawDescriptors);
77 
78         env->CallBooleanMethod(thiz, method_usbDeviceAdded,
79                 deviceAddress, classID, subClassID, descriptorsArray);
80 
81         env->DeleteLocalRef(descriptorsArray);
82         env->DeleteLocalRef(deviceAddress);
83 
84         checkAndClearExceptionFromCallback(env, __FUNCTION__);
85     } else {
86         // TODO return an error code here?
87         ALOGE("error reading descriptors\n");
88     }
89 
90     usb_device_close(device);
91 
92     return 0;
93 }
94 
usb_device_removed(const char * devAddress,void * clientData)95 static int usb_device_removed(const char *devAddress, void* clientData) {
96     JNIEnv* env = AndroidRuntime::getJNIEnv();
97     jobject thiz = (jobject)clientData;
98 
99     jstring deviceAddress = env->NewStringUTF(devAddress);
100     env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceAddress);
101     env->DeleteLocalRef(deviceAddress);
102     checkAndClearExceptionFromCallback(env, __FUNCTION__);
103     return 0;
104 }
105 
android_server_UsbHostManager_monitorUsbHostBus(JNIEnv *,jobject thiz)106 static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv* /* env */, jobject thiz)
107 {
108     struct usb_host_context* context = usb_host_init();
109     if (!context) {
110         ALOGE("usb_host_init failed");
111         return;
112     }
113     // this will never return so it is safe to pass thiz directly
114     usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz);
115 }
116 
android_server_UsbHostManager_openDevice(JNIEnv * env,jobject,jstring deviceAddress)117 static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject /* thiz */,
118                                                         jstring deviceAddress)
119 {
120     const char *deviceAddressStr = env->GetStringUTFChars(deviceAddress, NULL);
121     struct usb_device* device = usb_device_open(deviceAddressStr);
122     env->ReleaseStringUTFChars(deviceAddress, deviceAddressStr);
123 
124     if (!device)
125         return NULL;
126 
127     int fd = usb_device_get_fd(device);
128     if (fd < 0) {
129         usb_device_close(device);
130         return NULL;
131     }
132     int newFD = dup(fd);
133     usb_device_close(device);
134 
135     jobject fileDescriptor = jniCreateFileDescriptor(env, newFD);
136     if (fileDescriptor == NULL) {
137         close(newFD);
138         return NULL;
139     }
140     return env->NewObject(gParcelFileDescriptorOffsets.mClass,
141         gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
142 }
143 
144 static const JNINativeMethod method_table[] = {
145     { "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus },
146     { "nativeOpenDevice",  "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
147                                   (void*)android_server_UsbHostManager_openDevice },
148 };
149 
register_android_server_UsbHostManager(JNIEnv * env)150 int register_android_server_UsbHostManager(JNIEnv *env)
151 {
152     jclass clazz = env->FindClass("com/android/server/usb/UsbHostManager");
153     if (clazz == NULL) {
154         ALOGE("Can't find com/android/server/usb/UsbHostManager");
155         return -1;
156     }
157     method_usbDeviceAdded =
158             env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;II[B)Z");
159     if (method_usbDeviceAdded == NULL) {
160         ALOGE("Can't find beginUsbDeviceAdded");
161         return -1;
162     }
163     method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved",
164             "(Ljava/lang/String;)V");
165     if (method_usbDeviceRemoved == NULL) {
166         ALOGE("Can't find usbDeviceRemoved");
167         return -1;
168     }
169 
170     clazz = env->FindClass("android/os/ParcelFileDescriptor");
171     LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
172     gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
173     gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>",
174             "(Ljava/io/FileDescriptor;)V");
175     LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
176                  "Unable to find constructor for android.os.ParcelFileDescriptor");
177 
178     return jniRegisterNativeMethods(env, "com/android/server/usb/UsbHostManager",
179             method_table, NELEM(method_table));
180 }
181 
182 };
183