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_getPlayStatus;
     30 static jmethodID method_getElementAttr;
     31 static jmethodID method_registerNotification;
     32 
     33 static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
     34 static jobject mCallbacksObj = NULL;
     35 static JNIEnv *sCallbackEnv = NULL;
     36 
     37 static bool checkCallbackThread() {
     38     // Always fetch the latest callbackEnv from AdapterService.
     39     // Caching this could cause this sCallbackEnv to go out-of-sync
     40     // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
     41     // is received
     42     sCallbackEnv = getCallbackEnv();
     43 
     44     JNIEnv* env = AndroidRuntime::getJNIEnv();
     45     if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
     46     return true;
     47 }
     48 
     49 static void btavrcp_get_play_status_callback() {
     50     ALOGI("%s", __FUNCTION__);
     51 
     52     if (!checkCallbackThread()) {
     53         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
     54         return;
     55     }
     56 
     57     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus);
     58     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     59 }
     60 
     61 static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) {
     62     jintArray attrs;
     63 
     64     ALOGI("%s", __FUNCTION__);
     65 
     66     if (!checkCallbackThread()) {
     67         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
     68         return;
     69     }
     70     attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
     71     if (!attrs) {
     72         ALOGE("Fail to new jintArray for attrs");
     73         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     74         return;
     75     }
     76     sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
     77     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs);
     78     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     79     sCallbackEnv->DeleteLocalRef(attrs);
     80 }
     81 
     82 static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) {
     83     ALOGI("%s", __FUNCTION__);
     84 
     85     if (!checkCallbackThread()) {
     86         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
     87         return;
     88     }
     89 
     90     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
     91                                  (jint)event_id, (jint)param);
     92     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     93 }
     94 
     95 static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
     96     sizeof(sBluetoothAvrcpCallbacks),
     97     btavrcp_get_play_status_callback,
     98     NULL,
     99     NULL,
    100     NULL,
    101     NULL,
    102     NULL,
    103     NULL,
    104     btavrcp_get_element_attr_callback,
    105     btavrcp_register_notification_callback
    106 };
    107 
    108 static void classInitNative(JNIEnv* env, jclass clazz) {
    109     method_getPlayStatus =
    110         env->GetMethodID(clazz, "getPlayStatus", "()V");
    111 
    112     method_getElementAttr =
    113         env->GetMethodID(clazz, "getElementAttr", "(B[I)V");
    114 
    115     method_registerNotification =
    116         env->GetMethodID(clazz, "registerNotification", "(II)V");
    117 
    118     ALOGI("%s: succeeds", __FUNCTION__);
    119 }
    120 
    121 static void initNative(JNIEnv *env, jobject object) {
    122     const bt_interface_t* btInf;
    123     bt_status_t status;
    124 
    125     if ( (btInf = getBluetoothInterface()) == NULL) {
    126         ALOGE("Bluetooth module is not loaded");
    127         return;
    128     }
    129 
    130     if (sBluetoothAvrcpInterface !=NULL) {
    131          ALOGW("Cleaning up Avrcp Interface before initializing...");
    132          sBluetoothAvrcpInterface->cleanup();
    133          sBluetoothAvrcpInterface = NULL;
    134     }
    135 
    136     if (mCallbacksObj != NULL) {
    137          ALOGW("Cleaning up Avrcp callback object");
    138          env->DeleteGlobalRef(mCallbacksObj);
    139          mCallbacksObj = NULL;
    140     }
    141 
    142     if ( (sBluetoothAvrcpInterface = (btrc_interface_t *)
    143           btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) {
    144         ALOGE("Failed to get Bluetooth Avrcp Interface");
    145         return;
    146     }
    147 
    148     if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) !=
    149          BT_STATUS_SUCCESS) {
    150         ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
    151         sBluetoothAvrcpInterface = NULL;
    152         return;
    153     }
    154 
    155     mCallbacksObj = env->NewGlobalRef(object);
    156 }
    157 
    158 static void cleanupNative(JNIEnv *env, jobject object) {
    159     const bt_interface_t* btInf;
    160 
    161     if ( (btInf = getBluetoothInterface()) == NULL) {
    162         ALOGE("Bluetooth module is not loaded");
    163         return;
    164     }
    165 
    166     if (sBluetoothAvrcpInterface !=NULL) {
    167         sBluetoothAvrcpInterface->cleanup();
    168         sBluetoothAvrcpInterface = NULL;
    169     }
    170 
    171     if (mCallbacksObj != NULL) {
    172         env->DeleteGlobalRef(mCallbacksObj);
    173         mCallbacksObj = NULL;
    174     }
    175 }
    176 
    177 static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus,
    178                                        jint songLen, jint songPos) {
    179     bt_status_t status;
    180 
    181     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    182     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    183 
    184     if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus,
    185                                             songLen, songPos)) != BT_STATUS_SUCCESS) {
    186         ALOGE("Failed get_play_status_rsp, status: %d", status);
    187     }
    188 
    189     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    190 }
    191 
    192   static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr,
    193                                           jintArray attrIds, jobjectArray textArray) {
    194     jint *attr;
    195     bt_status_t status;
    196     jstring text;
    197     int i;
    198     btrc_element_attr_val_t *pAttrs = NULL;
    199     const char* textStr;
    200 
    201     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    202 
    203     if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
    204         ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
    205         return JNI_FALSE;
    206     }
    207 
    208     pAttrs = new btrc_element_attr_val_t[numAttr];
    209     if (!pAttrs) {
    210         ALOGE("get_element_attr_rsp: not have enough memeory");
    211         return JNI_FALSE;
    212     }
    213 
    214     attr = env->GetIntArrayElements(attrIds, NULL);
    215     if (!attr) {
    216         delete[] pAttrs;
    217         jniThrowIOException(env, EINVAL);
    218         return JNI_FALSE;
    219     }
    220 
    221     for (i = 0; i < numAttr; ++i) {
    222         text = (jstring) env->GetObjectArrayElement(textArray, i);
    223         textStr = env->GetStringUTFChars(text, NULL);
    224         if (!textStr) {
    225             ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL");
    226             env->DeleteLocalRef(text);
    227             break;
    228         }
    229 
    230         pAttrs[i].attr_id = attr[i];
    231         if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
    232             ALOGE("get_element_attr_rsp: string length exceed maximum");
    233             strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1);
    234             pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0;
    235         } else {
    236             strcpy((char *)pAttrs[i].text, textStr);
    237         }
    238         env->ReleaseStringUTFChars(text, textStr);
    239         env->DeleteLocalRef(text);
    240     }
    241 
    242     if (i < numAttr) {
    243         delete[] pAttrs;
    244         env->ReleaseIntArrayElements(attrIds, attr, 0);
    245         return JNI_FALSE;
    246     }
    247 
    248     if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) !=
    249         BT_STATUS_SUCCESS) {
    250         ALOGE("Failed get_element_attr_rsp, status: %d", status);
    251     }
    252 
    253     delete[] pAttrs;
    254     env->ReleaseIntArrayElements(attrIds, attr, 0);
    255     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    256 }
    257 
    258 static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object,
    259                                                         jint type, jint playStatus) {
    260     bt_status_t status;
    261     btrc_register_notification_t param;
    262 
    263     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    264     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    265 
    266     param.play_status = (btrc_play_status_t)playStatus;
    267     if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
    268                   (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
    269         ALOGE("Failed register_notification_rsp play status, status: %d", status);
    270     }
    271 
    272     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    273 }
    274 
    275 static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object,
    276                                                          jint type, jbyteArray track) {
    277     bt_status_t status;
    278     btrc_register_notification_t param;
    279     jbyte *trk;
    280     int i;
    281 
    282     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    283     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    284 
    285     trk = env->GetByteArrayElements(track, NULL);
    286     if (!trk) {
    287         jniThrowIOException(env, EINVAL);
    288         return JNI_FALSE;
    289     }
    290 
    291     for (i = 0; i < BTRC_UID_SIZE; ++i) {
    292       param.track[i] = trk[i];
    293     }
    294 
    295     if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
    296                   (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
    297         ALOGE("Failed register_notification_rsp track change, status: %d", status);
    298     }
    299 
    300     env->ReleaseByteArrayElements(track, trk, 0);
    301     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    302 }
    303 
    304 static jboolean registerNotificationRspPlayPosNative(JNIEnv *env, jobject object,
    305                                                         jint type, jint playPos) {
    306     bt_status_t status;
    307     btrc_register_notification_t param;
    308 
    309     ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    310     if (!sBluetoothAvrcpInterface) return JNI_FALSE;
    311 
    312     param.song_pos = (uint32_t)playPos;
    313     if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
    314                   (btrc_notification_type_t)type, &param)) != BT_STATUS_SUCCESS) {
    315         ALOGE("Failed register_notification_rsp play position, status: %d", status);
    316     }
    317 
    318     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    319 }
    320 
    321 static JNINativeMethod sMethods[] = {
    322     {"classInitNative", "()V", (void *) classInitNative},
    323     {"initNative", "()V", (void *) initNative},
    324     {"cleanupNative", "()V", (void *) cleanupNative},
    325     {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative},
    326     {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative},
    327     {"registerNotificationRspPlayStatusNative", "(II)Z",
    328      (void *) registerNotificationRspPlayStatusNative},
    329     {"registerNotificationRspTrackChangeNative", "(I[B)Z",
    330      (void *) registerNotificationRspTrackChangeNative},
    331     {"registerNotificationRspPlayPosNative", "(II)Z",
    332      (void *) registerNotificationRspPlayPosNative},
    333 };
    334 
    335 int register_com_android_bluetooth_avrcp(JNIEnv* env)
    336 {
    337     return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/Avrcp",
    338                                     sMethods, NELEM(sMethods));
    339 }
    340 
    341 }
    342