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