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