• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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