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