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 "BluetoothA2dpServiceJni"
     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 
     32 static const btav_interface_t *sBluetoothA2dpInterface = NULL;
     33 static jobject mCallbacksObj = NULL;
     34 static JNIEnv *sCallbackEnv = NULL;
     35 
     36 static bool checkCallbackThread() {
     37     // Always fetch the latest callbackEnv from AdapterService.
     38     // Caching this could cause this sCallbackEnv to go out-of-sync
     39     // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
     40     // is received
     41     //if (sCallbackEnv == NULL) {
     42     sCallbackEnv = getCallbackEnv();
     43     //}
     44 
     45     JNIEnv* env = AndroidRuntime::getJNIEnv();
     46     if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
     47     return true;
     48 }
     49 
     50 static void bta2dp_connection_state_callback(btav_connection_state_t state, bt_bdaddr_t* bd_addr) {
     51     jbyteArray addr;
     52 
     53     ALOGI("%s", __FUNCTION__);
     54 
     55     if (!checkCallbackThread()) {                                       \
     56         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
     57         return;                                                         \
     58     }
     59     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
     60     if (!addr) {
     61         ALOGE("Fail to new jbyteArray bd addr for connection state");
     62         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     63         return;
     64     }
     65 
     66     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
     67     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint) state,
     68                                  addr);
     69     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     70     sCallbackEnv->DeleteLocalRef(addr);
     71 }
     72 
     73 static void bta2dp_audio_state_callback(btav_audio_state_t state, bt_bdaddr_t* bd_addr) {
     74     jbyteArray addr;
     75 
     76     ALOGI("%s", __FUNCTION__);
     77 
     78     if (!checkCallbackThread()) {                                       \
     79         ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
     80         return;                                                         \
     81     }
     82     addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
     83     if (!addr) {
     84         ALOGE("Fail to new jbyteArray bd addr for connection state");
     85         checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     86         return;
     87     }
     88 
     89     sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
     90     sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint) state,
     91                                  addr);
     92     checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
     93     sCallbackEnv->DeleteLocalRef(addr);
     94 }
     95 
     96 static btav_callbacks_t sBluetoothA2dpCallbacks = {
     97     sizeof(sBluetoothA2dpCallbacks),
     98     bta2dp_connection_state_callback,
     99     bta2dp_audio_state_callback
    100 };
    101 
    102 static void classInitNative(JNIEnv* env, jclass clazz) {
    103     int err;
    104     const bt_interface_t* btInf;
    105     bt_status_t status;
    106 
    107     method_onConnectionStateChanged =
    108         env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
    109 
    110     method_onAudioStateChanged =
    111         env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
    112     /*
    113     if ( (btInf = getBluetoothInterface()) == NULL) {
    114         ALOGE("Bluetooth module is not loaded");
    115         return;
    116     }
    117 
    118     if ( (sBluetoothA2dpInterface = (btav_interface_t *)
    119           btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
    120         ALOGE("Failed to get Bluetooth A2DP Interface");
    121         return;
    122     }
    123     */
    124 
    125     // TODO(BT) do this only once or
    126     //          Do we need to do this every time the BT reenables?
    127     /*
    128     if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
    129         ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
    130         sBluetoothA2dpInterface = NULL;
    131         return;
    132     }*/
    133 
    134     ALOGI("%s: succeeds", __FUNCTION__);
    135 }
    136 
    137 static void initNative(JNIEnv *env, jobject object) {
    138     const bt_interface_t* btInf;
    139     bt_status_t status;
    140 
    141     if ( (btInf = getBluetoothInterface()) == NULL) {
    142         ALOGE("Bluetooth module is not loaded");
    143         return;
    144     }
    145 
    146     if (sBluetoothA2dpInterface !=NULL) {
    147          ALOGW("Cleaning up A2DP Interface before initializing...");
    148          sBluetoothA2dpInterface->cleanup();
    149          sBluetoothA2dpInterface = NULL;
    150     }
    151 
    152     if (mCallbacksObj != NULL) {
    153          ALOGW("Cleaning up A2DP callback object");
    154          env->DeleteGlobalRef(mCallbacksObj);
    155          mCallbacksObj = NULL;
    156     }
    157 
    158     if ( (sBluetoothA2dpInterface = (btav_interface_t *)
    159           btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
    160         ALOGE("Failed to get Bluetooth A2DP Interface");
    161         return;
    162     }
    163 
    164     if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
    165         ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
    166         sBluetoothA2dpInterface = NULL;
    167         return;
    168     }
    169 
    170     mCallbacksObj = env->NewGlobalRef(object);
    171 }
    172 
    173 static void cleanupNative(JNIEnv *env, jobject object) {
    174     const bt_interface_t* btInf;
    175     bt_status_t status;
    176 
    177     if ( (btInf = getBluetoothInterface()) == NULL) {
    178         ALOGE("Bluetooth module is not loaded");
    179         return;
    180     }
    181 
    182     if (sBluetoothA2dpInterface !=NULL) {
    183         sBluetoothA2dpInterface->cleanup();
    184         sBluetoothA2dpInterface = NULL;
    185     }
    186 
    187     if (mCallbacksObj != NULL) {
    188         env->DeleteGlobalRef(mCallbacksObj);
    189         mCallbacksObj = NULL;
    190     }
    191 }
    192 
    193 static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
    194     jbyte *addr;
    195     bt_bdaddr_t * btAddr;
    196     bt_status_t status;
    197 
    198     ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);
    199     if (!sBluetoothA2dpInterface) return JNI_FALSE;
    200 
    201     addr = env->GetByteArrayElements(address, NULL);
    202     btAddr = (bt_bdaddr_t *) addr;
    203     if (!addr) {
    204         jniThrowIOException(env, EINVAL);
    205         return JNI_FALSE;
    206     }
    207 
    208     if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
    209         ALOGE("Failed HF connection, status: %d", status);
    210     }
    211     env->ReleaseByteArrayElements(address, addr, 0);
    212     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    213 }
    214 
    215 static jboolean disconnectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
    216     jbyte *addr;
    217     bt_status_t status;
    218 
    219     if (!sBluetoothA2dpInterface) return JNI_FALSE;
    220 
    221     addr = env->GetByteArrayElements(address, NULL);
    222     if (!addr) {
    223         jniThrowIOException(env, EINVAL);
    224         return JNI_FALSE;
    225     }
    226 
    227     if ( (status = sBluetoothA2dpInterface->disconnect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
    228         ALOGE("Failed HF disconnection, status: %d", status);
    229     }
    230     env->ReleaseByteArrayElements(address, addr, 0);
    231     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    232 }
    233 
    234 static JNINativeMethod sMethods[] = {
    235     {"classInitNative", "()V", (void *) classInitNative},
    236     {"initNative", "()V", (void *) initNative},
    237     {"cleanupNative", "()V", (void *) cleanupNative},
    238     {"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
    239     {"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
    240 };
    241 
    242 int register_com_android_bluetooth_a2dp(JNIEnv* env)
    243 {
    244     return jniRegisterNativeMethods(env, "com/android/bluetooth/a2dp/A2dpStateMachine",
    245                                     sMethods, NELEM(sMethods));
    246 }
    247 
    248 }
    249