Home | History | Annotate | Download | only in jni
      1 /*
      2  * Copyright (C) 2012 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 "BluetoothAvrcpServiceJni"
     18 
     19 #define LOG_NDEBUG 0
     20 
     21 #include "com_android_bluetooth.h"
     22 #include "hardware/bt_rc.h"
     23 #include "utils/Log.h"
     24 #include "android_runtime/AndroidRuntime.h"
     25 
     26 #include <string.h>
     27 
     28 namespace android {
     29 static jmethodID method_getRcFeatures;
     30 static jmethodID method_getPlayStatus;
     31 static jmethodID method_getElementAttr;
     32 static jmethodID method_registerNotification;
     33 static jmethodID method_volumeChangeCallback;
     34 static jmethodID method_handlePassthroughCmd;
     35 
     36 static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
     37 static jobject mCallbacksObj = NULL;
     38 static JNIEnv *sCallbackEnv = NULL;
     39 
     40 static bool checkCallbackThread() {
     41     // Always fetch the latest callbackEnv from AdapterService.
     42     // Caching this could cause this sCallbackEnv to go out-of-sync
     43     // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
     44     // is received
     45     sCallbackEnv = getCallbackEnv();
     46 
     47     JNIEnv* env = AndroidRuntime::getJNIEnv();
     48     if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
     49     return true;
     50 }
     51 
     52 static void btavrcp_remote_features_callback(bt_bdaddr_t* bd_addr, btrc_remote_features_t features) {
     53     ALOGI("%s", __FUNCTION__);
     54     jbyteArray addr;
     55 
     56     if (!checkCallbackThread()) {
     57         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
     58         return;
     59     }
     60     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
     61     if (!addr) {
     62         ALOGE("Fail to new jbyteArray bd addr for connection state");
     63         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     64         return;
     65     }
     66 
     67     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
     68     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features);
     69     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     70     sCallbackEnv->DeleteLocalRef(addr);
     71 }
     72 
     73 static void btavrcp_get_play_status_callback() {
     74     ALOGI("%s", __FUNCTION__);
     75 
     76     if (!checkCallbackThread()) {
     77         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
     78         return;
     79     }
     80 
     81     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus);
     82     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     83 }
     84 
     85 static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) {
     86     jintArray attrs;
     87 
     88     ALOGI("%s", __FUNCTION__);
     89 
     90     if (!checkCallbackThread()) {
     91         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
     92         return;
     93     }
     94     attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
     95     if (!attrs) {
     96         ALOGE("Fail to new jintArray for attrs");
     97         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     98         return;
     99     }
    100     sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
    101     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs);
    102     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    103     sCallbackEnv->DeleteLocalRef(attrs);
    104 }
    105 
    106 static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) {
    107     ALOGI("%s", __FUNCTION__);
    108 
    109     if (!checkCallbackThread()) {
    110         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
    111         return;
    112     }
    113 
    114     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
    115                                  (jint)event_id, (jint)param);
    116     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    117 }
    118 
    119 static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype) {
    120     ALOGI("%s", __FUNCTION__);
    121 
    122     if (!checkCallbackThread()) {
    123         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
    124         return;
    125     }
    126 
    127     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, (jint)volume,
    128                                                                              (jint)ctype);
    129     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    130 }
    131 
    132 static void btavrcp_passthrough_command_callback(int id, int pressed) {
    133     ALOGI("%s", __FUNCTION__);
    134 
    135     if (!checkCallbackThread()) {
    136         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
    137         return;
    138     }
    139 
    140     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, (jint)id,
    141                                                                              (jint)pressed);
    142     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    143 }
    144 
    145 static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
    146     sizeof(sBluetoothAvrcpCallbacks),
    147     btavrcp_remote_features_callback,
    148     btavrcp_get_play_status_callback,
    149     NULL,
    150     NULL,
    151     NULL,
    152     NULL,
    153     NULL,
    154     NULL,
    155     btavrcp_get_element_attr_callback,
    156     btavrcp_register_notification_callback,
    157     btavrcp_volume_change_callback,
    158     btavrcp_passthrough_command_callback,
    159 };
    160 
    161 static void classInitNative(JNIEnv* env, jclass clazz) {
    162     method_getRcFeatures =
    163         env->GetMethodID(clazz, "getRcFeatures", "([BI)V");
    164     method_getPlayStatus =
    165         env->GetMethodID(clazz, "getPlayStatus", "()V");
    166 
    167     method_getElementAttr =
    168         env->GetMethodID(clazz, "getElementAttr", "(B[I)V");
    169 
    170     method_registerNotification =
    171         env->GetMethodID(clazz, "registerNotification", "(II)V");
    172 
    173     method_volumeChangeCallback =
    174         env->GetMethodID(clazz, "volumeChangeCallback", "(II)V");
    175 
    176     method_handlePassthroughCmd =
    177         env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V");
    178 
    179     ALOGI("%s: succeeds", __FUNCTION__);
    180 }
    181 
    182 static void initNative(JNIEnv *env, jobject object) {
    183     const bt_interface_t* btInf;
    184     bt_status_t status;
    185 
    186     if ( (btInf = getBluetoothInterface()) == NULL) {
    187         ALOGE("Bluetooth module is not loaded");
    188         return;
    189     }
    190 
    191     if (sBluetoothAvrcpInterface !=NULL) {
    192          ALOGW("Cleaning up Avrcp Interface before initializing...");
    193          sBluetoothAvrcpInterface->cleanup();
    194          sBluetoothAvrcpInterface = NULL;
    195     }
    196 
    197     if (mCallbacksObj != NULL) {
    198          ALOGW("Cleaning up Avrcp callback object");
    199          env->DeleteGlobalRef(mCallbacksObj);
    200          mCallbacksObj = NULL;
    201     }
    202 
    203     if ( (sBluetoothAvrcpInterface = (btrc_interface_t *)
    204           btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) {
    205         ALOGE("Failed to get Bluetooth Avrcp Interface");
    206         return;
    207     }
    208 
    209     if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) !=
    210          BT_STATUS_SUCCESS) {
    211         ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
    212         sBluetoothAvrcpInterface = NULL;
    213         return;
    214     }
    215 
    216     mCallbacksObj = env->NewGlobalRef(object);
    217 }
    218 
    219 static void cleanupNative(JNIEnv *env, jobject object) {
    220     const bt_interface_t* btInf;
    221 
    222     if ( (btInf = getBluetoothInterface()) == NULL) {
    223         ALOGE("Bluetooth module is not loaded");
    224         return;
    225     }
    226 
    227     if (sBluetoothAvrcpInterface !=NULL) {
    228         sBluetoothAvrcpInterface->cleanup();
    229         sBluetoothAvrcpInterface = NULL;
    230     }
    231 
    232     if (mCallbacksObj != NULL) {
    233         env->DeleteGlobalRef(mCallbacksObj);
    234         mCallbacksObj = NULL;
    235     }
    236 }
    237 
    238 static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus,
    239                                        jint songLen, jint songPos) {
    240     bt_status_t status;
    241 
    242     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    243     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    244 
    245     if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus,
    246                                             songLen, songPos)) != BT_STATUS_SUCCESS) {
    247         ALOGE("Failed get_play_status_rsp, status: %d", status);
    248     }
    249 
    250     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    251 }
    252 
    253   static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr,
    254                                           jintArray attrIds, jobjectArray textArray) {
    255     jint *attr;
    256     bt_status_t status;
    257     jstring text;
    258     int i;
    259     btrc_element_attr_val_t *pAttrs = NULL;
    260     const char* textStr;
    261 
    262     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    263 
    264     if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
    265         ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
    266         return JNI_FALSE;
    267     }
    268 
    269     pAttrs = new btrc_element_attr_val_t[numAttr];
    270     if (!pAttrs) {
    271         ALOGE("get_element_attr_rsp: not have enough memeory");
    272         return JNI_FALSE;
    273     }
    274 
    275     attr = env->GetIntArrayElements(attrIds, NULL);
    276     if (!attr) {
    277         delete[] pAttrs;
    278         jniThrowIOException(env, EINVAL);
    279         return JNI_FALSE;
    280     }
    281 
    282     for (i = 0; i < numAttr; ++i) {
    283         text = (jstring) env->GetObjectArrayElement(textArray, i);
    284         textStr = env->GetStringUTFChars(text, NULL);
    285         if (!textStr) {
    286             ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL");
    287             env->DeleteLocalRef(text);
    288             break;
    289         }
    290 
    291         pAttrs[i].attr_id = attr[i];
    292         if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
    293             ALOGE("get_element_attr_rsp: string length exceed maximum");
    294             strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1);
    295             pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0;
    296         } else {
    297             strcpy((char *)pAttrs[i].text, textStr);
    298         }
    299         env->ReleaseStringUTFChars(text, textStr);
    300         env->DeleteLocalRef(text);
    301     }
    302 
    303     if (i < numAttr) {
    304         delete[] pAttrs;
    305         env->ReleaseIntArrayElements(attrIds, attr, 0);
    306         return JNI_FALSE;
    307     }
    308 
    309     if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) !=
    310         BT_STATUS_SUCCESS) {
    311         ALOGE("Failed get_element_attr_rsp, status: %d", status);
    312     }
    313 
    314     delete[] pAttrs;
    315     env->ReleaseIntArrayElements(attrIds, attr, 0);
    316     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    317 }
    318 
    319 static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object,
    320                                                         jint type, jint playStatus) {
    321     bt_status_t status;
    322     btrc_register_notification_t param;
    323 
    324     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    325     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    326 
    327     param.play_status = (btrc_play_status_t)playStatus;
    328     if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
    329                   (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
    330         ALOGE("Failed register_notification_rsp play status, status: %d", status);
    331     }
    332 
    333     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    334 }
    335 
    336 static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object,
    337                                                          jint type, jbyteArray track) {
    338     bt_status_t status;
    339     btrc_register_notification_t param;
    340     jbyte *trk;
    341     int i;
    342 
    343     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    344     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    345 
    346     trk = env->GetByteArrayElements(track, NULL);
    347     if (!trk) {
    348         jniThrowIOException(env, EINVAL);
    349         return JNI_FALSE;
    350     }
    351 
    352     for (i = 0; i < BTRC_UID_SIZE; ++i) {
    353       param.track[i] = trk[i];
    354     }
    355 
    356     if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
    357                   (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
    358         ALOGE("Failed register_notification_rsp track change, status: %d", status);
    359     }
    360 
    361     env->ReleaseByteArrayElements(track, trk, 0);
    362     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    363 }
    364 
    365 static jboolean registerNotificationRspPlayPosNative(JNIEnv *env, jobject object,
    366                                                         jint type, jint playPos) {
    367     bt_status_t status;
    368     btrc_register_notification_t param;
    369 
    370     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    371     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    372 
    373     param.song_pos = (uint32_t)playPos;
    374     if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
    375                   (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
    376         ALOGE("Failed register_notification_rsp play position, status: %d", status);
    377     }
    378 
    379     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    380 }
    381 
    382 static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume) {
    383     bt_status_t status;
    384 
    385     //TODO: delete test code
    386     ALOGI("%s: jint: %d, uint8_t: %u", __FUNCTION__, volume, (uint8_t) volume);
    387 
    388     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    389     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    390 
    391     if ((status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume)) != BT_STATUS_SUCCESS) {
    392         ALOGE("Failed set_volume, status: %d", status);
    393     }
    394 
    395     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    396 }
    397 
    398 static JNINativeMethod sMethods[] = {
    399     {"classInitNative", "()V", (void *) classInitNative},
    400     {"initNative", "()V", (void *) initNative},
    401     {"cleanupNative", "()V", (void *) cleanupNative},
    402     {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative},
    403     {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative},
    404     {"registerNotificationRspPlayStatusNative", "(II)Z",
    405      (void *) registerNotificationRspPlayStatusNative},
    406     {"registerNotificationRspTrackChangeNative", "(I[B)Z",
    407      (void *) registerNotificationRspTrackChangeNative},
    408     {"registerNotificationRspPlayPosNative", "(II)Z",
    409      (void *) registerNotificationRspPlayPosNative},
    410     {"setVolumeNative", "(I)Z",
    411      (void *) setVolumeNative},
    412 };
    413 
    414 int register_com_android_bluetooth_avrcp(JNIEnv* env)
    415 {
    416     return jniRegisterNativeMethods(env, "com/android/bluetooth/avrcp/Avrcp",
    417                                     sMethods, NELEM(sMethods));
    418 }
    419 
    420 }
    421