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 "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_onAudioConfigChanged;
     32 
     33 static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL;
     34 static jobject mCallbacksObj = NULL;
     35 
     36 static void bta2dp_connection_state_callback(btav_connection_state_t state,
     37                                              RawAddress* bd_addr) {
     38   ALOGI("%s", __func__);
     39   CallbackEnv sCallbackEnv(__func__);
     40   if (!sCallbackEnv.valid()) return;
     41 
     42   ScopedLocalRef<jbyteArray> addr(
     43       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
     44   if (!addr.get()) {
     45     ALOGE("Fail to new jbyteArray bd addr for connection state");
     46     return;
     47   }
     48 
     49   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
     50                                    (jbyte*)bd_addr);
     51   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
     52                                (jint)state, addr.get());
     53 }
     54 
     55 static void bta2dp_audio_state_callback(btav_audio_state_t state,
     56                                         RawAddress* bd_addr) {
     57   ALOGI("%s", __func__);
     58   CallbackEnv sCallbackEnv(__func__);
     59   if (!sCallbackEnv.valid()) return;
     60 
     61   ScopedLocalRef<jbyteArray> addr(
     62       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
     63   if (!addr.get()) {
     64     ALOGE("Fail to new jbyteArray bd addr for connection state");
     65     return;
     66   }
     67 
     68   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
     69                                    (jbyte*)bd_addr);
     70   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
     71                                (jint)state, addr.get());
     72 }
     73 
     74 static void bta2dp_audio_config_callback(RawAddress* bd_addr,
     75                                          uint32_t sample_rate,
     76                                          uint8_t channel_count) {
     77   ALOGI("%s", __func__);
     78   CallbackEnv sCallbackEnv(__func__);
     79   if (!sCallbackEnv.valid()) return;
     80 
     81   ScopedLocalRef<jbyteArray> addr(
     82       sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
     83   if (!addr.get()) {
     84     ALOGE("Fail to new jbyteArray bd addr for connection state");
     85     return;
     86   }
     87 
     88   sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
     89                                    (jbyte*)bd_addr);
     90   sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioConfigChanged,
     91                                addr.get(), (jint)sample_rate,
     92                                (jint)channel_count);
     93 }
     94 
     95 static btav_sink_callbacks_t sBluetoothA2dpCallbacks = {
     96     sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
     97     bta2dp_audio_state_callback, bta2dp_audio_config_callback,
     98 };
     99 
    100 static void classInitNative(JNIEnv* env, jclass clazz) {
    101   method_onConnectionStateChanged =
    102       env->GetMethodID(clazz, "onConnectionStateChanged", "(I[B)V");
    103 
    104   method_onAudioStateChanged =
    105       env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
    106 
    107   method_onAudioConfigChanged =
    108       env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");
    109 
    110   ALOGI("%s: succeeds", __func__);
    111 }
    112 
    113 static void initNative(JNIEnv* env, jobject object) {
    114   const bt_interface_t* btInf = getBluetoothInterface();
    115   if (btInf == NULL) {
    116     ALOGE("Bluetooth module is not loaded");
    117     return;
    118   }
    119 
    120   if (sBluetoothA2dpInterface != NULL) {
    121     ALOGW("Cleaning up A2DP Interface before initializing...");
    122     sBluetoothA2dpInterface->cleanup();
    123     sBluetoothA2dpInterface = NULL;
    124   }
    125 
    126   if (mCallbacksObj != NULL) {
    127     ALOGW("Cleaning up A2DP callback object");
    128     env->DeleteGlobalRef(mCallbacksObj);
    129     mCallbacksObj = NULL;
    130   }
    131 
    132   sBluetoothA2dpInterface =
    133       (btav_sink_interface_t*)btInf->get_profile_interface(
    134           BT_PROFILE_ADVANCED_AUDIO_SINK_ID);
    135   if (sBluetoothA2dpInterface == NULL) {
    136     ALOGE("Failed to get Bluetooth A2DP Sink Interface");
    137     return;
    138   }
    139 
    140   bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks);
    141   if (status != BT_STATUS_SUCCESS) {
    142     ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status);
    143     sBluetoothA2dpInterface = NULL;
    144     return;
    145   }
    146 
    147   mCallbacksObj = env->NewGlobalRef(object);
    148 }
    149 
    150 static void cleanupNative(JNIEnv* env, jobject object) {
    151   const bt_interface_t* btInf = getBluetoothInterface();
    152 
    153   if (btInf == NULL) {
    154     ALOGE("Bluetooth module is not loaded");
    155     return;
    156   }
    157 
    158   if (sBluetoothA2dpInterface != NULL) {
    159     sBluetoothA2dpInterface->cleanup();
    160     sBluetoothA2dpInterface = NULL;
    161   }
    162 
    163   if (mCallbacksObj != NULL) {
    164     env->DeleteGlobalRef(mCallbacksObj);
    165     mCallbacksObj = NULL;
    166   }
    167 }
    168 
    169 static jboolean connectA2dpNative(JNIEnv* env, jobject object,
    170                                   jbyteArray address) {
    171   ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
    172   if (!sBluetoothA2dpInterface) return JNI_FALSE;
    173 
    174   jbyte* addr = env->GetByteArrayElements(address, NULL);
    175   if (!addr) {
    176     jniThrowIOException(env, EINVAL);
    177     return JNI_FALSE;
    178   }
    179 
    180   bt_status_t status = sBluetoothA2dpInterface->connect((RawAddress*)addr);
    181   if (status != BT_STATUS_SUCCESS) {
    182     ALOGE("Failed HF connection, status: %d", status);
    183   }
    184   env->ReleaseByteArrayElements(address, addr, 0);
    185   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    186 }
    187 
    188 static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
    189                                      jbyteArray address) {
    190   if (!sBluetoothA2dpInterface) return JNI_FALSE;
    191 
    192   jbyte* addr = env->GetByteArrayElements(address, NULL);
    193   if (!addr) {
    194     jniThrowIOException(env, EINVAL);
    195     return JNI_FALSE;
    196   }
    197 
    198   bt_status_t status = sBluetoothA2dpInterface->disconnect((RawAddress*)addr);
    199   if (status != BT_STATUS_SUCCESS) {
    200     ALOGE("Failed HF disconnection, status: %d", status);
    201   }
    202   env->ReleaseByteArrayElements(address, addr, 0);
    203   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
    204 }
    205 
    206 static void informAudioFocusStateNative(JNIEnv* env, jobject object,
    207                                         jint focus_state) {
    208   if (!sBluetoothA2dpInterface) return;
    209   sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state);
    210 }
    211 
    212 static void informAudioTrackGainNative(JNIEnv* env, jobject object,
    213                                        jfloat gain) {
    214   if (!sBluetoothA2dpInterface) return;
    215   sBluetoothA2dpInterface->set_audio_track_gain((float)gain);
    216 }
    217 
    218 static JNINativeMethod sMethods[] = {
    219     {"classInitNative", "()V", (void*)classInitNative},
    220     {"initNative", "()V", (void*)initNative},
    221     {"cleanupNative", "()V", (void*)cleanupNative},
    222     {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
    223     {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
    224     {"informAudioFocusStateNative", "(I)V", (void*)informAudioFocusStateNative},
    225     {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative},
    226 };
    227 
    228 int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) {
    229   return jniRegisterNativeMethods(
    230       env, "com/android/bluetooth/a2dpsink/A2dpSinkStateMachine", sMethods,
    231       NELEM(sMethods));
    232 }
    233 }
    234