1 /*
2 * Copyright (C) 2018 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 "AvrcpTargetJni"
18
19 #include <base/bind.h>
20 #include <base/callback.h>
21 #include <map>
22 #include <mutex>
23 #include <shared_mutex>
24 #include <vector>
25
26 #include "avrcp.h"
27 #include "com_android_bluetooth.h"
28 #include "utils/Log.h"
29
30 using namespace bluetooth::avrcp;
31
32 namespace android {
33
34 // Static Variables
35 static MediaCallbacks* mServiceCallbacks;
36 static ServiceInterface* sServiceInterface;
37 static jobject mJavaInterface;
38 static std::shared_timed_mutex interface_mutex;
39 static std::shared_timed_mutex callbacks_mutex;
40
41 // Forward Declarations
42 static void sendMediaKeyEvent(int, KeyState);
43 static std::string getCurrentMediaId();
44 static SongInfo getSongInfo();
45 static PlayStatus getCurrentPlayStatus();
46 static std::vector<SongInfo> getNowPlayingList();
47 static uint16_t getCurrentPlayerId();
48 static std::vector<MediaPlayerInfo> getMediaPlayerList();
49 using SetBrowsedPlayerCb = MediaInterface::SetBrowsedPlayerCallback;
50 static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb);
51 using GetFolderItemsCb = MediaInterface::FolderItemsCallback;
52 static void getFolderItems(uint16_t player_id, std::string media_id,
53 GetFolderItemsCb cb);
54 static void playItem(uint16_t player_id, bool now_playing,
55 std::string media_id);
56 static void setActiveDevice(const RawAddress& address);
57
58 static void volumeDeviceConnected(const RawAddress& address);
59 static void volumeDeviceConnected(
60 const RawAddress& address,
61 ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb);
62 static void volumeDeviceDisconnected(const RawAddress& address);
63 static void setVolume(int8_t volume);
64
65 // Local Variables
66 // TODO (apanicke): Use a map here to store the callback in order to
67 // support multi-browsing
68 SetBrowsedPlayerCb set_browsed_player_cb;
69 using map_entry = std::pair<std::string, GetFolderItemsCb>;
70 std::map<std::string, GetFolderItemsCb> get_folder_items_cb_map;
71 std::map<RawAddress, ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb>
72 volumeCallbackMap;
73
74 // TODO (apanicke): In the future, this interface should guarantee that
75 // all calls happen on the JNI Thread. Right now this is very difficult
76 // as it is hard to get a handle on the JNI thread from here.
77 class AvrcpMediaInterfaceImpl : public MediaInterface {
78 public:
SendKeyEvent(uint8_t key,KeyState state)79 void SendKeyEvent(uint8_t key, KeyState state) {
80 sendMediaKeyEvent(key, state);
81 }
82
GetSongInfo(SongInfoCallback cb)83 void GetSongInfo(SongInfoCallback cb) override {
84 auto info = getSongInfo();
85 cb.Run(info);
86 }
87
GetPlayStatus(PlayStatusCallback cb)88 void GetPlayStatus(PlayStatusCallback cb) override {
89 auto status = getCurrentPlayStatus();
90 cb.Run(status);
91 }
92
GetNowPlayingList(NowPlayingCallback cb)93 void GetNowPlayingList(NowPlayingCallback cb) override {
94 auto curr_song_id = getCurrentMediaId();
95 auto now_playing_list = getNowPlayingList();
96 cb.Run(curr_song_id, std::move(now_playing_list));
97 }
98
GetMediaPlayerList(MediaListCallback cb)99 void GetMediaPlayerList(MediaListCallback cb) override {
100 uint16_t current_player = getCurrentPlayerId();
101 auto player_list = getMediaPlayerList();
102 cb.Run(current_player, std::move(player_list));
103 }
104
GetFolderItems(uint16_t player_id,std::string media_id,FolderItemsCallback folder_cb)105 void GetFolderItems(uint16_t player_id, std::string media_id,
106 FolderItemsCallback folder_cb) override {
107 getFolderItems(player_id, media_id, folder_cb);
108 }
109
SetBrowsedPlayer(uint16_t player_id,SetBrowsedPlayerCallback browse_cb)110 void SetBrowsedPlayer(uint16_t player_id,
111 SetBrowsedPlayerCallback browse_cb) override {
112 setBrowsedPlayer(player_id, browse_cb);
113 }
114
RegisterUpdateCallback(MediaCallbacks * callback)115 void RegisterUpdateCallback(MediaCallbacks* callback) override {
116 // TODO (apanicke): Allow multiple registrations in the future
117 mServiceCallbacks = callback;
118 }
119
UnregisterUpdateCallback(MediaCallbacks * callback)120 void UnregisterUpdateCallback(MediaCallbacks* callback) override {
121 mServiceCallbacks = nullptr;
122 }
123
PlayItem(uint16_t player_id,bool now_playing,std::string media_id)124 void PlayItem(uint16_t player_id, bool now_playing,
125 std::string media_id) override {
126 playItem(player_id, now_playing, media_id);
127 }
128
SetActiveDevice(const RawAddress & address)129 void SetActiveDevice(const RawAddress& address) override {
130 setActiveDevice(address);
131 }
132 };
133 static AvrcpMediaInterfaceImpl mAvrcpInterface;
134
135 class VolumeInterfaceImpl : public VolumeInterface {
136 public:
DeviceConnected(const RawAddress & bdaddr)137 void DeviceConnected(const RawAddress& bdaddr) override {
138 volumeDeviceConnected(bdaddr);
139 }
140
DeviceConnected(const RawAddress & bdaddr,VolumeChangedCb cb)141 void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) override {
142 volumeDeviceConnected(bdaddr, cb);
143 }
144
DeviceDisconnected(const RawAddress & bdaddr)145 void DeviceDisconnected(const RawAddress& bdaddr) override {
146 volumeDeviceDisconnected(bdaddr);
147 }
148
SetVolume(int8_t volume)149 void SetVolume(int8_t volume) override { setVolume(volume); }
150 };
151 static VolumeInterfaceImpl mVolumeInterface;
152
153 static jmethodID method_getCurrentSongInfo;
154 static jmethodID method_getPlaybackStatus;
155 static jmethodID method_sendMediaKeyEvent;
156
157 static jmethodID method_getCurrentMediaId;
158 static jmethodID method_getNowPlayingList;
159
160 static jmethodID method_setBrowsedPlayer;
161 static jmethodID method_getCurrentPlayerId;
162 static jmethodID method_getMediaPlayerList;
163 static jmethodID method_getFolderItemsRequest;
164 static jmethodID method_playItem;
165
166 static jmethodID method_setActiveDevice;
167
168 static jmethodID method_volumeDeviceConnected;
169 static jmethodID method_volumeDeviceDisconnected;
170
171 static jmethodID method_setVolume;
172
classInitNative(JNIEnv * env,jclass clazz)173 static void classInitNative(JNIEnv* env, jclass clazz) {
174 method_getCurrentSongInfo = env->GetMethodID(
175 clazz, "getCurrentSongInfo", "()Lcom/android/bluetooth/audio_util/Metadata;");
176
177 method_getPlaybackStatus = env->GetMethodID(
178 clazz, "getPlayStatus", "()Lcom/android/bluetooth/audio_util/PlayStatus;");
179
180 method_sendMediaKeyEvent =
181 env->GetMethodID(clazz, "sendMediaKeyEvent", "(IZ)V");
182
183 method_getCurrentMediaId =
184 env->GetMethodID(clazz, "getCurrentMediaId", "()Ljava/lang/String;");
185
186 method_getNowPlayingList =
187 env->GetMethodID(clazz, "getNowPlayingList", "()Ljava/util/List;");
188
189 method_getCurrentPlayerId =
190 env->GetMethodID(clazz, "getCurrentPlayerId", "()I");
191
192 method_getMediaPlayerList =
193 env->GetMethodID(clazz, "getMediaPlayerList", "()Ljava/util/List;");
194
195 method_setBrowsedPlayer = env->GetMethodID(clazz, "setBrowsedPlayer", "(I)V");
196
197 method_getFolderItemsRequest = env->GetMethodID(
198 clazz, "getFolderItemsRequest", "(ILjava/lang/String;)V");
199
200 method_playItem =
201 env->GetMethodID(clazz, "playItem", "(IZLjava/lang/String;)V");
202
203 method_setActiveDevice =
204 env->GetMethodID(clazz, "setActiveDevice", "(Ljava/lang/String;)V");
205
206 // Volume Management functions
207 method_volumeDeviceConnected =
208 env->GetMethodID(clazz, "deviceConnected", "(Ljava/lang/String;Z)V");
209
210 method_volumeDeviceDisconnected =
211 env->GetMethodID(clazz, "deviceDisconnected", "(Ljava/lang/String;)V");
212
213 method_setVolume = env->GetMethodID(clazz, "setVolume", "(I)V");
214
215 ALOGI("%s: AvrcpTargetJni initialized!", __func__);
216 }
217
initNative(JNIEnv * env,jobject object)218 static void initNative(JNIEnv* env, jobject object) {
219 ALOGD("%s", __func__);
220 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
221 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
222 mJavaInterface = env->NewGlobalRef(object);
223
224 sServiceInterface = getBluetoothInterface()->get_avrcp_service();
225 sServiceInterface->Init(&mAvrcpInterface, &mVolumeInterface);
226 }
227
registerBipServerNative(JNIEnv * env,jobject object,jint l2cap_psm)228 static void registerBipServerNative(JNIEnv* env, jobject object,
229 jint l2cap_psm) {
230 ALOGD("%s: l2cap_psm=%d", __func__, (int)l2cap_psm);
231 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
232 if (sServiceInterface == nullptr) {
233 ALOGW("%s: Service not loaded.", __func__);
234 return;
235 }
236 sServiceInterface->RegisterBipServer((int)l2cap_psm);
237 }
238
unregisterBipServerNative(JNIEnv * env,jobject object)239 static void unregisterBipServerNative(JNIEnv* env, jobject object) {
240 ALOGD("%s", __func__);
241 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
242 if (sServiceInterface == nullptr) {
243 ALOGW("%s: Service not loaded.", __func__);
244 return;
245 }
246 sServiceInterface->UnregisterBipServer();
247 }
248
sendMediaUpdateNative(JNIEnv * env,jobject object,jboolean metadata,jboolean state,jboolean queue)249 static void sendMediaUpdateNative(JNIEnv* env, jobject object,
250 jboolean metadata, jboolean state,
251 jboolean queue) {
252 ALOGD("%s", __func__);
253 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
254 if (mServiceCallbacks == nullptr) {
255 ALOGW("%s: Service not loaded.", __func__);
256 return;
257 }
258
259 mServiceCallbacks->SendMediaUpdate(metadata == JNI_TRUE, state == JNI_TRUE,
260 queue == JNI_TRUE);
261 }
262
sendFolderUpdateNative(JNIEnv * env,jobject object,jboolean available_players,jboolean addressed_player,jboolean uids)263 static void sendFolderUpdateNative(JNIEnv* env, jobject object,
264 jboolean available_players,
265 jboolean addressed_player, jboolean uids) {
266 ALOGD("%s", __func__);
267 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
268 if (mServiceCallbacks == nullptr) {
269 ALOGW("%s: Service not loaded.", __func__);
270 return;
271 }
272
273 mServiceCallbacks->SendFolderUpdate(available_players == JNI_TRUE,
274 addressed_player == JNI_TRUE,
275 uids == JNI_TRUE);
276 }
277
cleanupNative(JNIEnv * env,jobject object)278 static void cleanupNative(JNIEnv* env, jobject object) {
279 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
280 std::unique_lock<std::shared_timed_mutex> callbacks_lock(callbacks_mutex);
281
282 get_folder_items_cb_map.clear();
283 volumeCallbackMap.clear();
284
285 sServiceInterface->Cleanup();
286 env->DeleteGlobalRef(mJavaInterface);
287 mJavaInterface = nullptr;
288 mServiceCallbacks = nullptr;
289 sServiceInterface = nullptr;
290 }
291
connectDeviceNative(JNIEnv * env,jobject object,jstring address)292 jboolean connectDeviceNative(JNIEnv* env, jobject object, jstring address) {
293 ALOGD("%s", __func__);
294 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
295 if (mServiceCallbacks == nullptr) {
296 ALOGW("%s: Service not loaded.", __func__);
297 return JNI_FALSE;
298 }
299
300 const char* tmp_addr = env->GetStringUTFChars(address, 0);
301 RawAddress bdaddr;
302 bool success = RawAddress::FromString(tmp_addr, bdaddr);
303 env->ReleaseStringUTFChars(address, tmp_addr);
304
305 if (!success) return JNI_FALSE;
306
307 return sServiceInterface->ConnectDevice(bdaddr) == true ? JNI_TRUE
308 : JNI_FALSE;
309 }
310
disconnectDeviceNative(JNIEnv * env,jobject object,jstring address)311 jboolean disconnectDeviceNative(JNIEnv* env, jobject object, jstring address) {
312 ALOGD("%s", __func__);
313 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
314 if (mServiceCallbacks == nullptr) {
315 ALOGW("%s: Service not loaded.", __func__);
316 return JNI_FALSE;
317 }
318
319 const char* tmp_addr = env->GetStringUTFChars(address, 0);
320 RawAddress bdaddr;
321 bool success = RawAddress::FromString(tmp_addr, bdaddr);
322 env->ReleaseStringUTFChars(address, tmp_addr);
323
324 if (!success) return JNI_FALSE;
325
326 return sServiceInterface->DisconnectDevice(bdaddr) == true ? JNI_TRUE
327 : JNI_FALSE;
328 }
329
sendMediaKeyEvent(int key,KeyState state)330 static void sendMediaKeyEvent(int key, KeyState state) {
331 ALOGD("%s", __func__);
332 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
333 CallbackEnv sCallbackEnv(__func__);
334 if (!sCallbackEnv.valid() || !mJavaInterface) return;
335 sCallbackEnv->CallVoidMethod(
336 mJavaInterface, method_sendMediaKeyEvent, key,
337 state == KeyState::PUSHED ? JNI_TRUE : JNI_FALSE);
338 }
339
getImageHandleFromJavaObj(JNIEnv * env,jobject image)340 static std::string getImageHandleFromJavaObj(JNIEnv* env, jobject image) {
341 std::string handle;
342
343 if (image == nullptr) return handle;
344
345 jclass class_image = env->GetObjectClass(image);
346 jmethodID method_getImageHandle =
347 env->GetMethodID(class_image, "getImageHandle", "()Ljava/lang/String;");
348 jstring imageHandle = (jstring) env->CallObjectMethod(
349 image, method_getImageHandle);
350 if (imageHandle == nullptr) {
351 return handle;
352 }
353
354 const char* value = env->GetStringUTFChars(imageHandle, nullptr);
355 handle = std::string(value);
356 env->ReleaseStringUTFChars(imageHandle, value);
357 env->DeleteLocalRef(imageHandle);
358 return handle;
359 }
360
getSongInfoFromJavaObj(JNIEnv * env,jobject metadata)361 static SongInfo getSongInfoFromJavaObj(JNIEnv* env, jobject metadata) {
362 SongInfo info;
363
364 if (metadata == nullptr) return info;
365
366 jclass class_metadata = env->GetObjectClass(metadata);
367 jfieldID field_mediaId =
368 env->GetFieldID(class_metadata, "mediaId", "Ljava/lang/String;");
369 jfieldID field_title =
370 env->GetFieldID(class_metadata, "title", "Ljava/lang/String;");
371 jfieldID field_artist =
372 env->GetFieldID(class_metadata, "artist", "Ljava/lang/String;");
373 jfieldID field_album =
374 env->GetFieldID(class_metadata, "album", "Ljava/lang/String;");
375 jfieldID field_trackNum =
376 env->GetFieldID(class_metadata, "trackNum", "Ljava/lang/String;");
377 jfieldID field_numTracks =
378 env->GetFieldID(class_metadata, "numTracks", "Ljava/lang/String;");
379 jfieldID field_genre =
380 env->GetFieldID(class_metadata, "genre", "Ljava/lang/String;");
381 jfieldID field_playingTime =
382 env->GetFieldID(class_metadata, "duration", "Ljava/lang/String;");
383 jfieldID field_image =
384 env->GetFieldID(class_metadata, "image", "Lcom/android/bluetooth/audio_util/Image;");
385
386 jstring jstr = (jstring)env->GetObjectField(metadata, field_mediaId);
387 if (jstr != nullptr) {
388 const char* value = env->GetStringUTFChars(jstr, nullptr);
389 info.media_id = std::string(value);
390 env->ReleaseStringUTFChars(jstr, value);
391 env->DeleteLocalRef(jstr);
392 }
393
394 jstr = (jstring)env->GetObjectField(metadata, field_title);
395 if (jstr != nullptr) {
396 const char* value = env->GetStringUTFChars(jstr, nullptr);
397 info.attributes.insert(
398 AttributeEntry(Attribute::TITLE, std::string(value)));
399 env->ReleaseStringUTFChars(jstr, value);
400 env->DeleteLocalRef(jstr);
401 }
402
403 jstr = (jstring)env->GetObjectField(metadata, field_artist);
404 if (jstr != nullptr) {
405 const char* value = env->GetStringUTFChars(jstr, nullptr);
406 info.attributes.insert(
407 AttributeEntry(Attribute::ARTIST_NAME, std::string(value)));
408 env->ReleaseStringUTFChars(jstr, value);
409 env->DeleteLocalRef(jstr);
410 }
411
412 jstr = (jstring)env->GetObjectField(metadata, field_album);
413 if (jstr != nullptr) {
414 const char* value = env->GetStringUTFChars(jstr, nullptr);
415 info.attributes.insert(
416 AttributeEntry(Attribute::ALBUM_NAME, std::string(value)));
417 env->ReleaseStringUTFChars(jstr, value);
418 env->DeleteLocalRef(jstr);
419 }
420
421 jstr = (jstring)env->GetObjectField(metadata, field_trackNum);
422 if (jstr != nullptr) {
423 const char* value = env->GetStringUTFChars(jstr, nullptr);
424 info.attributes.insert(
425 AttributeEntry(Attribute::TRACK_NUMBER, std::string(value)));
426 env->ReleaseStringUTFChars(jstr, value);
427 env->DeleteLocalRef(jstr);
428 }
429
430 jstr = (jstring)env->GetObjectField(metadata, field_numTracks);
431 if (jstr != nullptr) {
432 const char* value = env->GetStringUTFChars(jstr, nullptr);
433 info.attributes.insert(
434 AttributeEntry(Attribute::TOTAL_NUMBER_OF_TRACKS, std::string(value)));
435 env->ReleaseStringUTFChars(jstr, value);
436 env->DeleteLocalRef(jstr);
437 }
438
439 jstr = (jstring)env->GetObjectField(metadata, field_genre);
440 if (jstr != nullptr) {
441 const char* value = env->GetStringUTFChars(jstr, nullptr);
442 info.attributes.insert(
443 AttributeEntry(Attribute::GENRE, std::string(value)));
444 env->ReleaseStringUTFChars(jstr, value);
445 env->DeleteLocalRef(jstr);
446 }
447
448 jstr = (jstring)env->GetObjectField(metadata, field_playingTime);
449 if (jstr != nullptr) {
450 const char* value = env->GetStringUTFChars(jstr, nullptr);
451 info.attributes.insert(
452 AttributeEntry(Attribute::PLAYING_TIME, std::string(value)));
453 env->ReleaseStringUTFChars(jstr, value);
454 env->DeleteLocalRef(jstr);
455 }
456
457 jobject object_image = env->GetObjectField(metadata, field_image);
458 if (object_image != nullptr) {
459 std::string imageHandle = getImageHandleFromJavaObj(env, object_image);
460 if (!imageHandle.empty()) {
461 info.attributes.insert(
462 AttributeEntry(Attribute::DEFAULT_COVER_ART, imageHandle));
463 }
464 env->DeleteLocalRef(object_image);
465 }
466
467 return info;
468 }
469
getFolderInfoFromJavaObj(JNIEnv * env,jobject folder)470 static FolderInfo getFolderInfoFromJavaObj(JNIEnv* env, jobject folder) {
471 FolderInfo info;
472
473 jclass class_folder = env->GetObjectClass(folder);
474 jfieldID field_mediaId =
475 env->GetFieldID(class_folder, "mediaId", "Ljava/lang/String;");
476 jfieldID field_isPlayable = env->GetFieldID(class_folder, "isPlayable", "Z");
477 jfieldID field_name =
478 env->GetFieldID(class_folder, "title", "Ljava/lang/String;");
479
480 jstring jstr = (jstring)env->GetObjectField(folder, field_mediaId);
481 if (jstr != nullptr) {
482 const char* value = env->GetStringUTFChars(jstr, nullptr);
483 info.media_id = std::string(value);
484 env->ReleaseStringUTFChars(jstr, value);
485 env->DeleteLocalRef(jstr);
486 }
487
488 info.is_playable = env->GetBooleanField(folder, field_isPlayable) == JNI_TRUE;
489
490 jstr = (jstring)env->GetObjectField(folder, field_name);
491 if (jstr != nullptr) {
492 const char* value = env->GetStringUTFChars(jstr, nullptr);
493 info.name = std::string(value);
494 env->ReleaseStringUTFChars(jstr, value);
495 env->DeleteLocalRef(jstr);
496 }
497
498 return info;
499 }
500
getSongInfo()501 static SongInfo getSongInfo() {
502 ALOGD("%s", __func__);
503 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
504 CallbackEnv sCallbackEnv(__func__);
505 if (!sCallbackEnv.valid() || !mJavaInterface) return SongInfo();
506
507 jobject metadata =
508 sCallbackEnv->CallObjectMethod(mJavaInterface, method_getCurrentSongInfo);
509 SongInfo info = getSongInfoFromJavaObj(sCallbackEnv.get(), metadata);
510 sCallbackEnv->DeleteLocalRef(metadata);
511 return info;
512 }
513
getCurrentPlayStatus()514 static PlayStatus getCurrentPlayStatus() {
515 ALOGD("%s", __func__);
516 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
517 CallbackEnv sCallbackEnv(__func__);
518 if (!sCallbackEnv.valid() || !mJavaInterface) return PlayStatus();
519
520 PlayStatus status;
521 jobject playStatus =
522 sCallbackEnv->CallObjectMethod(mJavaInterface, method_getPlaybackStatus);
523
524 if (playStatus == nullptr) {
525 ALOGE("%s: Got a null play status", __func__);
526 return status;
527 }
528
529 jclass class_playStatus = sCallbackEnv->GetObjectClass(playStatus);
530 jfieldID field_position =
531 sCallbackEnv->GetFieldID(class_playStatus, "position", "J");
532 jfieldID field_duration =
533 sCallbackEnv->GetFieldID(class_playStatus, "duration", "J");
534 jfieldID field_state =
535 sCallbackEnv->GetFieldID(class_playStatus, "state", "B");
536
537 status.position = sCallbackEnv->GetLongField(playStatus, field_position);
538 status.duration = sCallbackEnv->GetLongField(playStatus, field_duration);
539 status.state = (PlayState)sCallbackEnv->GetByteField(playStatus, field_state);
540
541 sCallbackEnv->DeleteLocalRef(playStatus);
542
543 return status;
544 }
545
getCurrentMediaId()546 static std::string getCurrentMediaId() {
547 ALOGD("%s", __func__);
548 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
549 CallbackEnv sCallbackEnv(__func__);
550 if (!sCallbackEnv.valid() || !mJavaInterface) return "";
551
552 jstring media_id = (jstring)sCallbackEnv->CallObjectMethod(
553 mJavaInterface, method_getCurrentMediaId);
554 if (media_id == nullptr) {
555 ALOGE("%s: Got a null media ID", __func__);
556 return "";
557 }
558
559 const char* value = sCallbackEnv->GetStringUTFChars(media_id, nullptr);
560 std::string ret(value);
561 sCallbackEnv->ReleaseStringUTFChars(media_id, value);
562 sCallbackEnv->DeleteLocalRef(media_id);
563 return ret;
564 }
565
getNowPlayingList()566 static std::vector<SongInfo> getNowPlayingList() {
567 ALOGD("%s", __func__);
568 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
569 CallbackEnv sCallbackEnv(__func__);
570 if (!sCallbackEnv.valid() || !mJavaInterface) return std::vector<SongInfo>();
571
572 jobject song_list =
573 sCallbackEnv->CallObjectMethod(mJavaInterface, method_getNowPlayingList);
574 if (song_list == nullptr) {
575 ALOGE("%s: Got a null now playing list", __func__);
576 return std::vector<SongInfo>();
577 }
578
579 jclass class_list = sCallbackEnv->GetObjectClass(song_list);
580 jmethodID method_get =
581 sCallbackEnv->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;");
582 jmethodID method_size = sCallbackEnv->GetMethodID(class_list, "size", "()I");
583
584 auto size = sCallbackEnv->CallIntMethod(song_list, method_size);
585 if (size == 0) {
586 sCallbackEnv->DeleteLocalRef(song_list);
587 return std::vector<SongInfo>();
588 }
589 std::vector<SongInfo> ret;
590 for (int i = 0; i < size; i++) {
591 jobject song = sCallbackEnv->CallObjectMethod(song_list, method_get, i);
592 ret.push_back(getSongInfoFromJavaObj(sCallbackEnv.get(), song));
593 sCallbackEnv->DeleteLocalRef(song);
594 }
595
596 sCallbackEnv->DeleteLocalRef(song_list);
597
598 return ret;
599 }
600
getCurrentPlayerId()601 static uint16_t getCurrentPlayerId() {
602 ALOGD("%s", __func__);
603 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
604 CallbackEnv sCallbackEnv(__func__);
605 if (!sCallbackEnv.valid() || !mJavaInterface) return 0u;
606
607 jint id =
608 sCallbackEnv->CallIntMethod(mJavaInterface, method_getCurrentPlayerId);
609
610 return (static_cast<int>(id) & 0xFFFF);
611 }
612
getMediaPlayerList()613 static std::vector<MediaPlayerInfo> getMediaPlayerList() {
614 ALOGD("%s", __func__);
615 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
616 CallbackEnv sCallbackEnv(__func__);
617 if (!sCallbackEnv.valid() || !mJavaInterface)
618 return std::vector<MediaPlayerInfo>();
619
620 jobject player_list = (jobject)sCallbackEnv->CallObjectMethod(
621 mJavaInterface, method_getMediaPlayerList);
622
623 if (player_list == nullptr) {
624 ALOGE("%s: Got a null media player list", __func__);
625 return std::vector<MediaPlayerInfo>();
626 }
627
628 jclass class_list = sCallbackEnv->GetObjectClass(player_list);
629 jmethodID method_get =
630 sCallbackEnv->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;");
631 jmethodID method_size = sCallbackEnv->GetMethodID(class_list, "size", "()I");
632
633 jint list_size = sCallbackEnv->CallIntMethod(player_list, method_size);
634 if (list_size == 0) {
635 sCallbackEnv->DeleteLocalRef(player_list);
636 return std::vector<MediaPlayerInfo>();
637 }
638
639 jobject player_info =
640 sCallbackEnv->CallObjectMethod(player_list, method_get, 0);
641 jclass class_playerInfo = sCallbackEnv->GetObjectClass(player_info);
642 jfieldID field_playerId =
643 sCallbackEnv->GetFieldID(class_playerInfo, "id", "I");
644 jfieldID field_name =
645 sCallbackEnv->GetFieldID(class_playerInfo, "name", "Ljava/lang/String;");
646 jfieldID field_browsable =
647 sCallbackEnv->GetFieldID(class_playerInfo, "browsable", "Z");
648
649 std::vector<MediaPlayerInfo> ret_list;
650 for (jsize i = 0; i < list_size; i++) {
651 jobject player = sCallbackEnv->CallObjectMethod(player_list, method_get, i);
652
653 MediaPlayerInfo temp;
654 temp.id = sCallbackEnv->GetIntField(player, field_playerId);
655
656 jstring jstr = (jstring)sCallbackEnv->GetObjectField(player, field_name);
657 if (jstr != nullptr) {
658 const char* value = sCallbackEnv->GetStringUTFChars(jstr, nullptr);
659 temp.name = std::string(value);
660 sCallbackEnv->ReleaseStringUTFChars(jstr, value);
661 sCallbackEnv->DeleteLocalRef(jstr);
662 }
663
664 temp.browsing_supported =
665 sCallbackEnv->GetBooleanField(player, field_browsable) == JNI_TRUE
666 ? true
667 : false;
668
669 ret_list.push_back(std::move(temp));
670 sCallbackEnv->DeleteLocalRef(player);
671 }
672
673 sCallbackEnv->DeleteLocalRef(player_info);
674 sCallbackEnv->DeleteLocalRef(player_list);
675
676 return ret_list;
677 }
678
setBrowsedPlayer(uint16_t player_id,SetBrowsedPlayerCb cb)679 static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb cb) {
680 ALOGD("%s", __func__);
681 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
682 CallbackEnv sCallbackEnv(__func__);
683 if (!sCallbackEnv.valid() || !mJavaInterface) return;
684
685 set_browsed_player_cb = cb;
686 sCallbackEnv->CallVoidMethod(mJavaInterface, method_setBrowsedPlayer,
687 player_id);
688 }
689
setBrowsedPlayerResponseNative(JNIEnv * env,jobject object,jint player_id,jboolean success,jstring root_id,jint num_items)690 static void setBrowsedPlayerResponseNative(JNIEnv* env, jobject object,
691 jint player_id, jboolean success,
692 jstring root_id, jint num_items) {
693 ALOGD("%s", __func__);
694
695 std::string root;
696 if (root_id != nullptr) {
697 const char* value = env->GetStringUTFChars(root_id, nullptr);
698 root = std::string(value);
699 env->ReleaseStringUTFChars(root_id, value);
700 }
701
702 set_browsed_player_cb.Run(success == JNI_TRUE, root, num_items);
703 }
704
getFolderItemsResponseNative(JNIEnv * env,jobject object,jstring parent_id,jobject list)705 static void getFolderItemsResponseNative(JNIEnv* env, jobject object,
706 jstring parent_id, jobject list) {
707 ALOGD("%s", __func__);
708
709 std::string id;
710 if (parent_id != nullptr) {
711 const char* value = env->GetStringUTFChars(parent_id, nullptr);
712 id = std::string(value);
713 env->ReleaseStringUTFChars(parent_id, value);
714 }
715
716 // TODO (apanicke): Right now browsing will fail on a second device if two
717 // devices browse the same folder. Use a MultiMap to fix this behavior so
718 // that both callbacks can be handled with one lookup if a request comes
719 // for a folder that is already trying to be looked at.
720 if (get_folder_items_cb_map.find(id) == get_folder_items_cb_map.end()) {
721 ALOGE("Could not find response callback for the request of \"%s\"",
722 id.c_str());
723 return;
724 }
725
726 auto callback = get_folder_items_cb_map.find(id)->second;
727 get_folder_items_cb_map.erase(id);
728
729 if (list == nullptr) {
730 ALOGE("%s: Got a null get folder items response list", __func__);
731 callback.Run(std::vector<ListItem>());
732 return;
733 }
734
735 jclass class_list = env->GetObjectClass(list);
736 jmethodID method_get =
737 env->GetMethodID(class_list, "get", "(I)Ljava/lang/Object;");
738 jmethodID method_size = env->GetMethodID(class_list, "size", "()I");
739
740 jint list_size = env->CallIntMethod(list, method_size);
741 if (list_size == 0) {
742 callback.Run(std::vector<ListItem>());
743 return;
744 }
745
746 jobject list_item = env->CallObjectMethod(list, method_get, 0);
747 jclass class_listItem = env->GetObjectClass(list_item);
748 jfieldID field_isFolder = env->GetFieldID(class_listItem, "isFolder", "Z");
749 jfieldID field_folder = env->GetFieldID(
750 class_listItem, "folder", "Lcom/android/bluetooth/audio_util/Folder;");
751 jfieldID field_song = env->GetFieldID(
752 class_listItem, "song", "Lcom/android/bluetooth/audio_util/Metadata;");
753
754 std::vector<ListItem> ret_list;
755 for (jsize i = 0; i < list_size; i++) {
756 jobject item = env->CallObjectMethod(list, method_get, i);
757
758 bool is_folder = env->GetBooleanField(item, field_isFolder) == JNI_TRUE;
759
760 if (is_folder) {
761 jobject folder = env->GetObjectField(item, field_folder);
762 ListItem temp = {ListItem::FOLDER,
763 getFolderInfoFromJavaObj(env, folder),
764 SongInfo()};
765 ret_list.push_back(temp);
766 env->DeleteLocalRef(folder);
767 } else {
768 jobject song = env->GetObjectField(item, field_song);
769 ListItem temp = {ListItem::SONG, FolderInfo(),
770 getSongInfoFromJavaObj(env, song)};
771 ret_list.push_back(temp);
772 env->DeleteLocalRef(song);
773 }
774 env->DeleteLocalRef(item);
775 }
776
777 env->DeleteLocalRef(list_item);
778
779 callback.Run(std::move(ret_list));
780 }
781
getFolderItems(uint16_t player_id,std::string media_id,GetFolderItemsCb cb)782 static void getFolderItems(uint16_t player_id, std::string media_id,
783 GetFolderItemsCb cb) {
784 ALOGD("%s", __func__);
785 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
786 CallbackEnv sCallbackEnv(__func__);
787 if (!sCallbackEnv.valid() || !mJavaInterface) return;
788
789 // TODO (apanicke): Fix a potential media_id collision if two media players
790 // use the same media_id scheme or two devices browse the same content.
791 get_folder_items_cb_map.insert(map_entry(media_id, cb));
792
793 jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str());
794 sCallbackEnv->CallVoidMethod(mJavaInterface, method_getFolderItemsRequest,
795 player_id, j_media_id);
796 }
797
playItem(uint16_t player_id,bool now_playing,std::string media_id)798 static void playItem(uint16_t player_id, bool now_playing,
799 std::string media_id) {
800 ALOGD("%s", __func__);
801 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
802 CallbackEnv sCallbackEnv(__func__);
803 if (!sCallbackEnv.valid() || !mJavaInterface) return;
804
805 jstring j_media_id = sCallbackEnv->NewStringUTF(media_id.c_str());
806 sCallbackEnv->CallVoidMethod(mJavaInterface, method_playItem, player_id,
807 now_playing ? JNI_TRUE : JNI_FALSE, j_media_id);
808 }
809
setActiveDevice(const RawAddress & address)810 static void setActiveDevice(const RawAddress& address) {
811 ALOGD("%s", __func__);
812 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
813 CallbackEnv sCallbackEnv(__func__);
814 if (!sCallbackEnv.valid() || !mJavaInterface) return;
815
816 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str());
817 sCallbackEnv->CallVoidMethod(mJavaInterface, method_setActiveDevice,
818 j_bdaddr);
819 }
820
volumeDeviceConnected(const RawAddress & address)821 static void volumeDeviceConnected(const RawAddress& address) {
822 ALOGD("%s", __func__);
823 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
824 CallbackEnv sCallbackEnv(__func__);
825 if (!sCallbackEnv.valid() || !mJavaInterface) return;
826
827 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str());
828 sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected,
829 j_bdaddr, JNI_FALSE);
830 }
831
volumeDeviceConnected(const RawAddress & address,::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb)832 static void volumeDeviceConnected(
833 const RawAddress& address,
834 ::bluetooth::avrcp::VolumeInterface::VolumeChangedCb cb) {
835 ALOGD("%s", __func__);
836 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
837 CallbackEnv sCallbackEnv(__func__);
838 if (!sCallbackEnv.valid() || !mJavaInterface) return;
839
840 volumeCallbackMap.emplace(address, cb);
841
842 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str());
843 sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceConnected,
844 j_bdaddr, JNI_TRUE);
845 }
846
volumeDeviceDisconnected(const RawAddress & address)847 static void volumeDeviceDisconnected(const RawAddress& address) {
848 ALOGD("%s", __func__);
849 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
850 CallbackEnv sCallbackEnv(__func__);
851 if (!sCallbackEnv.valid() || !mJavaInterface) return;
852
853 volumeCallbackMap.erase(address);
854
855 jstring j_bdaddr = sCallbackEnv->NewStringUTF(address.ToString().c_str());
856 sCallbackEnv->CallVoidMethod(mJavaInterface, method_volumeDeviceDisconnected,
857 j_bdaddr);
858 }
859
sendVolumeChangedNative(JNIEnv * env,jobject object,jstring address,jint volume)860 static void sendVolumeChangedNative(JNIEnv* env, jobject object,
861 jstring address, jint volume) {
862 const char* tmp_addr = env->GetStringUTFChars(address, 0);
863 RawAddress bdaddr;
864 bool success = RawAddress::FromString(tmp_addr, bdaddr);
865 env->ReleaseStringUTFChars(address, tmp_addr);
866
867 if (!success) return;
868
869 ALOGD("%s", __func__);
870 if (volumeCallbackMap.find(bdaddr) != volumeCallbackMap.end()) {
871 volumeCallbackMap.find(bdaddr)->second.Run(volume & 0x7F);
872 }
873 }
874
setVolume(int8_t volume)875 static void setVolume(int8_t volume) {
876 ALOGD("%s", __func__);
877 std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
878 CallbackEnv sCallbackEnv(__func__);
879 if (!sCallbackEnv.valid() || !mJavaInterface) return;
880
881 sCallbackEnv->CallVoidMethod(mJavaInterface, method_setVolume, volume);
882 }
883
setBipClientStatusNative(JNIEnv * env,jobject object,jstring address,jboolean connected)884 static void setBipClientStatusNative(JNIEnv* env, jobject object,
885 jstring address, jboolean connected) {
886 std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
887 if (mServiceCallbacks == nullptr) {
888 ALOGW("%s: Service not loaded.", __func__);
889 return;
890 }
891
892 const char* tmp_addr = env->GetStringUTFChars(address, 0);
893 RawAddress bdaddr;
894 bool success = RawAddress::FromString(tmp_addr, bdaddr);
895 env->ReleaseStringUTFChars(address, tmp_addr);
896
897 if (!success) return;
898
899 bool status = (connected == JNI_TRUE);
900 sServiceInterface->SetBipClientStatus(bdaddr, status);
901 }
902
903 static JNINativeMethod sMethods[] = {
904 {"classInitNative", "()V", (void*)classInitNative},
905 {"initNative", "()V", (void*)initNative},
906 {"registerBipServerNative", "(I)V", (void*)registerBipServerNative},
907 {"unregisterBipServerNative", "()V", (void*)unregisterBipServerNative},
908 {"sendMediaUpdateNative", "(ZZZ)V", (void*)sendMediaUpdateNative},
909 {"sendFolderUpdateNative", "(ZZZ)V", (void*)sendFolderUpdateNative},
910 {"setBrowsedPlayerResponseNative", "(IZLjava/lang/String;I)V",
911 (void*)setBrowsedPlayerResponseNative},
912 {"getFolderItemsResponseNative", "(Ljava/lang/String;Ljava/util/List;)V",
913 (void*)getFolderItemsResponseNative},
914 {"cleanupNative", "()V", (void*)cleanupNative},
915 {"connectDeviceNative", "(Ljava/lang/String;)Z",
916 (void*)connectDeviceNative},
917 {"disconnectDeviceNative", "(Ljava/lang/String;)Z",
918 (void*)disconnectDeviceNative},
919 {"sendVolumeChangedNative", "(Ljava/lang/String;I)V",
920 (void*)sendVolumeChangedNative},
921 {"setBipClientStatusNative", "(Ljava/lang/String;Z)V",
922 (void*)setBipClientStatusNative},
923 };
924
register_com_android_bluetooth_avrcp_target(JNIEnv * env)925 int register_com_android_bluetooth_avrcp_target(JNIEnv* env) {
926 return jniRegisterNativeMethods(
927 env, "com/android/bluetooth/avrcp/AvrcpNativeInterface", sMethods,
928 NELEM(sMethods));
929 }
930
931 } // namespace android
932