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 "BluetoothA2dpSinkServiceJni"
     18 
     19 #define LOG_NDEBUG 0
     20 
     21 #include "com_android_bluetooth.h"
     22 #include "hardware/bt_av.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_onConnectionStateChanged;
     30 static jmethodID method_onAudioStateChanged;
     31 static jmethodID method_onAudioConfigChanged;
     32 
     33 static const btav_interface_t *sBluetoothA2dpInterface = 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     //if (sCallbackEnv == NULL) {
     43     sCallbackEnv = getCallbackEnv();
     44     //}
     45 
     46     JNIEnv* env = AndroidRuntime::getJNIEnv();
     47     if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
     48     return true;
     49 }
     50 
     51 static void bta2dp_connection_state_callback(btav_connection_state_t state, bt_bdaddr_t* bd_addr) {
     52     jbyteArray addr;
     53 
     54     ALOGI("%s", __FUNCTION__);
     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_onConnectionStateChanged, (jint) state,
     69                                  addr);
     70     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     71     sCallbackEnv->DeleteLocalRef(addr);
     72 }
     73 
     74 static void bta2dp_audio_state_callback(btav_audio_state_t state, bt_bdaddr_t* bd_addr) {
     75     jbyteArray addr;
     76 
     77     ALOGI("%s", __FUNCTION__);
     78 
     79     if (!checkCallbackThread()) {                                       \
     80         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
     81         return;                                                         \
     82     }
     83     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
     84     if (!addr) {
     85         ALOGE("Fail to new jbyteArray bd addr for connection state");
     86         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     87         return;
     88     }
     89 
     90     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
     91     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint) state,
     92                                  addr);
     93     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     94     sCallbackEnv->DeleteLocalRef(addr);
     95 }
     96 
     97 static void bta2dp_audio_config_callback(bt_bdaddr_t *bd_addr, uint32_t sample_rate, uint8_t channel_count) {
     98     jbyteArray addr;
     99 
    100     ALOGI("%s", __FUNCTION__);
    101 
    102     if (!checkCallbackThread()) {                                       \
    103         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
    104         return;                                                         \
    105     }
    106     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
    107     if (!addr) {
    108         ALOGE("Fail to new jbyteArray bd addr for connection state");
    109         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    110         return;
    111     }
    112 
    113     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
    114     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioConfigChanged, addr, (jint)sample_rate, (jint)channel_count);
    115     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
    116     sCallbackEnv->DeleteLocalRef(addr);
    117 }
    118 
    119 static btav_callbacks_t sBluetoothA2dpCallbacks = {
    120     sizeof(sBluetoothA2dpCallbacks),
    121     bta2dp_connection_state_callback,
    122     bta2dp_audio_state_callback,
    123     bta2dp_audio_config_callback
    124 };
    125 
    126 static void classInitNative(JNIEnv* env, jclass clazz) {
    127     int err;
    128     const bt_interface_t* btInf;
    129     bt_status_t status;
    130 
    131     method_onConnectionStateChanged =
    132         env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
    133 
    134     method_onAudioStateChanged =
    135         env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
    136 
    137     method_onAudioConfigChanged =
    138         env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");
    139 
    140     ALOGI("%s: succeeds", __FUNCTION__);
    141 }
    142 
    143 static void initNative(JNIEnv *env, jobject object) {
    144     const bt_interface_t* btInf;
    145     bt_status_t status;
    146 
    147     if ( (btInf = getBluetoothInterface()) == NULL) {
    148         ALOGE("Bluetooth module is not loaded");
    149         return;
    150     }
    151 
    152     if (sBluetoothA2dpInterface !=NULL) {
    153          ALOGW("Cleaning up A2DP Interface before initializing...");
    154          sBluetoothA2dpInterface->cleanup();
    155          sBluetoothA2dpInterface = NULL;
    156     }
    157 
    158     if (mCallbacksObj != NULL) {
    159          ALOGW("Cleaning up A2DP callback object");
    160          env->DeleteGlobalRef(mCallbacksObj);
    161          mCallbacksObj = NULL;
    162     }
    163 
    164     if ( (sBluetoothA2dpInterface = (btav_interface_t *)
    165           btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_SINK_ID)) == NULL) {
    166         ALOGE("Failed to get Bluetooth A2DP Sink Interface");
    167         return;
    168     }
    169 
    170     if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
    171         ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status);
    172         sBluetoothA2dpInterface = NULL;
    173         return;
    174     }
    175 
    176     mCallbacksObj = env->NewGlobalRef(object);
    177 }
    178 
    179 static void cleanupNative(JNIEnv *env, jobject object) {
    180     const bt_interface_t* btInf;
    181     bt_status_t status;
    182 
    183     if ( (btInf = getBluetoothInterface()) == NULL) {
    184         ALOGE("Bluetooth module is not loaded");
    185         return;
    186     }
    187 
    188     if (sBluetoothA2dpInterface !=NULL) {
    189         sBluetoothA2dpInterface->cleanup();
    190         sBluetoothA2dpInterface = NULL;
    191     }
    192 
    193     if (mCallbacksObj != NULL) {
    194         env->DeleteGlobalRef(mCallbacksObj);
    195         mCallbacksObj = NULL;
    196     }
    197 }
    198 
    199 static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
    200     jbyte *addr;
    201     bt_bdaddr_t * btAddr;
    202     bt_status_t status;
    203 
    204     ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);
    205     if (!sBluetoothA2dpInterface) return JNI_FALSE;
    206 
    207     addr = env->GetByteArrayElements(address, NULL);
    208     btAddr = (bt_bdaddr_t *) addr;
    209     if (!addr) {
    210         jniThrowIOException(env, EINVAL);
    211         return JNI_FALSE;
    212     }
    213 
    214     if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
    215         ALOGE("Failed HF connection, status: %d", status);
    216     }
    217     env->ReleaseByteArrayElements(address, addr, 0);
    218     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    219 }
    220 
    221 static jboolean disconnectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
    222     jbyte *addr;
    223     bt_status_t status;
    224 
    225     if (!sBluetoothA2dpInterface) return JNI_FALSE;
    226 
    227     addr = env->GetByteArrayElements(address, NULL);
    228     if (!addr) {
    229         jniThrowIOException(env, EINVAL);
    230         return JNI_FALSE;
    231     }
    232 
    233     if ( (status = sBluetoothA2dpInterface->disconnect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
    234         ALOGE("Failed HF disconnection, status: %d", status);
    235     }
    236     env->ReleaseByteArrayElements(address, addr, 0);
    237     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    238 }
    239 
    240 static JNINativeMethod sMethods[] = {
    241     {"classInitNative", "()V", (void *) classInitNative},
    242     {"initNative", "()V", (void *) initNative},
    243     {"cleanupNative", "()V", (void *) cleanupNative},
    244     {"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
    245     {"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
    246 };
    247 
    248 int register_com_android_bluetooth_a2dp_sink(JNIEnv* env)
    249 {
    250     return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/A2dpSinkStateMachine",
    251                                     sMethods, NELEM(sMethods));
    252 }
    253 
    254 }
    255