/* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "avrcp_service.h" #include #include #include #include #include #include "btif_av.h" #include "btif_common.h" #include "btif_dm.h" #include "device.h" #include "stack/include/btu.h" #if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) #include "mediatek/include/interop_mtk.h" #endif namespace bluetooth { namespace avrcp { // Static variables and interface definitions AvrcpService* AvrcpService::instance_ = nullptr; AvrcpService::ServiceInterfaceImpl* AvrcpService::service_interface_ = nullptr; std::map a2dp_status_map_; void do_in_avrcp_jni(const base::Closure& task) { do_in_jni_thread(FROM_HERE, task); } class A2dpInterfaceImpl : public A2dpInterface { RawAddress active_peer() override { return btif_av_source_active_peer(); } bool is_peer_in_silence_mode(const RawAddress& peer_address) override { return btif_av_is_peer_silenced(peer_address); } bool is_av_connected(const RawAddress& peer_address) override { return btif_av_is_connected(peer_address); } } a2dp_interface_; class AvrcpInterfaceImpl : public AvrcpInterface { public: uint16_t GetAvrcpVersion() { return AVRC_GetProfileVersion(); } uint16_t AddRecord(uint16_t service_uuid, const char* p_service_name, const char* p_provider_name, uint16_t categories, uint32_t sdp_handle, bool browse_supported, uint16_t profile_version, uint16_t cover_art_psm) override { return AVRC_AddRecord(service_uuid, p_service_name, p_provider_name, categories, sdp_handle, browse_supported, profile_version, cover_art_psm); } uint16_t RemoveRecord(uint32_t sdp_handle) { return AVRC_RemoveRecord(sdp_handle); } uint16_t FindService(uint16_t service_uuid, const RawAddress& bd_addr, tAVRC_SDP_DB_PARAMS* p_db, tAVRC_FIND_CBACK p_cback) override { return AVRC_FindService(service_uuid, bd_addr, p_db, p_cback); } uint16_t Open(uint8_t* p_handle, tAVRC_CONN_CB* p_ccb, const RawAddress& bd_addr) override { return AVRC_Open(p_handle, p_ccb, bd_addr); } void StartBrowseTimer(uint8_t handle) override { AVRC_StartBrowseTimer(handle); } void StopBrowseTimer(uint8_t handle) override { AVRC_StopBrowseTimer(handle); } uint16_t OpenBrowse(uint8_t handle, uint8_t conn_role) override { return AVRC_OpenBrowse(handle, conn_role); } uint16_t GetPeerMtu(uint8_t handle) override { return AVCT_GetPeerMtu(handle); } uint16_t GetBrowseMtu(uint8_t handle) override { return AVCT_GetBrowseMtu(handle); } uint16_t Close(uint8_t handle) override { return AVRC_Close(handle); } uint16_t CloseBrowse(uint8_t handle) override { return AVRC_CloseBrowse(handle); } uint16_t MsgReq(uint8_t handle, uint8_t label, uint8_t ctype, BT_HDR* p_pkt) override { return AVRC_MsgReq(handle, label, ctype, p_pkt); } } avrcp_interface_; class SdpInterfaceImpl : public SdpInterface { public: bool InitDiscoveryDb(tSDP_DISCOVERY_DB* a, uint32_t b, uint16_t c, const bluetooth::Uuid* d, uint16_t e, uint16_t* f) override { return SDP_InitDiscoveryDb(a, b, c, d, e, f); } bool ServiceSearchAttributeRequest(const RawAddress& a, tSDP_DISCOVERY_DB* b, tSDP_DISC_CMPL_CB* c) override { return SDP_ServiceSearchAttributeRequest(a, b, c); } tSDP_DISC_REC* FindServiceInDb(tSDP_DISCOVERY_DB* a, uint16_t b, t_sdp_disc_rec* c) override { return SDP_FindServiceInDb(a, b, c); } tSDP_DISC_ATTR* FindAttributeInRec(t_sdp_disc_rec* a, uint16_t b) override { return SDP_FindAttributeInRec(a, b); } bool FindProfileVersionInRec(t_sdp_disc_rec* a, uint16_t b, uint16_t* c) override { return SDP_FindProfileVersionInRec(a, b, c); } } sdp_interface_; // A wrapper class for the media callbacks that handles thread // switching/synchronization so the devices don't have to worry about it. class MediaInterfaceWrapper : public MediaInterface { public: MediaInterfaceWrapper(MediaInterface* cb) : wrapped_(cb){}; void SendKeyEvent(uint8_t key, KeyState state) override { do_in_avrcp_jni(base::Bind(&MediaInterface::SendKeyEvent, base::Unretained(wrapped_), key, state)); } void GetSongInfo(SongInfoCallback info_cb) override { auto cb_lambda = [](SongInfoCallback cb, SongInfo data) { do_in_main_thread(FROM_HERE, base::Bind(cb, data)); }; auto bound_cb = base::Bind(cb_lambda, info_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetSongInfo, base::Unretained(wrapped_), bound_cb)); } void GetPlayStatus(PlayStatusCallback status_cb) override { auto cb_lambda = [](PlayStatusCallback cb, PlayStatus status) { do_in_main_thread(FROM_HERE, base::Bind(cb, status)); }; auto bound_cb = base::Bind(cb_lambda, status_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetPlayStatus, base::Unretained(wrapped_), bound_cb)); } void GetNowPlayingList(NowPlayingCallback now_playing_cb) override { auto cb_lambda = [](NowPlayingCallback cb, std::string curr_media_id, std::vector song_list) { do_in_main_thread(FROM_HERE, base::Bind(cb, curr_media_id, std::move(song_list))); }; auto bound_cb = base::Bind(cb_lambda, now_playing_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetNowPlayingList, base::Unretained(wrapped_), bound_cb)); } void GetMediaPlayerList(MediaListCallback list_cb) override { auto cb_lambda = [](MediaListCallback cb, uint16_t curr_player, std::vector player_list) { do_in_main_thread(FROM_HERE, base::Bind(cb, curr_player, std::move(player_list))); }; auto bound_cb = base::Bind(cb_lambda, list_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetMediaPlayerList, base::Unretained(wrapped_), bound_cb)); } void GetFolderItems(uint16_t player_id, std::string media_id, FolderItemsCallback folder_cb) override { auto cb_lambda = [](FolderItemsCallback cb, std::vector item_list) { do_in_main_thread(FROM_HERE, base::Bind(cb, std::move(item_list))); }; auto bound_cb = base::Bind(cb_lambda, folder_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetFolderItems, base::Unretained(wrapped_), player_id, media_id, bound_cb)); } void SetBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCallback browse_cb) override { auto cb_lambda = [](SetBrowsedPlayerCallback cb, bool success, std::string root_id, uint32_t num_items) { do_in_main_thread(FROM_HERE, base::Bind(cb, success, root_id, num_items)); }; auto bound_cb = base::Bind(cb_lambda, browse_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::SetBrowsedPlayer, base::Unretained(wrapped_), player_id, bound_cb)); } void PlayItem(uint16_t player_id, bool now_playing, std::string media_id) override { do_in_avrcp_jni(base::Bind(&MediaInterface::PlayItem, base::Unretained(wrapped_), player_id, now_playing, media_id)); } void SetActiveDevice(const RawAddress& address) override { do_in_avrcp_jni(base::Bind(&MediaInterface::SetActiveDevice, base::Unretained(wrapped_), address)); } void RegisterUpdateCallback(MediaCallbacks* callback) override { wrapped_->RegisterUpdateCallback(callback); } void UnregisterUpdateCallback(MediaCallbacks* callback) override { wrapped_->UnregisterUpdateCallback(callback); } #if defined(MTK_AVRCP_APP_SETTINGS) && (MTK_AVRCP_APP_SETTINGS == TRUE) void GetAppSettingChange(GetSettingChangeCallback get_setting_cb) override { auto cb_lambda = [](GetSettingChangeCallback cb, BtrcPlayerSettings vals) { do_in_main_thread(FROM_HERE, base::Bind(cb, vals)); }; auto bound_cb = base::Bind(cb_lambda, get_setting_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetAppSettingChange, base::Unretained(wrapped_), bound_cb)); } void ListAppSettingAttrs(ListAttributesCallback list_attributes_cb) override { auto cb_lambda = [](ListAttributesCallback cb, std::vector attrs) { do_in_main_thread(FROM_HERE, base::Bind(cb, std::move(attrs))); }; auto bound_cb = base::Bind(cb_lambda, list_attributes_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::ListAppSettingAttrs, base::Unretained(wrapped_), bound_cb)); } void ListAppSettingValues(uint8_t attr_id, ListValuesCallback list_values_cb) override { auto cb_lambda = [](ListValuesCallback cb, std::vector values) { do_in_main_thread(FROM_HERE, base::Bind(cb, std::move(values))); }; auto bound_cb = base::Bind(cb_lambda, list_values_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::ListAppSettingValues, base::Unretained(wrapped_), attr_id, bound_cb)); } void GetAppSettingValues(std::vector attrs, GetValuesCallback get_values_cb) override { auto cb_lambda = [](GetValuesCallback cb, BtrcPlayerSettings vals) { do_in_main_thread(FROM_HERE, base::Bind(cb, vals)); }; auto bound_cb = base::Bind(cb_lambda, get_values_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetAppSettingValues, base::Unretained(wrapped_), std::move(attrs), bound_cb)); } void SetAppSettingValues(BtrcPlayerSettings vals, SetValuesCallback set_values_cb) override { auto cb_lambda = [](SetValuesCallback cb, BtrcStatus rsp_status) { do_in_main_thread(FROM_HERE, base::Bind(cb, rsp_status)); }; auto bound_cb = base::Bind(cb_lambda, set_values_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::SetAppSettingValues, base::Unretained(wrapped_), std::move(vals), bound_cb)); } void GetAppSettingAttrsText(std::vector attrs, GetAttrsTxtCallback get_attrs_txt_cb) override { auto cb_lambda = [](GetAttrsTxtCallback cb, std::vector attrs_txt) { do_in_main_thread(FROM_HERE, base::Bind(cb, std::move(attrs_txt))); }; auto bound_cb = base::Bind(cb_lambda, get_attrs_txt_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetAppSettingAttrsText, base::Unretained(wrapped_), std::move(attrs), bound_cb)); } void GetAppSettingValuesText(uint8_t attr_id, std::vector vals, GetValuesTxtCallback get_vals_txt_cb) override { auto cb_lambda = [](GetValuesTxtCallback cb, std::vector attrs_values_txt) { do_in_main_thread(FROM_HERE, base::Bind(cb, std::move(attrs_values_txt))); }; auto bound_cb = base::Bind(cb_lambda, get_vals_txt_cb); do_in_avrcp_jni(base::Bind(&MediaInterface::GetAppSettingValuesText, base::Unretained(wrapped_), attr_id, std::move(vals), bound_cb)); } #endif private: MediaInterface* wrapped_; }; // A wrapper class for the media callbacks that handles thread // switching/synchronization so the devices don't have to worry about it. class VolumeInterfaceWrapper : public VolumeInterface { public: VolumeInterfaceWrapper(VolumeInterface* interface) : wrapped_(interface){}; void DeviceConnected(const RawAddress& bdaddr) override { do_in_avrcp_jni( base::Bind(static_cast( &VolumeInterface::DeviceConnected), base::Unretained(wrapped_), bdaddr)); } void DeviceConnected(const RawAddress& bdaddr, VolumeChangedCb cb) override { auto cb_lambda = [](VolumeChangedCb cb, int8_t volume) { do_in_main_thread(FROM_HERE, base::Bind(cb, volume)); }; auto bound_cb = base::Bind(cb_lambda, cb); do_in_avrcp_jni(base::Bind(static_cast( &VolumeInterface::DeviceConnected), base::Unretained(wrapped_), bdaddr, bound_cb)); } void DeviceDisconnected(const RawAddress& bdaddr) override { const auto& a2dp_dev = a2dp_status_map_.find(bdaddr); if (a2dp_dev != a2dp_status_map_.end()) a2dp_status_map_.erase(bdaddr); do_in_avrcp_jni(base::Bind(&VolumeInterface::DeviceDisconnected, base::Unretained(wrapped_), bdaddr)); } void SetVolume(int8_t volume) override { do_in_avrcp_jni(base::Bind(&VolumeInterface::SetVolume, base::Unretained(wrapped_), volume)); } private: VolumeInterface* wrapped_; }; void AvrcpService::Init(MediaInterface* media_interface, VolumeInterface* volume_interface) { LOG(INFO) << "AVRCP Target Service started"; profile_version = avrcp_interface_.GetAvrcpVersion(); uint16_t supported_features = GetSupportedFeatures(profile_version); sdp_record_handle = SDP_CreateRecord(); avrcp_interface_.AddRecord(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target", NULL, supported_features, sdp_record_handle, true, profile_version, 0); btif_dm_add_uuid_to_eir(UUID_SERVCLASS_AV_REM_CTRL_TARGET); media_interface_ = new MediaInterfaceWrapper(media_interface); media_interface->RegisterUpdateCallback(instance_); VolumeInterfaceWrapper* wrapped_volume_interface = nullptr; if (volume_interface != nullptr) { wrapped_volume_interface = new VolumeInterfaceWrapper(volume_interface); } volume_interface_ = wrapped_volume_interface; ConnectionHandler::Initialize( base::Bind(&AvrcpService::DeviceCallback, base::Unretained(instance_)), &avrcp_interface_, &sdp_interface_, wrapped_volume_interface); connection_handler_ = ConnectionHandler::Get(); } uint16_t AvrcpService::GetSupportedFeatures(uint16_t profile_version) { switch (profile_version) { case AVRC_REV_1_6: return AVRCP_SUPF_TG_1_6; case AVRC_REV_1_5: return AVRCP_SUPF_TG_1_5; case AVRC_REV_1_4: return AVRCP_SUPF_TG_1_4; case AVRC_REV_1_3: return AVRCP_SUPF_TG_1_3; } return AVRCP_SUPF_TG_DEFAULT; } void AvrcpService::Cleanup() { LOG(INFO) << "AVRCP Target Service stopped"; a2dp_status_map_.clear(); avrcp_interface_.RemoveRecord(sdp_record_handle); btif_dm_remove_uuid_from_eir(UUID_SERVCLASS_AV_REM_CTRL_TARGET); sdp_record_handle = -1; connection_handler_->CleanUp(); connection_handler_ = nullptr; if (volume_interface_ != nullptr) { delete volume_interface_; } delete media_interface_; } void AvrcpService::RegisterBipServer(int psm) { LOG(INFO) << "AVRCP Target Service has registered a BIP OBEX server, psm=" << psm; avrcp_interface_.RemoveRecord(sdp_record_handle); uint16_t supported_features = GetSupportedFeatures(profile_version) | AVRC_SUPF_TG_PLAYER_COVER_ART; sdp_record_handle = SDP_CreateRecord(); avrcp_interface_.AddRecord(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target", NULL, supported_features, sdp_record_handle, true, profile_version, psm); } void AvrcpService::UnregisterBipServer() { LOG(INFO) << "AVRCP Target Service has unregistered a BIP OBEX server"; avrcp_interface_.RemoveRecord(sdp_record_handle); uint16_t supported_features = GetSupportedFeatures(profile_version); sdp_record_handle = SDP_CreateRecord(); avrcp_interface_.AddRecord(UUID_SERVCLASS_AV_REM_CTRL_TARGET, "AV Remote Control Target", NULL, supported_features, sdp_record_handle, true, profile_version, 0); } AvrcpService* AvrcpService::Get() { /** M: To avoid use it before avrcp service init@{ */ //CHECK(instance_); /** @} */ return instance_; } ServiceInterface* AvrcpService::GetServiceInterface() { if (service_interface_ == nullptr) { service_interface_ = new ServiceInterfaceImpl(); } return service_interface_; } void AvrcpService::ConnectDevice(const RawAddress& bdaddr) { VLOG(2) << __PRETTY_FUNCTION__ << ": address=" << bdaddr.ToString(); if (connection_handler_ == nullptr) return; connection_handler_->ConnectDevice(bdaddr); } /** M:third party apk not rsp play status, check from the audio status @{ */ void AvrcpService::MetadataChanged(const source_metadata_t* source_metadata, btav_audio_state_t state, const RawAddress& bdaddr) { PlayState last_state = audio_status_; bool a2dp_playing = false; PlayState a2dp_status = PlayState::ERROR; if (!source_metadata) { // a2dp status callback if (state != BTAV_AUDIO_STATE_STARTED) { a2dp_status = PlayState::PAUSED; } else { a2dp_status = PlayState::PLAYING; /** M:IOT carkit need track changed to refresh UI show song info after call ended or reconnect. @{ */ #if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) if (interop_mtk_match_addr_name(INTEROP_MTK_AVRCP_SEND_TRACK_WHEN_A2DP_START, &bdaddr)) { if (instance_ == nullptr || instance_->connection_handler_ == nullptr) return; for (const auto& device : instance_->connection_handler_->GetListOfDevices()) { if (bdaddr == device->GetAddress()) { do_in_main_thread( FROM_HERE, base::Bind(&Device::HandleTrackUpdate,device.get()->Get())); break; } } } #endif /** @} */ } // save the a2dp status for the device a2dp_status_map_[bdaddr] = a2dp_status; //find if all the device a2dp status is paused. for (const auto& entry : a2dp_status_map_) { if (entry.second == PlayState::PLAYING) { a2dp_playing = true; break; } } //if all devices are a2dp suspend, set to paused. if (!a2dp_playing) { VLOG(2) << "set to pause due to a2dp suspend"; audio_status_ = PlayState::PAUSED; } } else { // callback from audio stream start auto track_count = source_metadata->track_count; auto tracks = source_metadata->tracks; if (!btif_av_is_connected()) { VLOG(2) << __func__ << ":av is not connected, ignore audio status."; return; } // init audio_status_ with PAUSED if (track_count > 0) audio_status_ = PlayState::PAUSED; while (track_count) { VLOG(2) << __func__ << ": usage=" << tracks->usage << ", content_type=" << tracks->content_type << ", gain=" << tracks->gain; if (((tracks->usage == AUDIO_USAGE_MEDIA || tracks->usage == AUDIO_USAGE_GAME) || (tracks->content_type == AUDIO_CONTENT_TYPE_MUSIC || tracks->content_type == AUDIO_CONTENT_TYPE_MOVIE)) && (tracks->gain > 1e-6)) { audio_status_ = PlayState::PLAYING; break; } --track_count; ++tracks; } } VLOG(2) << __func__ << ":pre audio status = " << (int)last_state << "; new audio status = " << (int)audio_status_ << "; a2dp_playing = "<< (int)a2dp_playing; if (instance_ == nullptr || instance_->connection_handler_ == nullptr) return; if (last_state != audio_status_ || !source_metadata) { for (const auto& device : instance_->connection_handler_->GetListOfDevices()) { do_in_main_thread( FROM_HERE, base::Bind(&Device::HandlePlayStatusUpdate,device.get()->Get())); } } } /* }@ */ PlayState AvrcpService::GetA2dpState(const RawAddress& bdaddr) { const auto& a2dp_dev = a2dp_status_map_.find(bdaddr); if (a2dp_dev == a2dp_status_map_.end()) { return PlayState::ERROR; } else return a2dp_dev->second; } void AvrcpService::DisconnectDevice(const RawAddress& bdaddr) { VLOG(2) << __PRETTY_FUNCTION__ << ": address=" << bdaddr.ToString(); if (connection_handler_ == nullptr) return; connection_handler_->DisconnectDevice(bdaddr); } void AvrcpService::SetBipClientStatus(const RawAddress& bdaddr, bool connected) { VLOG(2) << __PRETTY_FUNCTION__ << ": address=" << bdaddr.ToString() << ", connected=" << connected; if (connection_handler_ == nullptr) return; connection_handler_->SetBipClientStatus(bdaddr, connected); } void AvrcpService::SendMediaUpdate(bool track_changed, bool play_state, bool queue) { LOG(INFO) << __PRETTY_FUNCTION__ << " track_changed=" << track_changed << " : " << " play_state=" << play_state << " : " << " queue=" << queue; if (instance_ == nullptr || instance_->connection_handler_ == nullptr) return; // This function may be called on any thread, we need to make sure that the // device update happens on the main thread. for (const auto& device : instance_->connection_handler_->GetListOfDevices()) { do_in_main_thread(FROM_HERE, base::Bind(&Device::SendMediaUpdate, device.get()->Get(), track_changed, play_state, queue)); } } void AvrcpService::SendFolderUpdate(bool available_players, bool addressed_players, bool uids) { LOG(INFO) << __PRETTY_FUNCTION__ << " available_players=" << available_players << " : " << " addressed_players=" << addressed_players << " : " << " uids=" << uids; if (instance_ == nullptr || instance_->connection_handler_ == nullptr) return; // Ensure that the update is posted to the correct thread for (const auto& device : instance_->connection_handler_->GetListOfDevices()) { do_in_main_thread(FROM_HERE, base::Bind(&Device::SendFolderUpdate, device.get()->Get(), available_players, addressed_players, uids)); } } #if defined(MTK_AVRCP_APP_SETTINGS) && (MTK_AVRCP_APP_SETTINGS == TRUE) void AvrcpService::SendAppSettingUpdate(bool setting_changed) { LOG(INFO) << __PRETTY_FUNCTION__; if (instance_ == nullptr || instance_->connection_handler_ == nullptr) return; // Ensure that the update is posted to the correct thread for (const auto& device : instance_->connection_handler_->GetListOfDevices()) { do_in_main_thread(FROM_HERE, base::Bind(&Device::SendSettingChange, device.get()->Get(), setting_changed)); } } #endif // Send out the track changed info to update the playback state for each device void AvrcpService::SendActiveDeviceChanged(const RawAddress& address) { SendMediaUpdate(false, true, false); } void AvrcpService::DeviceCallback(std::shared_ptr new_device) { if (new_device == nullptr) return; // TODO (apanicke): Pass the interfaces into the connection handler // so that the devices can be created with any interfaces they need. new_device->RegisterInterfaces(media_interface_, &a2dp_interface_, volume_interface_); } #if defined(MTK_A2DP_SRC_SINK_BOTH) && (MTK_A2DP_SRC_SINK_BOTH == TRUE) void AvrcpService::RegisterVolChanged(const RawAddress& bdaddr) { connection_handler_->RegisterVolChanged(bdaddr); } #endif // Service Interface void AvrcpService::ServiceInterfaceImpl::Init( MediaInterface* media_interface, VolumeInterface* volume_interface) { std::lock_guard lock(service_interface_lock_); // TODO: This function should block until the service is completely up so // that its possible to call Get() on the service immediately after calling // init without issues. CHECK(instance_ == nullptr); instance_ = new AvrcpService(); do_in_main_thread(FROM_HERE, base::Bind(&AvrcpService::Init, base::Unretained(instance_), media_interface, volume_interface)); } void AvrcpService::ServiceInterfaceImpl::RegisterBipServer(int psm) { std::lock_guard lock(service_interface_lock_); CHECK(instance_ != nullptr); do_in_main_thread(FROM_HERE, base::Bind(&AvrcpService::RegisterBipServer, base::Unretained(instance_), psm)); } void AvrcpService::ServiceInterfaceImpl::UnregisterBipServer() { std::lock_guard lock(service_interface_lock_); CHECK(instance_ != nullptr); do_in_main_thread(FROM_HERE, base::Bind(&AvrcpService::UnregisterBipServer, base::Unretained(instance_))); } bool AvrcpService::ServiceInterfaceImpl::ConnectDevice( const RawAddress& bdaddr) { std::lock_guard lock(service_interface_lock_); CHECK(instance_ != nullptr); do_in_main_thread(FROM_HERE, base::Bind(&AvrcpService::ConnectDevice, base::Unretained(instance_), bdaddr)); return true; } bool AvrcpService::ServiceInterfaceImpl::DisconnectDevice( const RawAddress& bdaddr) { std::lock_guard lock(service_interface_lock_); CHECK(instance_ != nullptr); do_in_main_thread(FROM_HERE, base::Bind(&AvrcpService::DisconnectDevice, base::Unretained(instance_), bdaddr)); return true; } void AvrcpService::ServiceInterfaceImpl::SetBipClientStatus( const RawAddress& bdaddr, bool connected) { std::lock_guard lock(service_interface_lock_); CHECK(instance_ != nullptr); do_in_main_thread(FROM_HERE, base::Bind(&AvrcpService::SetBipClientStatus, base::Unretained(instance_), bdaddr, connected)); } bool AvrcpService::ServiceInterfaceImpl::Cleanup() { std::lock_guard lock(service_interface_lock_); if (instance_ == nullptr) return false; do_in_main_thread(FROM_HERE, base::Bind(&AvrcpService::Cleanup, base::Owned(instance_))); // Setting instance to nullptr here is fine since it will be deleted on the // other thread. instance_ = nullptr; return true; } void AvrcpService::DebugDump(int fd) { if (instance_ == nullptr || instance_->connection_handler_ == nullptr) { dprintf(fd, "\nAVRCP Target Service not started\n"); return; } auto device_list = instance_->connection_handler_->GetListOfDevices(); dprintf(fd, "\nAVRCP Target Native Service: %zu devices\n", device_list.size()); std::stringstream stream; { ScopedIndent indent(stream); for (const auto& device : device_list) { stream << *device << std::endl; } } dprintf(fd, "%s", stream.str().c_str()); } } // namespace avrcp } // namespace bluetooth