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 "core_jni_helpers.h"
     24 
     25 #include <usbhost/usbhost.h>
     26 
     27 #include <chrono>
     28 
     29 #include <stdio.h>
     30 #include <sys/types.h>
     31 #include <sys/stat.h>
     32 #include <fcntl.h>
     33 
     34 using namespace android;
     35 using namespace std::chrono;
     36 
     37 static const int USB_CONTROL_READ_TIMEOUT_MS = 200;
     38 
     39 static jfieldID field_context;
     40 
     41 struct usb_device* get_device_from_object(JNIEnv* env, jobject connection)
     42 {
     43     return (struct usb_device*)env->GetLongField(connection, field_context);
     44 }
     45 
     46 static jboolean
     47 android_hardware_UsbDeviceConnection_open(JNIEnv *env, jobject thiz, jstring deviceName,
     48         jobject fileDescriptor)
     49 {
     50     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
     51     // duplicate the file descriptor, since ParcelFileDescriptor will eventually close its copy
     52     fd = dup(fd);
     53     if (fd < 0)
     54         return JNI_FALSE;
     55 
     56     const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
     57     struct usb_device* device = usb_device_new(deviceNameStr, fd);
     58     if (device) {
     59         env->SetLongField(thiz, field_context, (jlong)device);
     60     } else {
     61         ALOGE("usb_device_open failed for %s", deviceNameStr);
     62         close(fd);
     63     }
     64 
     65     env->ReleaseStringUTFChars(deviceName, deviceNameStr);
     66     return (device != NULL) ? JNI_TRUE : JNI_FALSE;
     67 }
     68 
     69 static void
     70 android_hardware_UsbDeviceConnection_close(JNIEnv *env, jobject thiz)
     71 {
     72     ALOGD("close\n");
     73     struct usb_device* device = get_device_from_object(env, thiz);
     74     if (device) {
     75         usb_device_close(device);
     76         env->SetLongField(thiz, field_context, 0);
     77     }
     78 }
     79 
     80 static jint
     81 android_hardware_UsbDeviceConnection_get_fd(JNIEnv *env, jobject thiz)
     82 {
     83     struct usb_device* device = get_device_from_object(env, thiz);
     84     if (!device) {
     85         ALOGE("device is closed in native_get_fd");
     86         return -1;
     87     }
     88     return usb_device_get_fd(device);
     89 }
     90 
     91 static jbyteArray
     92 android_hardware_UsbDeviceConnection_get_desc(JNIEnv *env, jobject thiz)
     93 {
     94     char buffer[16384];
     95     int fd = android_hardware_UsbDeviceConnection_get_fd(env, thiz);
     96     if (fd < 0) return NULL;
     97     lseek(fd, 0, SEEK_SET);
     98     int length = read(fd, buffer, sizeof(buffer));
     99     if (length < 0) return NULL;
    100 
    101     jbyteArray ret = env->NewByteArray(length);
    102     if (ret) {
    103         jbyte* bytes = (jbyte*)env->GetPrimitiveArrayCritical(ret, 0);
    104         if (bytes) {
    105             memcpy(bytes, buffer, length);
    106             env->ReleasePrimitiveArrayCritical(ret, bytes, 0);
    107         }
    108     }
    109     return ret;
    110 }
    111 
    112 static jboolean
    113 android_hardware_UsbDeviceConnection_claim_interface(JNIEnv *env, jobject thiz,
    114         jint interfaceID, jboolean force)
    115 {
    116     struct usb_device* device = get_device_from_object(env, thiz);
    117     if (!device) {
    118         ALOGE("device is closed in native_claim_interface");
    119         return JNI_FALSE;
    120     }
    121 
    122     int ret = usb_device_claim_interface(device, interfaceID);
    123     if (ret && force && errno == EBUSY) {
    124         // disconnect kernel driver and try again
    125         usb_device_connect_kernel_driver(device, interfaceID, false);
    126         ret = usb_device_claim_interface(device, interfaceID);
    127     }
    128     return (ret == 0) ? JNI_TRUE : JNI_FALSE;
    129 }
    130 
    131 static jboolean
    132 android_hardware_UsbDeviceConnection_release_interface(JNIEnv *env, jobject thiz, jint interfaceID)
    133 {
    134     struct usb_device* device = get_device_from_object(env, thiz);
    135     if (!device) {
    136         ALOGE("device is closed in native_release_interface");
    137         return JNI_FALSE;
    138     }
    139     int ret = usb_device_release_interface(device, interfaceID);
    140     if (ret == 0) {
    141         // allow kernel to reconnect its driver
    142         usb_device_connect_kernel_driver(device, interfaceID, true);
    143     }
    144     return (ret == 0) ? JNI_TRUE : JNI_FALSE;
    145 }
    146 
    147 static jboolean
    148 android_hardware_UsbDeviceConnection_set_interface(JNIEnv *env, jobject thiz, jint interfaceID,
    149         jint alternateSetting)
    150 {
    151     struct usb_device* device = get_device_from_object(env, thiz);
    152     if (!device) {
    153         ALOGE("device is closed in native_set_interface");
    154         return JNI_FALSE;
    155     }
    156     int ret = usb_device_set_interface(device, interfaceID, alternateSetting);
    157     return (ret == 0) ? JNI_TRUE : JNI_FALSE;
    158 }
    159 
    160 static jboolean
    161 android_hardware_UsbDeviceConnection_set_configuration(JNIEnv *env, jobject thiz, jint configurationID)
    162 {
    163     struct usb_device* device = get_device_from_object(env, thiz);
    164     if (!device) {
    165         ALOGE("device is closed in native_set_configuration");
    166         return JNI_FALSE;
    167     }
    168     int ret = usb_device_set_configuration(device, configurationID);
    169     return (ret == 0) ? JNI_TRUE : JNI_FALSE;
    170 }
    171 
    172 static jint
    173 android_hardware_UsbDeviceConnection_control_request(JNIEnv *env, jobject thiz,
    174         jint requestType, jint request, jint value, jint index,
    175         jbyteArray buffer, jint start, jint length, jint timeout)
    176 {
    177     struct usb_device* device = get_device_from_object(env, thiz);
    178     if (!device) {
    179         ALOGE("device is closed in native_control_request");
    180         return -1;
    181     }
    182 
    183     jbyte* bufferBytes = NULL;
    184     if (buffer) {
    185         bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
    186     }
    187 
    188     jint result = usb_device_control_transfer(device, requestType, request,
    189             value, index, bufferBytes + start, length, timeout);
    190 
    191     if (bufferBytes) {
    192         env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
    193     }
    194 
    195     return result;
    196 }
    197 
    198 static jint
    199 android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz,
    200         jint endpoint, jbyteArray buffer, jint start, jint length, jint timeout)
    201 {
    202     struct usb_device* device = get_device_from_object(env, thiz);
    203     if (!device) {
    204         ALOGE("device is closed in native_control_request");
    205         return -1;
    206     }
    207 
    208     jbyte* bufferBytes = NULL;
    209     if (buffer) {
    210         bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
    211     }
    212 
    213     jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes + start, length, timeout);
    214 
    215     if (bufferBytes) {
    216         env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
    217     }
    218 
    219     return result;
    220 }
    221 
    222 static jobject
    223 android_hardware_UsbDeviceConnection_request_wait(JNIEnv *env, jobject thiz, jlong timeoutMillis)
    224 {
    225     struct usb_device* device = get_device_from_object(env, thiz);
    226     if (!device) {
    227         ALOGE("device is closed in native_request_wait");
    228         return NULL;
    229     }
    230 
    231     struct usb_request* request;
    232     if (timeoutMillis == -1) {
    233         request = usb_request_wait(device, -1);
    234     } else {
    235         steady_clock::time_point currentTime = steady_clock::now();
    236         steady_clock::time_point endTime = currentTime + std::chrono::milliseconds(timeoutMillis);
    237 
    238         // Poll the existence of a request via usb_request_wait until we get a result, an unexpected
    239         // error or time out. As several threads can listen on the same fd, we might get wakeups
    240         // without data.
    241         while (1) {
    242             request = usb_request_wait(device, duration_cast<std::chrono::milliseconds>(endTime
    243                                - currentTime).count());
    244 
    245             int error = errno;
    246             if (request != NULL) {
    247                 break;
    248             }
    249 
    250             currentTime = steady_clock::now();
    251             if (currentTime >= endTime) {
    252                 jniThrowException(env, "java/util/concurrent/TimeoutException", "");
    253                 break;
    254             }
    255 
    256             if (error != EAGAIN) {
    257                 break;
    258             }
    259         };
    260     }
    261 
    262     if (request) {
    263         return (jobject)request->client_data;
    264     } else {
    265         return NULL;
    266     }
    267 }
    268 
    269 static jstring
    270 android_hardware_UsbDeviceConnection_get_serial(JNIEnv *env, jobject thiz)
    271 {
    272     struct usb_device* device = get_device_from_object(env, thiz);
    273     if (!device) {
    274         ALOGE("device is closed in native_get_serial");
    275         return NULL;
    276     }
    277     char* serial = usb_device_get_serial(device,
    278             USB_CONTROL_READ_TIMEOUT_MS);
    279     if (!serial)
    280         return NULL;
    281     jstring result = env->NewStringUTF(serial);
    282     free(serial);
    283     return result;
    284 }
    285 
    286 static jboolean
    287 android_hardware_UsbDeviceConnection_reset_device(JNIEnv *env, jobject thiz)
    288 {
    289     struct usb_device* device = get_device_from_object(env, thiz);
    290     if (!device) {
    291         ALOGE("device is closed in native_reset_device");
    292         return JNI_FALSE;
    293     }
    294     int ret = usb_device_reset(device);
    295     return (ret == 0) ? JNI_TRUE : JNI_FALSE;
    296 }
    297 
    298 static const JNINativeMethod method_table[] = {
    299     {"native_open",             "(Ljava/lang/String;Ljava/io/FileDescriptor;)Z",
    300                                         (void *)android_hardware_UsbDeviceConnection_open},
    301     {"native_close",            "()V",  (void *)android_hardware_UsbDeviceConnection_close},
    302     {"native_get_fd",           "()I",  (void *)android_hardware_UsbDeviceConnection_get_fd},
    303     {"native_get_desc",         "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc},
    304     {"native_claim_interface",  "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface},
    305     {"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface},
    306     {"native_set_interface","(II)Z",    (void *)android_hardware_UsbDeviceConnection_set_interface},
    307     {"native_set_configuration","(I)Z", (void *)android_hardware_UsbDeviceConnection_set_configuration},
    308     {"native_control_request",  "(IIII[BIII)I",
    309                                         (void *)android_hardware_UsbDeviceConnection_control_request},
    310     {"native_bulk_request",     "(I[BIII)I",
    311                                         (void *)android_hardware_UsbDeviceConnection_bulk_request},
    312     {"native_request_wait",             "(J)Landroid/hardware/usb/UsbRequest;",
    313                                         (void *)android_hardware_UsbDeviceConnection_request_wait},
    314     { "native_get_serial",      "()Ljava/lang/String;",
    315                                         (void*)android_hardware_UsbDeviceConnection_get_serial },
    316     {"native_reset_device","()Z", (void *)android_hardware_UsbDeviceConnection_reset_device},
    317 };
    318 
    319 int register_android_hardware_UsbDeviceConnection(JNIEnv *env)
    320 {
    321     jclass clazz = FindClassOrDie(env, "android/hardware/usb/UsbDeviceConnection");
    322     field_context = GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
    323 
    324     return RegisterMethodsOrDie(env, "android/hardware/usb/UsbDeviceConnection",
    325             method_table, NELEM(method_table));
    326 }
    327