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 "BluetoothAvrcpServiceJni"
18
19 #define LOG_NDEBUG 0
20
21 #include "com_android_bluetooth.h"
22 #include "hardware/bt_rc.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_getRcFeatures;
30 static jmethodID method_getPlayStatus;
31 static jmethodID method_getElementAttr;
32 static jmethodID method_registerNotification;
33 static jmethodID method_volumeChangeCallback;
34 static jmethodID method_handlePassthroughCmd;
35
36 static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
37 static jobject mCallbacksObj = NULL;
38 static JNIEnv *sCallbackEnv = NULL;
39
checkCallbackThread()40 static bool checkCallbackThread() {
41 // Always fetch the latest callbackEnv from AdapterService.
42 // Caching this could cause this sCallbackEnv to go out-of-sync
43 // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
44 // is received
45 sCallbackEnv = getCallbackEnv();
46
47 JNIEnv* env = AndroidRuntime::getJNIEnv();
48 if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
49 return true;
50 }
51
btavrcp_remote_features_callback(bt_bdaddr_t * bd_addr,btrc_remote_features_t features)52 static void btavrcp_remote_features_callback(bt_bdaddr_t* bd_addr, btrc_remote_features_t features) {
53 ALOGI("%s", __FUNCTION__);
54 jbyteArray addr;
55
56 if (!checkCallbackThread()) {
57 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
58 return;
59 }
60 addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
61 if (!addr) {
62 ALOGE("Unable to allocate byte array for bd_addr");
63 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
64 return;
65 }
66
67 if (mCallbacksObj) {
68 sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
69 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features);
70 } else {
71 ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
72 }
73
74 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
75 sCallbackEnv->DeleteLocalRef(addr);
76 }
77
btavrcp_get_play_status_callback()78 static void btavrcp_get_play_status_callback() {
79 ALOGI("%s", __FUNCTION__);
80
81 if (!checkCallbackThread()) {
82 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
83 return;
84 }
85
86 if (mCallbacksObj) {
87 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus);
88 } else {
89 ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
90 }
91 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
92 }
93
btavrcp_get_element_attr_callback(uint8_t num_attr,btrc_media_attr_t * p_attrs)94 static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) {
95 jintArray attrs;
96
97 ALOGI("%s", __FUNCTION__);
98
99 if (!checkCallbackThread()) {
100 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
101 return;
102 }
103 attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
104 if (!attrs) {
105 ALOGE("Fail to new jintArray for attrs");
106 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
107 return;
108 }
109 sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
110 if (mCallbacksObj) {
111 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs);
112 } else {
113 ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
114 }
115 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
116 sCallbackEnv->DeleteLocalRef(attrs);
117 }
118
btavrcp_register_notification_callback(btrc_event_id_t event_id,uint32_t param)119 static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) {
120 if (!checkCallbackThread()) {
121 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
122 return;
123 }
124 if (mCallbacksObj) {
125 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
126 (jint)event_id, (jint)param);
127 } else {
128 ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
129 }
130 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
131 }
132
btavrcp_volume_change_callback(uint8_t volume,uint8_t ctype)133 static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype) {
134 ALOGI("%s", __FUNCTION__);
135
136 if (!checkCallbackThread()) {
137 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
138 return;
139 }
140 if (mCallbacksObj) {
141 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, (jint)volume,
142 (jint)ctype);
143 } else {
144 ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
145 }
146
147 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
148 }
149
btavrcp_passthrough_command_callback(int id,int pressed)150 static void btavrcp_passthrough_command_callback(int id, int pressed) {
151 ALOGI("%s", __FUNCTION__);
152
153 if (!checkCallbackThread()) {
154 ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
155 return;
156 }
157 if (mCallbacksObj) {
158 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd,
159 (jint)id, (jint)pressed);
160 } else {
161 ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
162 }
163 checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
164 }
165
166 static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
167 sizeof(sBluetoothAvrcpCallbacks),
168 btavrcp_remote_features_callback,
169 btavrcp_get_play_status_callback,
170 NULL,
171 NULL,
172 NULL,
173 NULL,
174 NULL,
175 NULL,
176 btavrcp_get_element_attr_callback,
177 btavrcp_register_notification_callback,
178 btavrcp_volume_change_callback,
179 btavrcp_passthrough_command_callback,
180 };
181
classInitNative(JNIEnv * env,jclass clazz)182 static void classInitNative(JNIEnv* env, jclass clazz) {
183 method_getRcFeatures =
184 env->GetMethodID(clazz, "getRcFeatures", "([BI)V");
185 method_getPlayStatus =
186 env->GetMethodID(clazz, "getPlayStatus", "()V");
187
188 method_getElementAttr =
189 env->GetMethodID(clazz, "getElementAttr", "(B[I)V");
190
191 method_registerNotification =
192 env->GetMethodID(clazz, "registerNotification", "(II)V");
193
194 method_volumeChangeCallback =
195 env->GetMethodID(clazz, "volumeChangeCallback", "(II)V");
196
197 method_handlePassthroughCmd =
198 env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V");
199
200 ALOGI("%s: succeeds", __FUNCTION__);
201 }
202
initNative(JNIEnv * env,jobject object)203 static void initNative(JNIEnv *env, jobject object) {
204 const bt_interface_t* btInf;
205 bt_status_t status;
206
207 if ( (btInf = getBluetoothInterface()) == NULL) {
208 ALOGE("Bluetooth module is not loaded");
209 return;
210 }
211
212 if (sBluetoothAvrcpInterface !=NULL) {
213 ALOGW("Cleaning up Avrcp Interface before initializing...");
214 sBluetoothAvrcpInterface->cleanup();
215 sBluetoothAvrcpInterface = NULL;
216 }
217
218 if (mCallbacksObj != NULL) {
219 ALOGW("Cleaning up Avrcp callback object");
220 env->DeleteGlobalRef(mCallbacksObj);
221 mCallbacksObj = NULL;
222 }
223
224 if ( (sBluetoothAvrcpInterface = (btrc_interface_t *)
225 btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) {
226 ALOGE("Failed to get Bluetooth Avrcp Interface");
227 return;
228 }
229
230 if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) !=
231 BT_STATUS_SUCCESS) {
232 ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
233 sBluetoothAvrcpInterface = NULL;
234 return;
235 }
236
237 mCallbacksObj = env->NewGlobalRef(object);
238 }
239
cleanupNative(JNIEnv * env,jobject object)240 static void cleanupNative(JNIEnv *env, jobject object) {
241 const bt_interface_t* btInf;
242
243 if ( (btInf = getBluetoothInterface()) == NULL) {
244 ALOGE("Bluetooth module is not loaded");
245 return;
246 }
247
248 if (sBluetoothAvrcpInterface !=NULL) {
249 sBluetoothAvrcpInterface->cleanup();
250 sBluetoothAvrcpInterface = NULL;
251 }
252
253 if (mCallbacksObj != NULL) {
254 env->DeleteGlobalRef(mCallbacksObj);
255 mCallbacksObj = NULL;
256 }
257 }
258
getPlayStatusRspNative(JNIEnv * env,jobject object,jint playStatus,jint songLen,jint songPos)259 static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus,
260 jint songLen, jint songPos) {
261 bt_status_t status;
262
263 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
264 if (!sBluetoothAvrcpInterface) return JNI_FALSE;
265
266 if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus,
267 songLen, songPos)) != BT_STATUS_SUCCESS) {
268 ALOGE("Failed get_play_status_rsp, status: %d", status);
269 }
270
271 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
272 }
273
getElementAttrRspNative(JNIEnv * env,jobject object,jbyte numAttr,jintArray attrIds,jobjectArray textArray)274 static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr,
275 jintArray attrIds, jobjectArray textArray) {
276 jint *attr;
277 bt_status_t status;
278 jstring text;
279 int i;
280 btrc_element_attr_val_t *pAttrs = NULL;
281 const char* textStr;
282
283 if (!sBluetoothAvrcpInterface) return JNI_FALSE;
284
285 if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
286 ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
287 return JNI_FALSE;
288 }
289
290 pAttrs = new btrc_element_attr_val_t[numAttr];
291 if (!pAttrs) {
292 ALOGE("get_element_attr_rsp: not have enough memeory");
293 return JNI_FALSE;
294 }
295
296 attr = env->GetIntArrayElements(attrIds, NULL);
297 if (!attr) {
298 delete[] pAttrs;
299 jniThrowIOException(env, EINVAL);
300 return JNI_FALSE;
301 }
302
303 for (i = 0; i < numAttr; ++i) {
304 text = (jstring) env->GetObjectArrayElement(textArray, i);
305 textStr = env->GetStringUTFChars(text, NULL);
306 if (!textStr) {
307 ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL");
308 env->DeleteLocalRef(text);
309 break;
310 }
311
312 pAttrs[i].attr_id = attr[i];
313 if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
314 ALOGE("get_element_attr_rsp: string length exceed maximum");
315 strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1);
316 pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0;
317 } else {
318 strcpy((char *)pAttrs[i].text, textStr);
319 }
320 env->ReleaseStringUTFChars(text, textStr);
321 env->DeleteLocalRef(text);
322 }
323
324 if (i < numAttr) {
325 delete[] pAttrs;
326 env->ReleaseIntArrayElements(attrIds, attr, 0);
327 return JNI_FALSE;
328 }
329
330 if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) !=
331 BT_STATUS_SUCCESS) {
332 ALOGE("Failed get_element_attr_rsp, status: %d", status);
333 }
334
335 delete[] pAttrs;
336 env->ReleaseIntArrayElements(attrIds, attr, 0);
337 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
338 }
339
registerNotificationRspPlayStatusNative(JNIEnv * env,jobject object,jint type,jint playStatus)340 static jboolean registerNotificationRspPlayStatusNative(JNIEnv *env, jobject object,
341 jint type, jint playStatus) {
342 bt_status_t status;
343 btrc_register_notification_t param;
344
345 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
346 if (!sBluetoothAvrcpInterface) return JNI_FALSE;
347
348 param.play_status = (btrc_play_status_t)playStatus;
349 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
350 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
351 ALOGE("Failed register_notification_rsp play status, status: %d", status);
352 }
353
354 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
355 }
356
registerNotificationRspTrackChangeNative(JNIEnv * env,jobject object,jint type,jbyteArray track)357 static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object,
358 jint type, jbyteArray track) {
359 bt_status_t status;
360 btrc_register_notification_t param;
361 jbyte *trk;
362 int i;
363
364 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
365 if (!sBluetoothAvrcpInterface) return JNI_FALSE;
366
367 trk = env->GetByteArrayElements(track, NULL);
368 if (!trk) {
369 jniThrowIOException(env, EINVAL);
370 return JNI_FALSE;
371 }
372
373 for (i = 0; i < BTRC_UID_SIZE; ++i) {
374 param.track[i] = trk[i];
375 }
376
377 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
378 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
379 ALOGE("Failed register_notification_rsp track change, status: %d", status);
380 }
381
382 env->ReleaseByteArrayElements(track, trk, 0);
383 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
384 }
385
registerNotificationRspPlayPosNative(JNIEnv * env,jobject object,jint type,jint playPos)386 static jboolean registerNotificationRspPlayPosNative(JNIEnv *env, jobject object,
387 jint type, jint playPos) {
388 bt_status_t status;
389 btrc_register_notification_t param;
390
391 if (!sBluetoothAvrcpInterface) return JNI_FALSE;
392
393 param.song_pos = (uint32_t)playPos;
394 if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
395 (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
396 ALOGE("Failed register_notification_rsp play position, status: %d", status);
397 }
398
399 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
400 }
401
setVolumeNative(JNIEnv * env,jobject object,jint volume)402 static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume) {
403 bt_status_t status;
404
405 //TODO: delete test code
406 ALOGI("%s: jint: %d, uint8_t: %u", __FUNCTION__, volume, (uint8_t) volume);
407
408 ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
409 if (!sBluetoothAvrcpInterface) return JNI_FALSE;
410
411 if ((status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume)) != BT_STATUS_SUCCESS) {
412 ALOGE("Failed set_volume, status: %d", status);
413 }
414
415 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
416 }
417
418 static JNINativeMethod sMethods[] = {
419 {"classInitNative", "()V", (void *) classInitNative},
420 {"initNative", "()V", (void *) initNative},
421 {"cleanupNative", "()V", (void *) cleanupNative},
422 {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative},
423 {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative},
424 {"registerNotificationRspPlayStatusNative", "(II)Z",
425 (void *) registerNotificationRspPlayStatusNative},
426 {"registerNotificationRspTrackChangeNative", "(I[B)Z",
427 (void *) registerNotificationRspTrackChangeNative},
428 {"registerNotificationRspPlayPosNative", "(II)Z",
429 (void *) registerNotificationRspPlayPosNative},
430 {"setVolumeNative", "(I)Z",
431 (void *) setVolumeNative},
432 };
433
register_com_android_bluetooth_avrcp(JNIEnv * env)434 int register_com_android_bluetooth_avrcp(JNIEnv* env)
435 {
436 return jniRegisterNativeMethods(env, "com/android/bluetooth/avrcp/Avrcp",
437 sMethods, NELEM(sMethods));
438 }
439
440 }
441