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 "android_runtime/AndroidRuntime.h"
     22 #include "com_android_bluetooth.h"
     23 #include "hardware/bt_av.h"
     24 #include "utils/Log.h"
     25 
     26 #include <string.h>
     27 
     28 namespace android {
     29 static jmethodID method_onConnectionStateChanged;
     30 static jmethodID method_onAudioStateChanged;
     31 static jmethodID method_onCodecConfigChanged;
     32 
     33 static struct {
     34   jclass clazz;
     35   jmethodID constructor;
     36   jmethodID getCodecType;
     37   jmethodID getCodecPriority;
     38   jmethodID getSampleRate;
     39   jmethodID getBitsPerSample;
     40   jmethodID getChannelMode;
     41   jmethodID getCodecSpecific1;
     42   jmethodID getCodecSpecific2;
     43   jmethodID getCodecSpecific3;
     44   jmethodID getCodecSpecific4;
     45 } android_bluetooth_BluetoothCodecConfig;
     46 
     47 static const btav_source_interface_t* sBluetoothA2dpInterface = NULL;
     48 static jobject mCallbacksObj = NULL;
     49 
     50 static void bta2dp_connection_state_callback(btav_connection_state_t state,
     51                                              RawAddress* bd_addr) {
     52   ALOGI("%s", __func__);
     53   CallbackEnv sCallbackEnv(__func__);
     54   if (!sCallbackEnv.valid()) return;
     55 
     56   ScopedLocalRef<jbyteArray> addr(
     57       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
     58   if (!addr.get()) {
     59     ALOGE("Fail to new jbyteArray bd addr for connection state");
     60     return;
     61   }
     62 
     63   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
     64                                    (jbyte*)bd_addr);
     65   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
     66                                (jint)state, addr.get());
     67 }
     68 
     69 static void bta2dp_audio_state_callback(btav_audio_state_t state,
     70                                         RawAddress* bd_addr) {
     71   ALOGI("%s", __func__);
     72   CallbackEnv sCallbackEnv(__func__);
     73   if (!sCallbackEnv.valid()) return;
     74 
     75   ScopedLocalRef<jbyteArray> addr(
     76       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
     77   if (!addr.get()) {
     78     ALOGE("Fail to new jbyteArray bd addr for connection state");
     79     return;
     80   }
     81 
     82   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
     83                                    (jbyte*)bd_addr);
     84   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
     85                                (jint)state, addr.get());
     86 }
     87 
     88 static void bta2dp_audio_config_callback(
     89     btav_a2dp_codec_config_t codec_config,
     90     std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
     91     std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) {
     92   ALOGI("%s", __func__);
     93   CallbackEnv sCallbackEnv(__func__);
     94   if (!sCallbackEnv.valid()) return;
     95 
     96   jobject codecConfigObj = sCallbackEnv->NewObject(
     97       android_bluetooth_BluetoothCodecConfig.clazz,
     98       android_bluetooth_BluetoothCodecConfig.constructor,
     99       (jint)codec_config.codec_type, (jint)codec_config.codec_priority,
    100       (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample,
    101       (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1,
    102       (jlong)codec_config.codec_specific_2,
    103       (jlong)codec_config.codec_specific_3,
    104       (jlong)codec_config.codec_specific_4);
    105 
    106   jsize i = 0;
    107   jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray(
    108       (jsize)codecs_local_capabilities.size(),
    109       android_bluetooth_BluetoothCodecConfig.clazz, NULL);
    110   for (auto const& cap : codecs_local_capabilities) {
    111     jobject capObj = sCallbackEnv->NewObject(
    112         android_bluetooth_BluetoothCodecConfig.clazz,
    113         android_bluetooth_BluetoothCodecConfig.constructor,
    114         (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
    115         (jint)cap.bits_per_sample, (jint)cap.channel_mode,
    116         (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
    117         (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
    118     sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj);
    119     sCallbackEnv->DeleteLocalRef(capObj);
    120   }
    121 
    122   i = 0;
    123   jobjectArray selectable_capabilities_array = sCallbackEnv->NewObjectArray(
    124       (jsize)codecs_selectable_capabilities.size(),
    125       android_bluetooth_BluetoothCodecConfig.clazz, NULL);
    126   for (auto const& cap : codecs_selectable_capabilities) {
    127     jobject capObj = sCallbackEnv->NewObject(
    128         android_bluetooth_BluetoothCodecConfig.clazz,
    129         android_bluetooth_BluetoothCodecConfig.constructor,
    130         (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
    131         (jint)cap.bits_per_sample, (jint)cap.channel_mode,
    132         (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
    133         (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
    134     sCallbackEnv->SetObjectArrayElement(selectable_capabilities_array, i++,
    135                                         capObj);
    136     sCallbackEnv->DeleteLocalRef(capObj);
    137   }
    138 
    139   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCodecConfigChanged,
    140                                codecConfigObj, local_capabilities_array,
    141                                selectable_capabilities_array);
    142 }
    143 
    144 static btav_source_callbacks_t sBluetoothA2dpCallbacks = {
    145     sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
    146     bta2dp_audio_state_callback, bta2dp_audio_config_callback,
    147 };
    148 
    149 static void classInitNative(JNIEnv* env, jclass clazz) {
    150   jclass jniBluetoothCodecConfigClass =
    151       env->FindClass("android/bluetooth/BluetoothCodecConfig");
    152   android_bluetooth_BluetoothCodecConfig.constructor =
    153       env->GetMethodID(jniBluetoothCodecConfigClass, "<init>", "(IIIIIJJJJ)V");
    154   android_bluetooth_BluetoothCodecConfig.getCodecType =
    155       env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
    156   android_bluetooth_BluetoothCodecConfig.getCodecPriority =
    157       env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
    158   android_bluetooth_BluetoothCodecConfig.getSampleRate =
    159       env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
    160   android_bluetooth_BluetoothCodecConfig.getBitsPerSample =
    161       env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
    162   android_bluetooth_BluetoothCodecConfig.getChannelMode =
    163       env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
    164   android_bluetooth_BluetoothCodecConfig.getCodecSpecific1 = env->GetMethodID(
    165       jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
    166   android_bluetooth_BluetoothCodecConfig.getCodecSpecific2 = env->GetMethodID(
    167       jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
    168   android_bluetooth_BluetoothCodecConfig.getCodecSpecific3 = env->GetMethodID(
    169       jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
    170   android_bluetooth_BluetoothCodecConfig.getCodecSpecific4 = env->GetMethodID(
    171       jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
    172 
    173   method_onConnectionStateChanged =
    174       env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
    175 
    176   method_onAudioStateChanged =
    177       env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
    178 
    179   method_onCodecConfigChanged = env->GetMethodID(
    180       clazz, "onCodecConfigChanged",
    181       "(Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/"
    182       "BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V");
    183 
    184   ALOGI("%s: succeeds", __func__);
    185 }
    186 
    187 static std::vector<btav_a2dp_codec_config_t> prepareCodecPreferences(
    188     JNIEnv* env, jobject object, jobjectArray codecConfigArray) {
    189   std::vector<btav_a2dp_codec_config_t> codec_preferences;
    190 
    191   int numConfigs = env->GetArrayLength(codecConfigArray);
    192   for (int i = 0; i < numConfigs; i++) {
    193     jobject jcodecConfig = env->GetObjectArrayElement(codecConfigArray, i);
    194     if (jcodecConfig == nullptr) continue;
    195     if (!env->IsInstanceOf(jcodecConfig,
    196                            android_bluetooth_BluetoothCodecConfig.clazz)) {
    197       ALOGE("Invalid BluetoothCodecConfig instance");
    198       continue;
    199     }
    200     jint codecType = env->CallIntMethod(
    201         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType);
    202     jint codecPriority = env->CallIntMethod(
    203         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecPriority);
    204     jint sampleRate = env->CallIntMethod(
    205         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getSampleRate);
    206     jint bitsPerSample = env->CallIntMethod(
    207         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getBitsPerSample);
    208     jint channelMode = env->CallIntMethod(
    209         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getChannelMode);
    210     jlong codecSpecific1 = env->CallLongMethod(
    211         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific1);
    212     jlong codecSpecific2 = env->CallLongMethod(
    213         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific2);
    214     jlong codecSpecific3 = env->CallLongMethod(
    215         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific3);
    216     jlong codecSpecific4 = env->CallLongMethod(
    217         jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific4);
    218 
    219     btav_a2dp_codec_config_t codec_config = {
    220         .codec_type = static_cast<btav_a2dp_codec_index_t>(codecType),
    221         .codec_priority =
    222             static_cast<btav_a2dp_codec_priority_t>(codecPriority),
    223         .sample_rate = static_cast<btav_a2dp_codec_sample_rate_t>(sampleRate),
    224         .bits_per_sample =
    225             static_cast<btav_a2dp_codec_bits_per_sample_t>(bitsPerSample),
    226         .channel_mode =
    227             static_cast<btav_a2dp_codec_channel_mode_t>(channelMode),
    228         .codec_specific_1 = codecSpecific1,
    229         .codec_specific_2 = codecSpecific2,
    230         .codec_specific_3 = codecSpecific3,
    231         .codec_specific_4 = codecSpecific4};
    232 
    233     codec_preferences.push_back(codec_config);
    234   }
    235   return codec_preferences;
    236 }
    237 
    238 static void initNative(JNIEnv* env, jobject object,
    239                        jobjectArray codecConfigArray) {
    240   const bt_interface_t* btInf = getBluetoothInterface();
    241   if (btInf == NULL) {
    242     ALOGE("Bluetooth module is not loaded");
    243     return;
    244   }
    245 
    246   if (sBluetoothA2dpInterface != NULL) {
    247     ALOGW("Cleaning up A2DP Interface before initializing...");
    248     sBluetoothA2dpInterface->cleanup();
    249     sBluetoothA2dpInterface = NULL;
    250   }
    251 
    252   if (mCallbacksObj != NULL) {
    253     ALOGW("Cleaning up A2DP callback object");
    254     env->DeleteGlobalRef(mCallbacksObj);
    255     mCallbacksObj = NULL;
    256   }
    257 
    258   if ((mCallbacksObj = env->NewGlobalRef(object)) == NULL) {
    259     ALOGE("Failed to allocate Global Ref for A2DP Callbacks");
    260     return;
    261   }
    262 
    263   android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(
    264       env->FindClass("android/bluetooth/BluetoothCodecConfig"));
    265   if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {
    266     ALOGE("Failed to allocate Global Ref for BluetoothCodecConfig class");
    267     return;
    268   }
    269 
    270   sBluetoothA2dpInterface =
    271       (btav_source_interface_t*)btInf->get_profile_interface(
    272           BT_PROFILE_ADVANCED_AUDIO_ID);
    273   if (sBluetoothA2dpInterface == NULL) {
    274     ALOGE("Failed to get Bluetooth A2DP Interface");
    275     return;
    276   }
    277 
    278   std::vector<btav_a2dp_codec_config_t> codec_priorities =
    279       prepareCodecPreferences(env, object, codecConfigArray);
    280 
    281   bt_status_t status =
    282       sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks, codec_priorities);
    283   if (status != BT_STATUS_SUCCESS) {
    284     ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
    285     sBluetoothA2dpInterface = NULL;
    286     return;
    287   }
    288 }
    289 
    290 static void cleanupNative(JNIEnv* env, jobject object) {
    291   const bt_interface_t* btInf = getBluetoothInterface();
    292   if (btInf == NULL) {
    293     ALOGE("Bluetooth module is not loaded");
    294     return;
    295   }
    296 
    297   if (sBluetoothA2dpInterface != NULL) {
    298     sBluetoothA2dpInterface->cleanup();
    299     sBluetoothA2dpInterface = NULL;
    300   }
    301 
    302   env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz);
    303   android_bluetooth_BluetoothCodecConfig.clazz = nullptr;
    304 
    305   if (mCallbacksObj != NULL) {
    306     env->DeleteGlobalRef(mCallbacksObj);
    307     mCallbacksObj = NULL;
    308   }
    309 }
    310 
    311 static jboolean connectA2dpNative(JNIEnv* env, jobject object,
    312                                   jbyteArray address) {
    313   ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
    314   if (!sBluetoothA2dpInterface) return JNI_FALSE;
    315 
    316   jbyte* addr = env->GetByteArrayElements(address, NULL);
    317   if (!addr) {
    318     jniThrowIOException(env, EINVAL);
    319     return JNI_FALSE;
    320   }
    321 
    322   bt_status_t status = sBluetoothA2dpInterface->connect((RawAddress*)addr);
    323   if (status != BT_STATUS_SUCCESS) {
    324     ALOGE("Failed HF connection, status: %d", status);
    325   }
    326   env->ReleaseByteArrayElements(address, addr, 0);
    327   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    328 }
    329 
    330 static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
    331                                      jbyteArray address) {
    332   if (!sBluetoothA2dpInterface) return JNI_FALSE;
    333 
    334   jbyte* addr = env->GetByteArrayElements(address, NULL);
    335   if (!addr) {
    336     jniThrowIOException(env, EINVAL);
    337     return JNI_FALSE;
    338   }
    339 
    340   bt_status_t status = sBluetoothA2dpInterface->disconnect((RawAddress*)addr);
    341   if (status != BT_STATUS_SUCCESS) {
    342     ALOGE("Failed HF disconnection, status: %d", status);
    343   }
    344   env->ReleaseByteArrayElements(address, addr, 0);
    345   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    346 }
    347 
    348 static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object,
    349                                                jobjectArray codecConfigArray) {
    350   if (!sBluetoothA2dpInterface) return JNI_FALSE;
    351 
    352   std::vector<btav_a2dp_codec_config_t> codec_preferences =
    353       prepareCodecPreferences(env, object, codecConfigArray);
    354 
    355   bt_status_t status = sBluetoothA2dpInterface->config_codec(codec_preferences);
    356   if (status != BT_STATUS_SUCCESS) {
    357     ALOGE("Failed codec configuration, status: %d", status);
    358   }
    359   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    360 }
    361 
    362 static JNINativeMethod sMethods[] = {
    363     {"classInitNative", "()V", (void*)classInitNative},
    364     {"initNative", "([Landroid/bluetooth/BluetoothCodecConfig;)V",
    365      (void*)initNative},
    366     {"cleanupNative", "()V", (void*)cleanupNative},
    367     {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
    368     {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
    369     {"setCodecConfigPreferenceNative",
    370      "([Landroid/bluetooth/BluetoothCodecConfig;)Z",
    371      (void*)setCodecConfigPreferenceNative},
    372 };
    373 
    374 int register_com_android_bluetooth_a2dp(JNIEnv* env) {
    375   return jniRegisterNativeMethods(env,
    376                                   "com/android/bluetooth/a2dp/A2dpStateMachine",
    377                                   sMethods, NELEM(sMethods));
    378 }
    379 }
    380