764 lines
28 KiB
C++
764 lines
28 KiB
C++
/*
|
|
* 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 <base/bind.h>
|
|
#include <base/logging.h>
|
|
#include <base/threading/thread.h>
|
|
#include <mutex>
|
|
#include <sstream>
|
|
|
|
#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<RawAddress, PlayState> 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<SongInfo> 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<MediaPlayerInfo> 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<ListItem> 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<BtrcPlayerAttr> 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<uint8_t> 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<BtrcPlayerAttr> 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<BtrcPlayerAttr> attrs,
|
|
GetAttrsTxtCallback get_attrs_txt_cb) override {
|
|
auto cb_lambda = [](GetAttrsTxtCallback cb,
|
|
std::vector<BtrcPlayerSettingText> 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<uint8_t> vals,
|
|
GetValuesTxtCallback get_vals_txt_cb) override {
|
|
auto cb_lambda = [](GetValuesTxtCallback cb,
|
|
std::vector<BtrcPlayerSettingText> 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<void (VolumeInterface::*)(const RawAddress&)>(
|
|
&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<void (VolumeInterface::*)(
|
|
const RawAddress&, VolumeChangedCb)>(
|
|
&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<Device> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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
|