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