• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 #include "rust/topshim/btav/btav_shim.h"
18 
19 #include <cstdio>
20 #include <map>
21 #include <memory>
22 
23 #include "base/functional/callback.h"
24 #include "btif/include/btif_av.h"
25 #include "include/hardware/avrcp/avrcp.h"
26 #include "include/hardware/bluetooth.h"
27 #include "rust/cxx.h"
28 #include "src/profiles/a2dp.rs.h"
29 #include "src/profiles/avrcp.rs.h"
30 #include "types/raw_address.h"
31 
32 namespace rusty = ::bluetooth::topshim::rust;
33 
34 namespace bluetooth::avrcp {
35 class AvrcpMediaInterfaceImpl : public MediaInterface {
36 public:
SendKeyEvent(uint8_t key,KeyState state)37   void SendKeyEvent(uint8_t key, KeyState state) {
38     rusty::avrcp_send_key_event(key, state == KeyState::PUSHED);
39   }
40 
GetSongInfo(SongInfoCallback cb)41   void GetSongInfo(SongInfoCallback cb) override { cb.Run(songInfo_); }
42 
GetPlayStatus(PlayStatusCallback cb)43   void GetPlayStatus(PlayStatusCallback cb) override { cb.Run(playStatus_); }
44 
GetNowPlayingList(NowPlayingCallback cb)45   void GetNowPlayingList(NowPlayingCallback cb) override {
46     cb.Run(currentSongId_, nowPlayingList_);
47   }
48 
GetMediaPlayerList(MediaListCallback cb)49   void GetMediaPlayerList(MediaListCallback cb) override { cb.Run(currentPlayer_, playerList_); }
50 
GetFolderItems(uint16_t player_id,std::string media_id,FolderItemsCallback folder_cb)51   void GetFolderItems([[maybe_unused]] uint16_t player_id, [[maybe_unused]] std::string media_id,
52                       [[maybe_unused]] FolderItemsCallback folder_cb) override {}
53 
GetAddressedPlayer(GetAddressedPlayerCallback cb)54   void GetAddressedPlayer(GetAddressedPlayerCallback cb) override { cb.Run(currentPlayer_); }
55 
SetBrowsedPlayer(uint16_t player_id,std::string current_path,SetBrowsedPlayerCallback browse_cb)56   void SetBrowsedPlayer([[maybe_unused]] uint16_t player_id,
57                         [[maybe_unused]] std::string current_path,
58                         [[maybe_unused]] SetBrowsedPlayerCallback browse_cb) override {}
59 
SetAddressedPlayer(uint16_t player_id,SetAddressedPlayerCallback cb)60   void SetAddressedPlayer([[maybe_unused]] uint16_t player_id,
61                           [[maybe_unused]] SetAddressedPlayerCallback cb) override {}
62 
RegisterUpdateCallback(MediaCallbacks * callback)63   void RegisterUpdateCallback(MediaCallbacks* callback) override { mediaCb_ = callback; }
64 
UnregisterUpdateCallback(MediaCallbacks * callback)65   void UnregisterUpdateCallback([[maybe_unused]] MediaCallbacks* callback) override {
66     mediaCb_ = nullptr;
67   }
68 
PlayItem(uint16_t player_id,bool now_playing,std::string media_id)69   void PlayItem([[maybe_unused]] uint16_t player_id, [[maybe_unused]] bool now_playing,
70                 [[maybe_unused]] std::string media_id) override {}
71 
SetActiveDevice(const RawAddress & addr)72   void SetActiveDevice(const RawAddress& addr) override { rusty::avrcp_set_active_device(addr); }
73 
SetPlaybackStatus(const PlayState & state)74   void SetPlaybackStatus(const PlayState& state) {
75     playStatus_.state = state;
76     if (mediaCb_) {
77       mediaCb_->SendMediaUpdate(/*track_changed*/ false, /*play_state*/ true, /*queuefalse*/ false);
78     }
79   }
80 
SetPosition(int64_t position_us)81   void SetPosition(int64_t position_us) {
82     // Unit conversion from microsecond to millisecond.
83     playStatus_.position = position_us / 1000;
84     if (mediaCb_) {
85       mediaCb_->SendMediaUpdate(/*track_changed*/ false, /*play_state*/ true, /*queuefalse*/ false);
86     }
87   }
88 
SetMetadata(const std::string & title,const std::string & artist,const std::string & album,int64_t length_us)89   void SetMetadata(const std::string& title, const std::string& artist, const std::string& album,
90                    int64_t length_us) {
91     if (title.length() || artist.length() || album.length()) {
92       songInfo_.attributes.clear();
93       // Reuse title for media_id, ideally this should be a shorter UID.
94       songInfo_.media_id = title;
95       if (title.length()) {
96         songInfo_.attributes.emplace(avrcp::AttributeEntry(avrcp::Attribute::TITLE, title));
97       }
98       if (artist.length()) {
99         songInfo_.attributes.emplace(avrcp::AttributeEntry(avrcp::Attribute::ARTIST_NAME, artist));
100       }
101       if (album.length()) {
102         songInfo_.attributes.emplace(avrcp::AttributeEntry(avrcp::Attribute::ALBUM_NAME, album));
103       }
104       // Floss's media implementation does not fully support the "Now Playing List," as Floss does
105       // not receive additional media information other than the current playing one. However, not
106       // all peripherals request metadata through the "Get Element Attributes" request. Instead,
107       // many get such information through the "Track Changed Notification." Hence, fill the
108       // playlist with one item here and have the Current Song ID always point to the only entry to
109       // support the track changed notification.
110       nowPlayingList_.clear();
111       nowPlayingList_.emplace_back(songInfo_);
112       currentSongId_ = songInfo_.media_id;
113       if (mediaCb_) {
114         mediaCb_->SendMediaUpdate(/*track_changed*/ true, /*play_state*/ false,
115                                   /*queuefalse*/ false);
116       }
117     }
118 
119     if (length_us) {
120       // Unit conversion from microsecond to millisecond.
121       playStatus_.duration = length_us / 1000;
122       if (mediaCb_) {
123         mediaCb_->SendMediaUpdate(/*track_changed*/ false, /*play_state*/ true,
124                                   /*queuefalse*/ false);
125       }
126     }
127   }
128 
AddPlayer(std::string name,bool browsing_supported)129   uint16_t AddPlayer(std::string name, bool browsing_supported) {
130     playerList_.push_back(MediaPlayerInfo{player_id_, name, browsing_supported});
131 
132     if (mediaCb_) {
133       mediaCb_->SendFolderUpdate(
134               /*available_players*/ true, /*addressed_players*/ true, /*uids_changed*/ false);
135     }
136 
137     return player_id_++;
138   }
139 
140 private:
141   MediaCallbacks* mediaCb_;
142 
143   PlayStatus playStatus_;
144   SongInfo songInfo_;
145   std::string currentSongId_;
146   std::vector<MediaPlayerInfo> playerList_;
147   std::vector<SongInfo> nowPlayingList_;
148   uint16_t currentPlayer_;
149 
150   uint16_t player_id_ = 0;
151 };
152 
153 class VolumeInterfaceImpl : public VolumeInterface {
154 public:
DeviceConnected(const RawAddress & addr)155   void DeviceConnected(const RawAddress& addr) override {
156     rusty::avrcp_device_connected(addr, /*absolute_volume_enabled=*/false);
157   }
158 
DeviceConnected(const RawAddress & addr,VolumeChangedCb cb)159   void DeviceConnected(const RawAddress& addr, VolumeChangedCb cb) override {
160     volumeCbs[addr] = std::move(cb);
161     rusty::avrcp_device_connected(addr, /*absolute_volume_enabled=*/true);
162   }
163 
DeviceDisconnected(const RawAddress & addr)164   void DeviceDisconnected(const RawAddress& addr) override {
165     volumeCbs.erase(addr);
166     rusty::avrcp_device_disconnected(addr);
167   }
168 
169   // Set TG's (Android, ChromeOS) volume.
SetVolume(int8_t volume)170   void SetVolume(int8_t volume) override {
171     if (volume < 0) {
172       return;
173     }
174 
175     rusty::avrcp_absolute_volume_update(volume);
176   }
177 
178   // Set CT's (headsets, speakers) volume.
SetDeviceVolume(const RawAddress & addr,int8_t volume)179   void SetDeviceVolume(const RawAddress& addr, int8_t volume) {
180     if (volume < 0) {
181       return;
182     }
183 
184     const auto& cb_iter = this->volumeCbs.find(addr);
185     if (cb_iter != this->volumeCbs.end()) {
186       cb_iter->second.Run(volume);
187     }
188   }
189 
190 private:
191   std::map<RawAddress, VolumeInterface::VolumeChangedCb> volumeCbs;
192 };
193 
194 }  // namespace bluetooth::avrcp
195 
196 namespace bluetooth {
197 namespace topshim {
198 namespace rust {
199 namespace internal {
200 static A2dpIntf* g_a2dpif;
201 static AvrcpIntf* g_avrcpif;
202 
to_rust_codec_config(const btav_a2dp_codec_config_t & config)203 static A2dpCodecConfig to_rust_codec_config(const btav_a2dp_codec_config_t& config) {
204   A2dpCodecConfig rconfig = {.codec_type = static_cast<uint8_t>(config.codec_type),
205                              .codec_priority = config.codec_priority,
206                              .sample_rate = static_cast<uint8_t>(config.sample_rate),
207                              .bits_per_sample = static_cast<uint8_t>(config.bits_per_sample),
208                              .channel_mode = static_cast<uint8_t>(config.channel_mode),
209                              .codec_specific_1 = config.codec_specific_1,
210                              .codec_specific_2 = config.codec_specific_2,
211                              .codec_specific_3 = config.codec_specific_3,
212                              .codec_specific_4 = config.codec_specific_4};
213   return rconfig;
214 }
215 
from_rust_codec_config(const A2dpCodecConfig & rconfig)216 static btav_a2dp_codec_config_t from_rust_codec_config(const A2dpCodecConfig& rconfig) {
217   btav_a2dp_codec_config_t config = {
218           .codec_type = static_cast<btav_a2dp_codec_index_t>(rconfig.codec_type),
219           .codec_priority = static_cast<btav_a2dp_codec_priority_t>(rconfig.codec_priority),
220           .sample_rate = static_cast<btav_a2dp_codec_sample_rate_t>(rconfig.sample_rate),
221           .bits_per_sample =
222                   static_cast<btav_a2dp_codec_bits_per_sample_t>(rconfig.bits_per_sample),
223           .channel_mode = static_cast<btav_a2dp_codec_channel_mode_t>(rconfig.channel_mode),
224           .codec_specific_1 = rconfig.codec_specific_1,
225           .codec_specific_2 = rconfig.codec_specific_2,
226           .codec_specific_3 = rconfig.codec_specific_3,
227           .codec_specific_4 = rconfig.codec_specific_4,
228   };
229   return config;
230 }
231 
to_rust_codec_config_vec(const std::vector<btav_a2dp_codec_config_t> & configs)232 static ::rust::Vec<A2dpCodecConfig> to_rust_codec_config_vec(
233         const std::vector<btav_a2dp_codec_config_t>& configs) {
234   ::rust::Vec<A2dpCodecConfig> rconfigs;
235 
236   for (btav_a2dp_codec_config_t c : configs) {
237     rconfigs.push_back(to_rust_codec_config(c));
238   }
239   return rconfigs;
240 }
241 
connection_state_cb(const RawAddress & addr,btav_connection_state_t state,const btav_error_t & error)242 static void connection_state_cb(const RawAddress& addr, btav_connection_state_t state,
243                                 const btav_error_t& error) {
244   // CAUTION: The error_msg field is a reference and could refer to a rvalue on the stack.
245   //          DO NOT make this conversion into a helper function.
246   A2dpError a2dp_error = {
247           .status = error.status,
248           .error_code = error.error_code,
249           .error_msg = error.error_msg.value_or(""),
250   };
251   rusty::connection_state_callback(addr, state, a2dp_error);
252 }
audio_state_cb(const RawAddress & addr,btav_audio_state_t state)253 static void audio_state_cb(const RawAddress& addr, btav_audio_state_t state) {
254   rusty::audio_state_callback(addr, state);
255 }
audio_config_cb(const RawAddress & addr,btav_a2dp_codec_config_t codec_config,std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities)256 static void audio_config_cb(const RawAddress& addr, btav_a2dp_codec_config_t codec_config,
257                             std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
258                             std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) {
259   A2dpCodecConfig cfg = to_rust_codec_config(codec_config);
260   ::rust::Vec<A2dpCodecConfig> lcaps = to_rust_codec_config_vec(codecs_local_capabilities);
261   ::rust::Vec<A2dpCodecConfig> scaps = to_rust_codec_config_vec(codecs_selectable_capabilities);
262   rusty::audio_config_callback(addr, cfg, lcaps, scaps);
263 }
mandatory_codec_preferred_cb(const RawAddress & addr)264 static bool mandatory_codec_preferred_cb(const RawAddress& addr) {
265   rusty::mandatory_codec_preferred_callback(addr);
266   return false;
267 }
268 
269 btav_source_callbacks_t g_callbacks = {
270         sizeof(btav_source_callbacks_t), connection_state_cb, audio_state_cb, audio_config_cb,
271         mandatory_codec_preferred_cb,
272 };
273 }  // namespace internal
274 
~A2dpIntf()275 A2dpIntf::~A2dpIntf() {
276   // TODO
277 }
278 
GetA2dpProfile(const unsigned char * btif)279 std::unique_ptr<A2dpIntf> GetA2dpProfile(const unsigned char* btif) {
280   if (internal::g_a2dpif) {
281     std::abort();
282   }
283 
284   const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif);
285 
286   auto a2dpif = std::make_unique<A2dpIntf>();
287   internal::g_a2dpif = a2dpif.get();
288   return a2dpif;
289 }
290 
init() const291 int A2dpIntf::init() const {
292   btav_a2dp_codec_config_t a2dp_config_sbc{
293           .codec_type = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC,
294           .codec_priority = BTAV_A2DP_CODEC_PRIORITY_HIGHEST,
295           // Using default settings for those untouched fields
296   };
297 
298   std::vector<btav_a2dp_codec_config_t> codec_priorities(1, a2dp_config_sbc);
299   std::vector<btav_a2dp_codec_config_t> b;
300   std::vector<btav_a2dp_codec_info_t> c;
301   return btif_av_source_init(&internal::g_callbacks, 1, codec_priorities, b, &c);
302 }
303 
connect(RawAddress addr) const304 uint32_t A2dpIntf::connect(RawAddress addr) const { return btif_av_source_connect(addr); }
disconnect(RawAddress addr) const305 uint32_t A2dpIntf::disconnect(RawAddress addr) const { return btif_av_source_disconnect(addr); }
set_silence_device(RawAddress addr,bool silent) const306 int A2dpIntf::set_silence_device(RawAddress addr, bool silent) const {
307   return btif_av_source_set_silence_device(addr, silent);
308 }
set_active_device(RawAddress addr) const309 int A2dpIntf::set_active_device(RawAddress addr) const {
310   return btif_av_source_set_active_device(addr);
311 }
config_codec(RawAddress addr,::rust::Vec<A2dpCodecConfig> codec_preferences) const312 int A2dpIntf::config_codec(RawAddress addr, ::rust::Vec<A2dpCodecConfig> codec_preferences) const {
313   std::vector<btav_a2dp_codec_config_t> prefs;
314   for (size_t i = 0; i < codec_preferences.size(); ++i) {
315     prefs.push_back(internal::from_rust_codec_config(codec_preferences[i]));
316   }
317   return btif_av_source_set_codec_config_preference(addr, prefs);
318 }
319 
cleanup() const320 void A2dpIntf::cleanup() const { btif_av_source_cleanup(); }
set_audio_config(A2dpCodecConfig rconfig) const321 bool A2dpIntf::set_audio_config(A2dpCodecConfig rconfig) const {
322   bluetooth::audio::a2dp::AudioConfig config = {
323           .sample_rate = static_cast<btav_a2dp_codec_sample_rate_t>(rconfig.sample_rate),
324           .bits_per_sample =
325                   static_cast<btav_a2dp_codec_bits_per_sample_t>(rconfig.bits_per_sample),
326           .channel_mode = static_cast<btav_a2dp_codec_channel_mode_t>(rconfig.channel_mode),
327   };
328   return bluetooth::audio::a2dp::SetAudioConfig(config);
329 }
start_audio_request() const330 bool A2dpIntf::start_audio_request() const { return bluetooth::audio::a2dp::StartRequest(); }
stop_audio_request() const331 bool A2dpIntf::stop_audio_request() const { return bluetooth::audio::a2dp::StopRequest(); }
suspend_audio_request() const332 bool A2dpIntf::suspend_audio_request() const { return bluetooth::audio::a2dp::SuspendRequest(); }
get_presentation_position() const333 RustPresentationPosition A2dpIntf::get_presentation_position() const {
334   bluetooth::audio::a2dp::PresentationPosition p =
335           bluetooth::audio::a2dp::GetPresentationPosition();
336   RustPresentationPosition rposition = {
337           .remote_delay_report_ns = p.remote_delay_report_ns,
338           .total_bytes_read = p.total_bytes_read,
339           .data_position_sec = p.data_position.tv_sec,
340           .data_position_nsec = static_cast<int32_t>(p.data_position.tv_nsec),
341   };
342   return rposition;
343 }
344 
345 // AVRCP
346 
347 static bluetooth::avrcp::AvrcpMediaInterfaceImpl mAvrcpInterface;
348 static bluetooth::avrcp::VolumeInterfaceImpl mVolumeInterface;
349 
GetAvrcpProfile(const unsigned char * btif)350 std::unique_ptr<AvrcpIntf> GetAvrcpProfile(const unsigned char* btif) {
351   if (internal::g_avrcpif) {
352     std::abort();
353   }
354 
355   const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif);
356 
357   auto avrcpif = std::make_unique<AvrcpIntf>(
358           reinterpret_cast<avrcp::ServiceInterface*>(btif_->get_avrcp_service()));
359   internal::g_avrcpif = avrcpif.get();
360   return avrcpif;
361 }
362 
~AvrcpIntf()363 AvrcpIntf::~AvrcpIntf() {}
364 
init()365 void AvrcpIntf::init() { intf_->Init(&mAvrcpInterface, &mVolumeInterface, nullptr); }
366 
cleanup()367 void AvrcpIntf::cleanup() { intf_->Cleanup(); }
368 
connect(RawAddress addr)369 uint32_t AvrcpIntf::connect(RawAddress addr) { return intf_->ConnectDevice(addr); }
disconnect(RawAddress addr)370 uint32_t AvrcpIntf::disconnect(RawAddress addr) { return intf_->DisconnectDevice(addr); }
371 
set_volume(RawAddress addr,int8_t volume)372 void AvrcpIntf::set_volume(RawAddress addr, int8_t volume) {
373   return mVolumeInterface.SetDeviceVolume(addr, volume);
374 }
375 
set_playback_status(const::rust::String & status)376 void AvrcpIntf::set_playback_status(const ::rust::String& status) {
377   avrcp::PlayState state = avrcp::PlayState::STOPPED;
378 
379   if (status == "stopped") {
380     state = avrcp::PlayState::STOPPED;
381   }
382   if (status == "playing") {
383     state = avrcp::PlayState::PLAYING;
384   }
385   if (status == "paused") {
386     state = avrcp::PlayState::PAUSED;
387   }
388 
389   mAvrcpInterface.SetPlaybackStatus(state);
390 }
391 
set_position(int64_t position)392 void AvrcpIntf::set_position(int64_t position) { mAvrcpInterface.SetPosition(position); }
393 
set_metadata(const::rust::String & title,const::rust::String & artist,const::rust::String & album,int64_t length_us)394 void AvrcpIntf::set_metadata(const ::rust::String& title, const ::rust::String& artist,
395                              const ::rust::String& album, int64_t length_us) {
396   mAvrcpInterface.SetMetadata(std::string(title), std::string(artist), std::string(album),
397                               length_us);
398 }
399 
add_player(const::rust::String & name,bool browsing_supported)400 uint16_t AvrcpIntf::add_player(const ::rust::String& name, bool browsing_supported) {
401   return mAvrcpInterface.AddPlayer(std::string(name), browsing_supported);
402 }
403 
404 }  // namespace rust
405 }  // namespace topshim
406 }  // namespace bluetooth
407