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