• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &param)) != 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, &param)) != 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, &param)) != 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