You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
581 lines
21 KiB
581 lines
21 KiB
/*
|
|
* 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 "connection_handler.h"
|
|
|
|
#include <base/bind.h>
|
|
#include <base/logging.h>
|
|
#include <map>
|
|
|
|
#include "avrc_defs.h"
|
|
#include "avrcp_message_converter.h"
|
|
#include "bt_types.h"
|
|
#include "btu.h"
|
|
#include "packet/avrcp/avrcp_packet.h"
|
|
// TODO (apanicke): Remove dependency on this header once we cleanup feature
|
|
// handling.
|
|
#include "bta/include/bta_av_api.h"
|
|
#include "device/include/interop.h"
|
|
#include "osi/include/allocator.h"
|
|
#include "osi/include/properties.h"
|
|
|
|
namespace bluetooth {
|
|
namespace avrcp {
|
|
|
|
ConnectionHandler* ConnectionHandler::instance_ = nullptr;
|
|
|
|
ConnectionHandler* ConnectionHandler::Get() {
|
|
CHECK(instance_);
|
|
|
|
return instance_;
|
|
}
|
|
|
|
bool IsAbsoluteVolumeEnabled(const RawAddress* bdaddr) {
|
|
char volume_disabled[PROPERTY_VALUE_MAX] = {0};
|
|
osi_property_get("persist.bluetooth.disableabsvol", volume_disabled, "false");
|
|
if (strncmp(volume_disabled, "true", 4) == 0) {
|
|
LOG(INFO) << "Absolute volume disabled by property";
|
|
return false;
|
|
}
|
|
if (interop_match_addr(INTEROP_DISABLE_ABSOLUTE_VOLUME, bdaddr)) {
|
|
LOG(INFO) << "Absolute volume disabled by IOP table";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ConnectionHandler::Initialize(const ConnectionCallback& callback,
|
|
AvrcpInterface* avrcp, SdpInterface* sdp,
|
|
VolumeInterface* vol) {
|
|
CHECK(instance_ == nullptr);
|
|
CHECK(avrcp != nullptr);
|
|
CHECK(sdp != nullptr);
|
|
|
|
// TODO (apanicke): When transitioning to using this service, implement
|
|
// SDP Initialization for AVRCP Here.
|
|
instance_ = new ConnectionHandler();
|
|
instance_->connection_cb_ = callback;
|
|
instance_->avrc_ = avrcp;
|
|
instance_->sdp_ = sdp;
|
|
instance_->vol_ = vol;
|
|
|
|
// Set up the AVRCP acceptor connection
|
|
if (!instance_->AvrcpConnect(false, RawAddress::kAny)) {
|
|
instance_->CleanUp();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ConnectionHandler::CleanUp() {
|
|
CHECK(instance_ != nullptr);
|
|
|
|
// TODO (apanicke): Cleanup the SDP Entries here
|
|
for (auto entry = instance_->device_map_.begin();
|
|
entry != instance_->device_map_.end();) {
|
|
auto curr = entry;
|
|
entry++;
|
|
curr->second->DeviceDisconnected();
|
|
instance_->avrc_->Close(curr->first);
|
|
}
|
|
instance_->device_map_.clear();
|
|
instance_->feature_map_.clear();
|
|
|
|
instance_->weak_ptr_factory_.InvalidateWeakPtrs();
|
|
|
|
delete instance_;
|
|
instance_ = nullptr;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ConnectionHandler::InitForTesting(ConnectionHandler* handler) {
|
|
CHECK(instance_ == nullptr);
|
|
instance_ = handler;
|
|
}
|
|
|
|
bool ConnectionHandler::ConnectDevice(const RawAddress& bdaddr) {
|
|
LOG(INFO) << "Attempting to connect to device " << bdaddr;
|
|
|
|
for (const auto& pair : device_map_) {
|
|
if (bdaddr == pair.second->GetAddress()) {
|
|
LOG(WARNING) << "Already connected to device with address " << bdaddr;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
auto connection_lambda = [](ConnectionHandler* instance_,
|
|
const RawAddress& bdaddr, uint16_t status,
|
|
uint16_t version, uint16_t features) {
|
|
LOG(INFO) << __PRETTY_FUNCTION__
|
|
<< " SDP Completed features=" << loghex(features);
|
|
if (status != AVRC_SUCCESS || !(features & BTA_AV_FEAT_RCCT)) {
|
|
LOG(ERROR) << "Failed to do SDP: status=" << loghex(status)
|
|
<< " features=" << loghex(features)
|
|
<< " supports controller: " << (features & BTA_AV_FEAT_RCCT);
|
|
instance_->connection_cb_.Run(std::shared_ptr<Device>());
|
|
}
|
|
|
|
instance_->feature_map_.emplace(bdaddr, features);
|
|
instance_->AvrcpConnect(true, bdaddr);
|
|
return;
|
|
};
|
|
|
|
return SdpLookup(bdaddr, base::Bind(connection_lambda, this, bdaddr), false);
|
|
}
|
|
|
|
bool ConnectionHandler::DisconnectDevice(const RawAddress& bdaddr) {
|
|
for (auto it = device_map_.begin(); it != device_map_.end(); it++) {
|
|
if (bdaddr == it->second->GetAddress()) {
|
|
uint8_t handle = it->first;
|
|
return avrc_->Close(handle) == AVRC_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ConnectionHandler::SetBipClientStatus(const RawAddress& bdaddr,
|
|
bool connected) {
|
|
for (auto it = device_map_.begin(); it != device_map_.end(); it++) {
|
|
if (bdaddr == it->second->GetAddress()) {
|
|
it->second->SetBipClientStatus(connected);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::shared_ptr<Device>> ConnectionHandler::GetListOfDevices()
|
|
const {
|
|
std::vector<std::shared_ptr<Device>> list;
|
|
for (const auto& device : device_map_) {
|
|
list.push_back(device.second);
|
|
}
|
|
return list;
|
|
}
|
|
|
|
bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb,
|
|
bool retry) {
|
|
LOG(INFO) << __PRETTY_FUNCTION__;
|
|
|
|
tAVRC_SDP_DB_PARAMS db_params;
|
|
// TODO (apanicke): This needs to be replaced with smarter memory management.
|
|
tSDP_DISCOVERY_DB* disc_db =
|
|
(tSDP_DISCOVERY_DB*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
|
|
uint16_t attr_list[] = {ATTR_ID_SERVICE_CLASS_ID_LIST,
|
|
ATTR_ID_BT_PROFILE_DESC_LIST,
|
|
ATTR_ID_SUPPORTED_FEATURES};
|
|
|
|
db_params.db_len =
|
|
BT_DEFAULT_BUFFER_SIZE; // Some magic number found in the AVRCP code
|
|
db_params.num_attr = sizeof(attr_list) / sizeof(attr_list[0]);
|
|
db_params.p_db = disc_db;
|
|
db_params.p_attrs = attr_list;
|
|
|
|
return avrc_->FindService(UUID_SERVCLASS_AV_REMOTE_CONTROL, bdaddr,
|
|
&db_params,
|
|
base::Bind(&ConnectionHandler::SdpCb,
|
|
weak_ptr_factory_.GetWeakPtr(), bdaddr,
|
|
cb, disc_db, retry)) == AVRC_SUCCESS;
|
|
}
|
|
|
|
bool ConnectionHandler::AvrcpConnect(bool initiator, const RawAddress& bdaddr) {
|
|
LOG(INFO) << "Connect to device " << bdaddr.ToString();
|
|
|
|
tAVRC_CONN_CB open_cb;
|
|
if (initiator) {
|
|
open_cb.ctrl_cback = base::Bind(&ConnectionHandler::InitiatorControlCb,
|
|
weak_ptr_factory_.GetWeakPtr());
|
|
} else {
|
|
open_cb.ctrl_cback = base::Bind(&ConnectionHandler::AcceptorControlCb,
|
|
weak_ptr_factory_.GetWeakPtr());
|
|
}
|
|
open_cb.msg_cback =
|
|
base::Bind(&ConnectionHandler::MessageCb, weak_ptr_factory_.GetWeakPtr());
|
|
open_cb.company_id = AVRC_CO_GOOGLE;
|
|
open_cb.conn = initiator ? AVRC_CONN_INT
|
|
: AVRC_CONN_ACP; // 0 if initiator, 1 if acceptor
|
|
// TODO (apanicke): We shouldn't need RCCT to do absolute volume. The current
|
|
// AVRC_API requires it though.
|
|
open_cb.control = BTA_AV_FEAT_RCTG | BTA_AV_FEAT_RCCT | BTA_AV_FEAT_METADATA
|
|
| AVRC_CT_PASSIVE;
|
|
|
|
uint8_t handle = 0;
|
|
uint16_t status = avrc_->Open(&handle, &open_cb, bdaddr);
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": handle=" << loghex(handle)
|
|
<< " status= " << loghex(status);
|
|
return status == AVRC_SUCCESS;
|
|
}
|
|
|
|
void ConnectionHandler::InitiatorControlCb(uint8_t handle, uint8_t event,
|
|
uint16_t result,
|
|
const RawAddress* peer_addr) {
|
|
DCHECK(!connection_cb_.is_null());
|
|
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": handle=" << loghex(handle)
|
|
<< " result=" << loghex(result)
|
|
<< " addr=" << (peer_addr ? peer_addr->ToString() : "none");
|
|
|
|
switch (event) {
|
|
case AVRC_OPEN_IND_EVT: {
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Connection Opened Event";
|
|
|
|
const auto& feature_iter = feature_map_.find(*peer_addr);
|
|
if (feature_iter == feature_map_.end()) {
|
|
LOG(ERROR) << "Features do not exist even though SDP should have been "
|
|
"done first";
|
|
return;
|
|
}
|
|
|
|
bool supports_browsing = feature_iter->second & BTA_AV_FEAT_BROWSE;
|
|
|
|
if (supports_browsing) {
|
|
avrc_->OpenBrowse(handle, AVCT_INT);
|
|
}
|
|
|
|
// TODO (apanicke): Implement a system to cache SDP entries. For most
|
|
// devices SDP is completed after the device connects AVRCP so that
|
|
// information isn't very useful when trying to control our
|
|
// capabilities. For now always use AVRCP 1.6.
|
|
auto&& callback = base::Bind(&ConnectionHandler::SendMessage,
|
|
base::Unretained(this), handle);
|
|
auto&& ctrl_mtu = avrc_->GetPeerMtu(handle) - AVCT_HDR_LEN;
|
|
auto&& browse_mtu = avrc_->GetBrowseMtu(handle) - AVCT_HDR_LEN;
|
|
std::shared_ptr<Device> newDevice = std::make_shared<Device>(
|
|
*peer_addr, !supports_browsing, callback, ctrl_mtu, browse_mtu);
|
|
|
|
device_map_[handle] = newDevice;
|
|
// TODO (apanicke): Create the device with all of the interfaces it
|
|
// needs. Return the new device where the service will register the
|
|
// interfaces it needs.
|
|
connection_cb_.Run(newDevice);
|
|
|
|
if (feature_iter->second & BTA_AV_FEAT_ADV_CTRL) {
|
|
newDevice->RegisterVolumeChanged();
|
|
} else if (instance_->vol_ != nullptr) {
|
|
instance_->vol_->DeviceConnected(newDevice->GetAddress());
|
|
}
|
|
|
|
} break;
|
|
|
|
case AVRC_CLOSE_IND_EVT: {
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Connection Closed Event";
|
|
|
|
if (device_map_.find(handle) == device_map_.end()) {
|
|
LOG(WARNING)
|
|
<< "Connection Close received from device that doesn't exist";
|
|
return;
|
|
}
|
|
avrc_->Close(handle);
|
|
feature_map_.erase(device_map_[handle]->GetAddress());
|
|
device_map_[handle]->DeviceDisconnected();
|
|
device_map_.erase(handle);
|
|
} break;
|
|
|
|
case AVRC_BROWSE_OPEN_IND_EVT: {
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Browse Open Event";
|
|
// NOTE (apanicke): We don't need to explicitly handle this message
|
|
// since the AVCTP Layer will still send us browsing messages
|
|
// regardless. It would be useful to note this though for future
|
|
// compatibility issues.
|
|
if (device_map_.find(handle) == device_map_.end()) {
|
|
LOG(WARNING) << "Browse Opened received from device that doesn't exist";
|
|
return;
|
|
}
|
|
|
|
auto browse_mtu = avrc_->GetBrowseMtu(handle) - AVCT_HDR_LEN;
|
|
device_map_[handle]->SetBrowseMtu(browse_mtu);
|
|
} break;
|
|
case AVRC_BROWSE_CLOSE_IND_EVT:
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Browse Close Event";
|
|
break;
|
|
default:
|
|
LOG(ERROR) << "Unknown AVRCP Control event";
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ConnectionHandler::AcceptorControlCb(uint8_t handle, uint8_t event,
|
|
uint16_t result,
|
|
const RawAddress* peer_addr) {
|
|
DCHECK(!connection_cb_.is_null());
|
|
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": handle=" << loghex(handle)
|
|
<< " result=" << loghex(result)
|
|
<< " addr=" << (peer_addr ? peer_addr->ToString() : "none");
|
|
|
|
switch (event) {
|
|
case AVRC_OPEN_IND_EVT: {
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Connection Opened Event";
|
|
|
|
auto&& callback = base::Bind(&ConnectionHandler::SendMessage,
|
|
weak_ptr_factory_.GetWeakPtr(), handle);
|
|
auto&& ctrl_mtu = avrc_->GetPeerMtu(handle) - AVCT_HDR_LEN;
|
|
auto&& browse_mtu = avrc_->GetBrowseMtu(handle) - AVCT_HDR_LEN;
|
|
std::shared_ptr<Device> newDevice = std::make_shared<Device>(
|
|
*peer_addr, false, callback, ctrl_mtu, browse_mtu);
|
|
|
|
device_map_[handle] = newDevice;
|
|
connection_cb_.Run(newDevice);
|
|
|
|
LOG(INFO) << __PRETTY_FUNCTION__
|
|
<< ": Performing SDP on connected device. address="
|
|
<< peer_addr->ToString();
|
|
auto sdp_lambda = [](ConnectionHandler* instance_, uint8_t handle,
|
|
uint16_t status, uint16_t version,
|
|
uint16_t features) {
|
|
if (instance_->device_map_.find(handle) ==
|
|
instance_->device_map_.end()) {
|
|
LOG(WARNING) << __PRETTY_FUNCTION__
|
|
<< ": No device found for handle: " << loghex(handle);
|
|
return;
|
|
}
|
|
|
|
auto device = instance_->device_map_[handle];
|
|
instance_->feature_map_.emplace(device->GetAddress(), features);
|
|
|
|
// TODO (apanicke): Report to the VolumeInterface that a new Device is
|
|
// connected that doesn't support absolute volume.
|
|
if (features & BTA_AV_FEAT_ADV_CTRL) {
|
|
device->RegisterVolumeChanged();
|
|
} else if (instance_->vol_ != nullptr) {
|
|
instance_->vol_->DeviceConnected(device->GetAddress());
|
|
}
|
|
};
|
|
|
|
SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle), false);
|
|
|
|
avrc_->OpenBrowse(handle, AVCT_ACP);
|
|
AvrcpConnect(false, RawAddress::kAny);
|
|
} break;
|
|
|
|
case AVRC_CLOSE_IND_EVT: {
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Connection Closed Event";
|
|
|
|
if (device_map_.find(handle) == device_map_.end()) {
|
|
LOG(WARNING)
|
|
<< "Connection Close received from device that doesn't exist";
|
|
return;
|
|
}
|
|
avrc_->Close(handle);
|
|
feature_map_.erase(device_map_[handle]->GetAddress());
|
|
device_map_[handle]->DeviceDisconnected();
|
|
device_map_.erase(handle);
|
|
} break;
|
|
|
|
case AVRC_BROWSE_OPEN_IND_EVT: {
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Browse Open Event";
|
|
// NOTE (apanicke): We don't need to explicitly handle this message
|
|
// since the AVCTP Layer will still send us browsing messages
|
|
// regardless. It would be useful to note this though for future
|
|
// compatibility issues.
|
|
if (device_map_.find(handle) == device_map_.end()) {
|
|
LOG(WARNING) << "Browse Opened received from device that doesn't exist";
|
|
return;
|
|
}
|
|
|
|
auto browse_mtu = avrc_->GetBrowseMtu(handle) - AVCT_HDR_LEN;
|
|
device_map_[handle]->SetBrowseMtu(browse_mtu);
|
|
} break;
|
|
case AVRC_BROWSE_CLOSE_IND_EVT:
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Browse Close Event";
|
|
break;
|
|
default:
|
|
LOG(ERROR) << "Unknown AVRCP Control event";
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ConnectionHandler::MessageCb(uint8_t handle, uint8_t label, uint8_t opcode,
|
|
tAVRC_MSG* p_msg) {
|
|
if (device_map_.find(handle) == device_map_.end()) {
|
|
LOG(ERROR) << "Message received for unconnected device: handle="
|
|
<< loghex(handle);
|
|
return;
|
|
}
|
|
|
|
auto pkt = AvrcpMessageConverter::Parse(p_msg);
|
|
|
|
if (opcode == AVRC_OP_BROWSE) {
|
|
VLOG(4) << "Browse Message received on handle " << (unsigned int)handle;
|
|
device_map_[handle]->BrowseMessageReceived(label, BrowsePacket::Parse(pkt));
|
|
return;
|
|
}
|
|
|
|
VLOG(4) << "Message received on handle " << (unsigned int)handle;
|
|
device_map_[handle]->MessageReceived(label, Packet::Parse(pkt));
|
|
}
|
|
|
|
void ConnectionHandler::SdpCb(RawAddress bdaddr, SdpCallback cb,
|
|
tSDP_DISCOVERY_DB* disc_db, bool retry,
|
|
uint16_t status) {
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": SDP lookup callback received";
|
|
|
|
if (status == SDP_CONN_FAILED and !retry) {
|
|
LOG(WARNING) << __PRETTY_FUNCTION__ << ": SDP Failure retry again";
|
|
SdpLookup(bdaddr, cb, true);
|
|
return;
|
|
} else if (status != AVRC_SUCCESS) {
|
|
LOG(ERROR) << __PRETTY_FUNCTION__
|
|
<< ": SDP Failure: status = " << (unsigned int)status;
|
|
cb.Run(status, 0, 0);
|
|
osi_free(disc_db);
|
|
return;
|
|
}
|
|
|
|
// Check the peer features
|
|
tSDP_DISC_REC* sdp_record = nullptr;
|
|
uint16_t peer_features = 0;
|
|
uint16_t peer_avrcp_version = 0;
|
|
|
|
// TODO (apanicke): Replace this in favor of our own supported features.
|
|
sdp_record =
|
|
sdp_->FindServiceInDb(disc_db, UUID_SERVCLASS_AV_REMOTE_CONTROL, nullptr);
|
|
if (sdp_record != nullptr) {
|
|
LOG(INFO) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " supports remote control";
|
|
peer_features |= BTA_AV_FEAT_RCCT;
|
|
|
|
if ((sdp_->FindAttributeInRec(sdp_record, ATTR_ID_BT_PROFILE_DESC_LIST)) !=
|
|
NULL) {
|
|
/* get profile version (if failure, version parameter is not updated) */
|
|
sdp_->FindProfileVersionInRec(
|
|
sdp_record, UUID_SERVCLASS_AV_REMOTE_CONTROL, &peer_avrcp_version);
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " peer avrcp version=" << loghex(peer_avrcp_version);
|
|
|
|
if (peer_avrcp_version >= AVRC_REV_1_3) {
|
|
// These are the standard features, another way to check this is to
|
|
// search for CAT1 on the remote device
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " supports metadata";
|
|
peer_features |= (BTA_AV_FEAT_VENDOR | BTA_AV_FEAT_METADATA);
|
|
}
|
|
if (peer_avrcp_version >= AVRC_REV_1_4) {
|
|
/* get supported categories */
|
|
VLOG(1) << __PRETTY_FUNCTION__ << " Get Supported categories";
|
|
tSDP_DISC_ATTR* sdp_attribute =
|
|
sdp_->FindAttributeInRec(sdp_record, ATTR_ID_SUPPORTED_FEATURES);
|
|
if (sdp_attribute != NULL) {
|
|
VLOG(1) << __PRETTY_FUNCTION__
|
|
<< "Get Supported categories SDP ATTRIBUTES != null";
|
|
uint16_t categories = sdp_attribute->attr_value.v.u16;
|
|
if (categories & AVRC_SUPF_CT_CAT2) {
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " supports advanced control";
|
|
if (IsAbsoluteVolumeEnabled(&bdaddr)) {
|
|
peer_features |= (BTA_AV_FEAT_ADV_CTRL);
|
|
}
|
|
}
|
|
if (categories & AVRC_SUPF_CT_BROWSE) {
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " supports browsing";
|
|
peer_features |= (BTA_AV_FEAT_BROWSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
sdp_record = sdp_->FindServiceInDb(disc_db, UUID_SERVCLASS_AV_REM_CTRL_TARGET,
|
|
nullptr);
|
|
if (sdp_record != nullptr) {
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " supports remote control target";
|
|
|
|
uint16_t peer_avrcp_target_version = 0;
|
|
sdp_->FindProfileVersionInRec(sdp_record, UUID_SERVCLASS_AV_REMOTE_CONTROL,
|
|
&peer_avrcp_target_version);
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " peer avrcp target version="
|
|
<< loghex(peer_avrcp_target_version);
|
|
|
|
if ((sdp_->FindAttributeInRec(sdp_record, ATTR_ID_BT_PROFILE_DESC_LIST)) !=
|
|
NULL) {
|
|
if (peer_avrcp_target_version >= AVRC_REV_1_4) {
|
|
/* get supported categories */
|
|
VLOG(1) << __PRETTY_FUNCTION__ << " Get Supported categories";
|
|
tSDP_DISC_ATTR* sdp_attribute =
|
|
sdp_->FindAttributeInRec(sdp_record, ATTR_ID_SUPPORTED_FEATURES);
|
|
if (sdp_attribute != NULL) {
|
|
VLOG(1) << __PRETTY_FUNCTION__
|
|
<< "Get Supported categories SDP ATTRIBUTES != null";
|
|
uint16_t categories = sdp_attribute->attr_value.v.u16;
|
|
if (categories & AVRC_SUPF_CT_CAT2) {
|
|
VLOG(1) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString()
|
|
<< " supports advanced control";
|
|
if (IsAbsoluteVolumeEnabled(&bdaddr)) {
|
|
peer_features |= (BTA_AV_FEAT_ADV_CTRL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
osi_free(disc_db);
|
|
|
|
cb.Run(status, peer_avrcp_version, peer_features);
|
|
}
|
|
|
|
void ConnectionHandler::SendMessage(
|
|
uint8_t handle, uint8_t label, bool browse,
|
|
std::unique_ptr<::bluetooth::PacketBuilder> message) {
|
|
std::shared_ptr<::bluetooth::Packet> packet = VectorPacket::Make();
|
|
message->Serialize(packet);
|
|
|
|
uint8_t ctype = AVRC_RSP_ACCEPT;
|
|
if (!browse) {
|
|
ctype =
|
|
(uint8_t)(::bluetooth::Packet::Specialize<Packet>(packet)->GetCType());
|
|
}
|
|
|
|
DLOG(INFO) << "SendMessage to handle=" << loghex(handle);
|
|
|
|
BT_HDR* pkt = (BT_HDR*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);
|
|
|
|
pkt->offset = AVCT_MSG_OFFSET;
|
|
// TODO (apanicke): Update this constant. Currently this is a unique event
|
|
// used to tell the AVRCP API layer that the data is properly formatted and
|
|
// doesn't need to be processed. In the future, this is the only place sending
|
|
// the packet so none of these layer specific fields will be used.
|
|
pkt->event = 0xFFFF;
|
|
/* Handle for AVRCP fragment */
|
|
uint16_t op_code = (uint16_t)(::bluetooth::Packet::Specialize<Packet>(packet)->GetOpcode());
|
|
if (!browse && (op_code == (uint16_t)(Opcode::VENDOR))) {
|
|
pkt->event = op_code;
|
|
}
|
|
|
|
// TODO (apanicke): This layer specific stuff can go away once we move over
|
|
// to the new service.
|
|
pkt->layer_specific = AVCT_DATA_CTRL;
|
|
if (browse) {
|
|
pkt->layer_specific = AVCT_DATA_BROWSE;
|
|
}
|
|
|
|
pkt->len = packet->size();
|
|
uint8_t* p_data = (uint8_t*)(pkt + 1) + pkt->offset;
|
|
for (auto it = packet->begin(); it != packet->end(); it++) {
|
|
*p_data++ = *it;
|
|
}
|
|
|
|
avrc_->MsgReq(handle, label, ctype, pkt);
|
|
}
|
|
|
|
} // namespace avrcp
|
|
} // namespace bluetooth
|