Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f1f83d67 authored by Sanket Agarwal's avatar Sanket Agarwal
Browse files

A2DP Sink:Audio Rendering patch and AutoConnect Functionality

 -  Add autoconnect functionality for A2DP Sink
 -  Use AudioPatch mechanism for sink playback
 -  AudioFocus approach to manage concurrencies

A2DP Sink: Support for AudioTrack

- add support for audiotrack to render audio data
- add support for AVRCP State to trigger audio
  rendering

AVRCP_CTRL: add support for AVRCP 1.3 Controller

- fill placeholder function for AVRCP 1.3
- add supporting classes to handle AVRCP Commands

AVRCP Controller: Support for retaining volume level and blocking streaming from remote.

- retain volume level on new connection request
- block streaming from remote.

Change-Id: I8c31fd1779b196ced0fb0870855b93263ea331ec
parent 576a121c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.DEVICE_POWER" />
    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />

    <!-- For PBAP Owner Vcard Info -->
    <uses-permission android:name="android.permission.READ_PROFILE"/>
+35 −1
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ namespace android {
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onAudioStateChanged;
static jmethodID method_onAudioConfigChanged;
static jmethodID method_onAudioFocusRequested;

static const btav_interface_t *sBluetoothA2dpInterface = NULL;
static jobject mCallbacksObj = NULL;
@@ -116,11 +117,34 @@ static void bta2dp_audio_config_callback(bt_bdaddr_t *bd_addr, uint32_t sample_r
    sCallbackEnv->DeleteLocalRef(addr);
}

static void bta2dp_audio_focus_request_callback(bt_bdaddr_t *bd_addr) {
    jbyteArray addr;

    ALOGI("%s", __FUNCTION__);

    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }
    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("Fail to new jbyteArray bd addr for connection state");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }

    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioFocusRequested, addr);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
}

static btav_callbacks_t sBluetoothA2dpCallbacks = {
    sizeof(sBluetoothA2dpCallbacks),
    bta2dp_connection_state_callback,
    bta2dp_audio_state_callback,
    bta2dp_audio_config_callback
    bta2dp_audio_config_callback,
    bta2dp_audio_focus_request_callback
};

static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -137,6 +161,9 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
    method_onAudioConfigChanged =
        env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");

    method_onAudioFocusRequested =
        env->GetMethodID(clazz, "onAudioFocusRequested", "([B)V");

    ALOGI("%s: succeeds", __FUNCTION__);
}

@@ -237,12 +264,19 @@ static jboolean disconnectA2dpNative(JNIEnv *env, jobject object, jbyteArray add
    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

static void informAudioFocusStateNative(JNIEnv *env, jobject object, jint focus_state) {
    if (!sBluetoothA2dpInterface) return;

    sBluetoothA2dpInterface->audio_focus_state((uint8_t)focus_state);

}
static JNINativeMethod sMethods[] = {
    {"classInitNative", "()V", (void *) classInitNative},
    {"initNative", "()V", (void *) initNative},
    {"cleanupNative", "()V", (void *) cleanupNative},
    {"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
    {"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
    {"informAudioFocusStateNative", "(I)V", (void *) informAudioFocusStateNative},
};

int register_com_android_bluetooth_a2dp_sink(JNIEnv* env)
+496 −3
Original line number Diff line number Diff line
@@ -28,6 +28,17 @@
namespace android {
static jmethodID method_handlePassthroughRsp;
static jmethodID method_onConnectionStateChanged;
static jmethodID method_getRcFeatures;
static jmethodID method_setplayerappsettingrsp;
static jmethodID method_handleplayerappsetting;
static jmethodID method_handleplayerappsettingchanged;
static jmethodID method_handleSetAbsVolume;
static jmethodID method_handleRegisterNotificationAbsVol;
static jmethodID method_handletrackchanged;
static jmethodID method_handleplaypositionchanged;
static jmethodID method_handleplaystatuschanged;
static jmethodID method_handleGroupNavigationRsp;


static const btrc_ctrl_interface_t *sBluetoothAvrcpInterface = NULL;
static jobject mCallbacksObj = NULL;
@@ -59,6 +70,19 @@ static void btavrcp_passthrough_response_callback(int id, int pressed) {
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

static void btavrcp_groupnavigation_response_callback(int id, int pressed) {
    ALOGI("%s", __FUNCTION__);

    if (!checkCallbackThread()) {
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
        return;
    }

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleGroupNavigationRsp, (jint)id,
                                                                             (jint)pressed);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}

static void btavrcp_connection_state_callback(bool state, bt_bdaddr_t* bd_addr) {
    jbyteArray addr;

@@ -84,20 +108,361 @@ static void btavrcp_connection_state_callback(bool state, bt_bdaddr_t* bd_addr)
    sCallbackEnv->DeleteLocalRef(addr);
}

static void btavrcp_get_rcfeatures_callback(bt_bdaddr_t *bd_addr, int features) {
    jbyteArray addr;

    ALOGI("%s", __FUNCTION__);

    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("Fail to new jbyteArray bd addr ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }

    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
}

static void btavrcp_setplayerapplicationsetting_rsp_callback(bt_bdaddr_t *bd_addr,
                                                                    uint8_t accepted) {
    jbyteArray addr;

    ALOGI("%s", __FUNCTION__);

    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("Fail to new jbyteArray bd addr ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }

    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setplayerappsettingrsp, addr, (jint)accepted);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
}

static void btavrcp_playerapplicationsetting_callback(bt_bdaddr_t *bd_addr, uint8_t num_attr,
        btrc_player_app_attr_t *app_attrs, uint8_t num_ext_attr,
        btrc_player_app_ext_attr_t *ext_attrs) {
    ALOGI("%s", __FUNCTION__);
    jbyteArray addr;
    jbyteArray playerattribs;
    jint arraylen;
    int i,k;

    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if (!addr) {
        ALOGE("Fail to new jbyteArray bd addr ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr);
    /* TODO ext attrs
     * Flattening defined attributes: <id,num_values,values[]>
     */
    arraylen = 0;
    for (i = 0; i < num_attr; i++)
    {
        /*2 bytes for id and num */
        arraylen += 2 + app_attrs[i].num_val;
    }
    ALOGI(" arraylen %d", arraylen);
    playerattribs = sCallbackEnv->NewByteArray(arraylen);
    if(!playerattribs)
    {
        ALOGE("Fail to new jbyteArray playerattribs ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        sCallbackEnv->DeleteLocalRef(addr);
        return;
    }
    k= 0;
    for (i = 0; (i < num_attr)&&(k < arraylen); i++)
    {
        sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(app_attrs[i].attr_id));
        k++;
        sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(app_attrs[i].num_val));
        k++;
        sCallbackEnv->SetByteArrayRegion(playerattribs, k, app_attrs[i].num_val,
                (jbyte*)(app_attrs[i].attr_val));
        k = k + app_attrs[i].num_val;
    }
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplayerappsetting, addr,
            playerattribs, (jint)arraylen);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
    sCallbackEnv->DeleteLocalRef(playerattribs);
}

static void btavrcp_playerapplicationsetting_changed_callback(bt_bdaddr_t *bd_addr,
                         btrc_player_settings_t *p_vals) {

    jbyteArray addr;
    jbyteArray playerattribs;
    int i, k, arraylen;
    ALOGI("%s", __FUNCTION__);

    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if ((!addr)) {
        ALOGE("Fail to get new array ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr);
    arraylen = p_vals->num_attr*2;
    playerattribs = sCallbackEnv->NewByteArray(arraylen);
    if(!playerattribs)
    {
        ALOGE("Fail to new jbyteArray playerattribs ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        sCallbackEnv->DeleteLocalRef(addr);
        return;
    }
    /*
     * Flatening format: <id,val>
     */
    k = 0;
    for (i = 0; (i < p_vals->num_attr)&&( k < arraylen);i++)
    {
        sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(p_vals->attr_ids[i]));
        k++;
        sCallbackEnv->SetByteArrayRegion(playerattribs, k, 1, (jbyte*)&(p_vals->attr_values[i]));
        k++;
    }
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplayerappsettingchanged, addr,
            playerattribs, (jint)arraylen);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
    sCallbackEnv->DeleteLocalRef(playerattribs);
}

static void btavrcp_set_abs_vol_cmd_callback(bt_bdaddr_t *bd_addr, uint8_t abs_vol,
        uint8_t label) {

    jbyteArray addr;
    ALOGI("%s", __FUNCTION__);

    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if ((!addr)) {
        ALOGE("Fail to get new array ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }

    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleSetAbsVolume, addr, (jbyte)abs_vol,
                                 (jbyte)label);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
}

static void btavrcp_register_notification_absvol_callback(bt_bdaddr_t *bd_addr, uint8_t label) {
    jbyteArray addr;

    ALOGI("%s", __FUNCTION__);

    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if ((!addr)) {
        ALOGE("Fail to get new array ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }

    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleRegisterNotificationAbsVol, addr,
                                 (jbyte)label);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
}

static void btavrcp_track_changed_callback(bt_bdaddr_t *bd_addr, uint8_t num_attr,
                           btrc_element_attr_val_t *p_attrs) {
    /*
     * byteArray will be formatted like this: id,len,string
     * Assuming text feild to be null terminated.
     */
    jbyteArray addr;
    jintArray attribIds;
    jobjectArray stringArray;
    jstring str;
    jclass strclazz;
    jint i;
    ALOGI("%s", __FUNCTION__);
    if (!checkCallbackThread()) {                                       \
        ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
        return;                                                         \
    }

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if ((!addr)) {
        ALOGE("Fail to get new array ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    attribIds = sCallbackEnv->NewIntArray(num_attr);
    if(!attribIds) {
        ALOGE(" failed to set new array for attribIds");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        sCallbackEnv->DeleteLocalRef(addr);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*)bd_addr);

    strclazz = sCallbackEnv->FindClass("java/lang/String");
    stringArray = sCallbackEnv->NewObjectArray((jint)num_attr, strclazz, 0);
    if(!stringArray) {
        ALOGE(" failed to get String array");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        sCallbackEnv->DeleteLocalRef(addr);
        sCallbackEnv->DeleteLocalRef(attribIds);
        return;
    }
    for(i = 0; i < num_attr; i++)
    {
        str = sCallbackEnv->NewStringUTF((char*)(p_attrs[i].text));
        if(!str) {
            ALOGE(" Unable to get str ");
            checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
            sCallbackEnv->DeleteLocalRef(addr);
            sCallbackEnv->DeleteLocalRef(attribIds);
            sCallbackEnv->DeleteLocalRef(stringArray);
            return;
        }
        sCallbackEnv->SetIntArrayRegion(attribIds, i, 1, (jint*)&(p_attrs[i].attr_id));
        sCallbackEnv->SetObjectArrayElement(stringArray, i,str);
        sCallbackEnv->DeleteLocalRef(str);
    }

    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handletrackchanged, addr,
         (jbyte)(num_attr), attribIds, stringArray);
    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    sCallbackEnv->DeleteLocalRef(addr);
    sCallbackEnv->DeleteLocalRef(attribIds);
    /* TODO check do we need to delete str seperately or not */
    sCallbackEnv->DeleteLocalRef(stringArray);
    sCallbackEnv->DeleteLocalRef(strclazz);
}

static void btavrcp_play_position_changed_callback(bt_bdaddr_t *bd_addr, uint32_t song_len,
        uint32_t song_pos) {

    jbyteArray addr;
    ALOGI("%s", __FUNCTION__);

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if ((!addr)) {
        ALOGE("Fail to get new array ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplaypositionchanged, addr,
         (jint)(song_len), (jint)song_pos);
    sCallbackEnv->DeleteLocalRef(addr);
}

static void btavrcp_play_status_changed_callback(bt_bdaddr_t *bd_addr,
        btrc_play_status_t play_status) {
    jbyteArray addr;
    ALOGI("%s", __FUNCTION__);

    addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    if ((!addr)) {
        ALOGE("Fail to get new array ");
        checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
        return;
    }
    sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplaystatuschanged, addr,
             (jbyte)play_status);
    sCallbackEnv->DeleteLocalRef(addr);
}

static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = {
    sizeof(sBluetoothAvrcpCallbacks),
    btavrcp_passthrough_response_callback,
    btavrcp_connection_state_callback
    btavrcp_groupnavigation_response_callback,
    btavrcp_connection_state_callback,
    btavrcp_get_rcfeatures_callback,
    btavrcp_setplayerapplicationsetting_rsp_callback,
    btavrcp_playerapplicationsetting_callback,
    btavrcp_playerapplicationsetting_changed_callback,
    btavrcp_set_abs_vol_cmd_callback,
    btavrcp_register_notification_absvol_callback,
    btavrcp_track_changed_callback,
    btavrcp_play_position_changed_callback,
    btavrcp_play_status_changed_callback
};

static void classInitNative(JNIEnv* env, jclass clazz) {
    method_handlePassthroughRsp =
        env->GetMethodID(clazz, "handlePassthroughRsp", "(II)V");

    method_handleGroupNavigationRsp =
        env->GetMethodID(clazz, "handleGroupNavigationRsp", "(II)V");

    method_onConnectionStateChanged =
        env->GetMethodID(clazz, "onConnectionStateChanged", "(Z[B)V");

    method_getRcFeatures =
        env->GetMethodID(clazz, "getRcFeatures", "([BI)V");

    method_setplayerappsettingrsp =
        env->GetMethodID(clazz, "setPlayerAppSettingRsp", "([BB)V");

    method_handleplayerappsetting =
        env->GetMethodID(clazz, "handlePlayerAppSetting", "([B[BI)V");

    method_handleplayerappsettingchanged =
        env->GetMethodID(clazz, "onPlayerAppSettingChanged", "([B[BI)V");

    method_handleSetAbsVolume =
        env->GetMethodID(clazz, "handleSetAbsVolume", "([BBB)V");

    method_handleRegisterNotificationAbsVol =
        env->GetMethodID(clazz, "handleRegisterNotificationAbsVol", "([BB)V");

    method_handletrackchanged =
        env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V");

    method_handleplaypositionchanged =
        env->GetMethodID(clazz, "onPlayPositionChanged", "([BII)V");

    method_handleplaystatuschanged =
            env->GetMethodID(clazz, "onPlayStatusChanged", "([BB)V");
    ALOGI("%s: succeeds", __FUNCTION__);
}

@@ -183,12 +548,140 @@ static jboolean sendPassThroughCommandNative(JNIEnv *env, jobject object, jbyteA
    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

static jboolean sendGroupNavigationCommandNative(JNIEnv *env, jobject object, jbyteArray address,
                                                    jint key_code, jint key_state) {
    jbyte *addr;
    bt_status_t status;

    if (!sBluetoothAvrcpInterface) return JNI_FALSE;

    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);

    ALOGI("key_code: %d, key_state: %d", key_code, key_state);

    addr = env->GetByteArrayElements(address, NULL);
    if (!addr) {
        jniThrowIOException(env, EINVAL);
        return JNI_FALSE;
    }

    if ((status = sBluetoothAvrcpInterface->send_group_navigation_cmd((bt_bdaddr_t *)addr,
            (uint8_t)key_code, (uint8_t)key_state))!= BT_STATUS_SUCCESS) {
        ALOGE("Failed sending Grp Navigation command, status: %d", status);
    }
    env->ReleaseByteArrayElements(address, addr, 0);

    return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}

static void setPlayerApplicationSettingValuesNative(JNIEnv *env, jobject object, jbyteArray address,
                                                    jbyte num_attrib, jbyteArray attrib_ids,
                                                    jbyteArray attrib_val) {
    bt_status_t status;
    jbyte *addr;
    uint8_t *pAttrs = NULL;
    uint8_t *pAttrsVal = NULL;
    int i;
    jbyte *attr;
    jbyte *attr_val;

    if (!sBluetoothAvrcpInterface) return;

    addr = env->GetByteArrayElements(address, NULL);
    if (!addr) {
        jniThrowIOException(env, EINVAL);
        return;
    }

    pAttrs = new uint8_t[num_attrib];
    pAttrsVal = new uint8_t[num_attrib];
    if ((!pAttrs) ||(!pAttrsVal)) {
        delete[] pAttrs;
        ALOGE("setPlayerApplicationSettingValuesNative: not have enough memeory");
        return;
    }
    attr = env->GetByteArrayElements(attrib_ids, NULL);
    attr_val = env->GetByteArrayElements(attrib_val, NULL);
    if ((!attr)||(!attr_val)) {
        delete[] pAttrs;
        delete[] pAttrsVal;
        jniThrowIOException(env, EINVAL);
        return;
    }
    for (i = 0; i < num_attrib; ++i) {
        pAttrs[i] = (uint8_t)attr[i];
        pAttrsVal[i] = (uint8_t)attr_val[i];
    }
    if (i < num_attrib) {
        delete[] pAttrs;
        delete[] pAttrsVal;
        env->ReleaseByteArrayElements(attrib_ids, attr, 0);
        env->ReleaseByteArrayElements(attrib_val, attr_val, 0);
        return;
    }

    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    if ((status = sBluetoothAvrcpInterface->set_player_app_setting_cmd((bt_bdaddr_t *)addr,
                                    (uint8_t)num_attrib, pAttrs, pAttrsVal))!= BT_STATUS_SUCCESS) {
        ALOGE("Failed sending setPlAppSettValNative command, status: %d", status);
    }
    delete[] pAttrs;
    delete[] pAttrsVal;
    env->ReleaseByteArrayElements(attrib_ids, attr, 0);
    env->ReleaseByteArrayElements(attrib_val, attr_val, 0);
    env->ReleaseByteArrayElements(address, addr, 0);
}

static void sendAbsVolRspNative(JNIEnv *env, jobject object, jbyteArray address,
                                jint abs_vol, jint label) {
    bt_status_t status;
    jbyte *addr;

    if (!sBluetoothAvrcpInterface) return;
    addr = env->GetByteArrayElements(address, NULL);
    if (!addr) {
        jniThrowIOException(env, EINVAL);
        return;
    }

    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    if ((status = sBluetoothAvrcpInterface->set_volume_rsp((bt_bdaddr_t *)addr,
                  (uint8_t)abs_vol, (uint8_t)label))!= BT_STATUS_SUCCESS) {
        ALOGE("Failed sending sendAbsVolRspNative command, status: %d", status);
    }
    env->ReleaseByteArrayElements(address, addr, 0);
}

static void sendRegisterAbsVolRspNative(JNIEnv *env, jobject object, jbyteArray address,
                                        jbyte rsp_type, jint abs_vol, jint label) {
    bt_status_t status;
    jbyte *addr;

    if (!sBluetoothAvrcpInterface) return;
    addr = env->GetByteArrayElements(address, NULL);
    if (!addr) {
        jniThrowIOException(env, EINVAL);
        return;
    }
    ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
    if ((status = sBluetoothAvrcpInterface->register_abs_vol_rsp((bt_bdaddr_t *)addr,
                  (btrc_notification_type_t)rsp_type,(uint8_t)abs_vol, (uint8_t)label))
                  != BT_STATUS_SUCCESS) {
        ALOGE("Failed sending sendRegisterAbsVolRspNative command, status: %d", status);
    }
    env->ReleaseByteArrayElements(address, addr, 0);
}

static JNINativeMethod sMethods[] = {
    {"classInitNative", "()V", (void *) classInitNative},
    {"initNative", "()V", (void *) initNative},
    {"cleanupNative", "()V", (void *) cleanupNative},
    {"sendPassThroughCommandNative", "([BII)Z",
     (void *) sendPassThroughCommandNative},
    {"sendPassThroughCommandNative", "([BII)Z",(void *) sendPassThroughCommandNative},
    {"sendGroupNavigationCommandNative", "([BII)Z",(void *) sendGroupNavigationCommandNative},
    {"setPlayerApplicationSettingValuesNative", "([BB[B[B)V",
                               (void *) setPlayerApplicationSettingValuesNative},
    {"sendAbsVolRspNative", "([BII)V",(void *) sendAbsVolRspNative},
    {"sendRegisterAbsVolRspNative", "([BBII)V",(void *) sendRegisterAbsVolRspNative},
};

int register_com_android_bluetooth_avrcp_controller(JNIEnv* env)
Loading