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,jint maxConnectedAudioDevices)112 static void initNative(JNIEnv* env, jobject object,
113                        jint maxConnectedAudioDevices) {
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                                                      maxConnectedAudioDevices);
142   if (status != BT_STATUS_SUCCESS) {
143     ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status);
144     sBluetoothA2dpInterface = NULL;
145     return;
146   }
147 
148   mCallbacksObj = env->NewGlobalRef(object);
149 }
150 
cleanupNative(JNIEnv * env,jobject object)151 static void cleanupNative(JNIEnv* env, jobject object) {
152   const bt_interface_t* btInf = getBluetoothInterface();
153 
154   if (btInf == NULL) {
155     ALOGE("Bluetooth module is not loaded");
156     return;
157   }
158 
159   if (sBluetoothA2dpInterface != NULL) {
160     sBluetoothA2dpInterface->cleanup();
161     sBluetoothA2dpInterface = NULL;
162   }
163 
164   if (mCallbacksObj != NULL) {
165     env->DeleteGlobalRef(mCallbacksObj);
166     mCallbacksObj = NULL;
167   }
168 }
169 
connectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)170 static jboolean connectA2dpNative(JNIEnv* env, jobject object,
171                                   jbyteArray address) {
172   ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
173   if (!sBluetoothA2dpInterface) return JNI_FALSE;
174 
175   jbyte* addr = env->GetByteArrayElements(address, NULL);
176   if (!addr) {
177     jniThrowIOException(env, EINVAL);
178     return JNI_FALSE;
179   }
180 
181   RawAddress bd_addr;
182   bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
183   bt_status_t status = sBluetoothA2dpInterface->connect(bd_addr);
184   if (status != BT_STATUS_SUCCESS) {
185     ALOGE("Failed HF connection, status: %d", status);
186   }
187   env->ReleaseByteArrayElements(address, addr, 0);
188   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
189 }
190 
disconnectA2dpNative(JNIEnv * env,jobject object,jbyteArray address)191 static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
192                                      jbyteArray address) {
193   if (!sBluetoothA2dpInterface) return JNI_FALSE;
194 
195   jbyte* addr = env->GetByteArrayElements(address, NULL);
196   if (!addr) {
197     jniThrowIOException(env, EINVAL);
198     return JNI_FALSE;
199   }
200 
201   RawAddress bd_addr;
202   bd_addr.FromOctets(reinterpret_cast<const uint8_t*>(addr));
203   bt_status_t status = sBluetoothA2dpInterface->disconnect(bd_addr);
204   if (status != BT_STATUS_SUCCESS) {
205     ALOGE("Failed HF disconnection, status: %d", status);
206   }
207   env->ReleaseByteArrayElements(address, addr, 0);
208   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
209 }
210 
informAudioFocusStateNative(JNIEnv * env,jobject object,jint focus_state)211 static void informAudioFocusStateNative(JNIEnv* env, jobject object,
212                                         jint focus_state) {
213   if (!sBluetoothA2dpInterface) return;
214   sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state);
215 }
216 
informAudioTrackGainNative(JNIEnv * env,jobject object,jfloat gain)217 static void informAudioTrackGainNative(JNIEnv* env, jobject object,
218                                        jfloat gain) {
219   if (!sBluetoothA2dpInterface) return;
220   sBluetoothA2dpInterface->set_audio_track_gain((float)gain);
221 }
222 
setActiveDeviceNative(JNIEnv * env,jobject object,jbyteArray address)223 static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
224                                       jbyteArray address) {
225   if (!sBluetoothA2dpInterface) return JNI_FALSE;
226 
227   ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
228 
229   jbyte* addr = env->GetByteArrayElements(address, NULL);
230   if (!addr) {
231     jniThrowIOException(env, EINVAL);
232     return JNI_FALSE;
233   }
234 
235   RawAddress rawAddress;
236   rawAddress.FromOctets((uint8_t*)addr);
237   bt_status_t status = sBluetoothA2dpInterface->set_active_device(rawAddress);
238   if (status != BT_STATUS_SUCCESS) {
239     ALOGE("Failed sending passthru command, status: %d", status);
240   }
241   env->ReleaseByteArrayElements(address, addr, 0);
242 
243   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
244 }
245 
246 static JNINativeMethod sMethods[] = {
247     {"classInitNative", "()V", (void*)classInitNative},
248     {"initNative", "(I)V", (void*)initNative},
249     {"cleanupNative", "()V", (void*)cleanupNative},
250     {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
251     {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
252     {"informAudioFocusStateNative", "(I)V", (void*)informAudioFocusStateNative},
253     {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative},
254     {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
255 };
256 
register_com_android_bluetooth_a2dp_sink(JNIEnv * env)257 int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) {
258   return jniRegisterNativeMethods(
259       env, "com/android/bluetooth/a2dpsink/A2dpSinkService", sMethods,
260       NELEM(sMethods));
261 }
262 }
263