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