Home | History | Annotate | Download | only in jni
      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 
     32 using namespace android;
     33 
     34 static jfieldID field_context;
     35 
     36 struct usb_device* get_device_from_object(JNIEnv* env, jobject connection)
     37 {
     38     return (struct usb_device*)env->GetIntField(connection, field_context);
     39 }
     40 
     41 static jboolean
     42 android_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         LOGE("usb_device_open failed for %s", deviceNameStr);
     57         close(fd);
     58     }
     59 
     60     env->ReleaseStringUTFChars(deviceName, deviceNameStr);
     61     return (device != NULL);
     62 }
     63 
     64 static void
     65 android_hardware_UsbDeviceConnection_close(JNIEnv *env, jobject thiz)
     66 {
     67     LOGD("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 
     75 static jint
     76 android_hardware_UsbDeviceConnection_get_fd(JNIEnv *env, jobject thiz)
     77 {
     78     struct usb_device* device = get_device_from_object(env, thiz);
     79     if (!device) {
     80         LOGE("device is closed in native_get_fd");
     81         return -1;
     82     }
     83     return usb_device_get_fd(device);
     84 }
     85 
     86 static jbyteArray
     87 android_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 
    107 static jboolean
    108 android_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         LOGE("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 
    126 static jint
    127 android_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         LOGE("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 
    142 static jint
    143 android_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         LOGE("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 
    171 static jint
    172 android_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         LOGE("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 
    198 static jobject
    199 android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz)
    200 {
    201     struct usb_device* device = get_device_from_object(env, thiz);
    202     if (!device) {
    203         LOGE("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 
    214 static jstring
    215 android_hardware_UsbDeviceConnection_get_serial(JNIEnv *env, jobject thiz)
    216 {
    217     struct usb_device* device = get_device_from_object(env, thiz);
    218     if (!device) {
    219         LOGE("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 
    230 static 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 
    248 int register_android_hardware_UsbDeviceConnection(JNIEnv *env)
    249 {
    250     jclass clazz = env->FindClass("android/hardware/usb/UsbDeviceConnection");
    251     if (clazz == NULL) {
    252         LOGE("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         LOGE("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