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