| /* |
| * 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 "avrcp_packet.h" |
| #include "bt_types.h" |
| #include "btu.h" |
| // TODO (apanicke): Remove dependency on this header once we cleanup feature |
| // handling. |
| #include "bta/include/bta_av_api.h" |
| #include "osi/include/allocator.h" |
| |
| namespace bluetooth { |
| namespace avrcp { |
| |
| ConnectionHandler* ConnectionHandler::instance_ = nullptr; |
| |
| ConnectionHandler* ConnectionHandler::Get() { |
| CHECK(instance_); |
| |
| return instance_; |
| } |
| |
| bool ConnectionHandler::Initialize(const ConnectionCallback& callback, |
| AvrcpInterface* avrcp, SdpInterface* sdp) { |
| 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; |
| |
| // 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 (const auto& entry : instance_->device_map_) { |
| entry.second->DeviceDisconnected(); |
| instance_->avrc_->Close(entry.first); |
| } |
| instance_->device_map_.clear(); |
| instance_->feature_map_.clear(); |
| |
| 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)); |
| } |
| |
| bool ConnectionHandler::DisconnectDevice(const RawAddress& bdaddr) { |
| for (auto it = device_map_.begin(); it != device_map_.end(); it++) { |
| if (bdaddr == it->second->GetAddress()) { |
| it->second->DeviceDisconnected(); |
| uint8_t handle = it->first; |
| device_map_.erase(handle); |
| return avrc_->Close(handle) == AVRC_SUCCESS; |
| } |
| } |
| |
| return false; |
| } |
| |
| std::vector<std::shared_ptr<Device>> ConnectionHandler::GetListOfDevices() |
| const { |
| std::vector<std::shared_ptr<Device>> list; |
| for (auto device : device_map_) { |
| list.push_back(device.second); |
| } |
| return list; |
| } |
| |
| bool ConnectionHandler::SdpLookup(const RawAddress& bdaddr, SdpCallback cb) { |
| 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, base::Unretained(this), |
| bdaddr, cb, disc_db)) == 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, |
| base::Unretained(this)); |
| } else { |
| open_cb.ctrl_cback = base::Bind(&ConnectionHandler::AcceptorControlCb, |
| base::Unretained(this)); |
| } |
| open_cb.msg_cback = |
| base::Bind(&ConnectionHandler::MessageCb, base::Unretained(this)); |
| 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; |
| |
| 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. |
| std::shared_ptr<Device> newDevice = |
| std::make_shared<Device>(*peer_addr, !supports_browsing, |
| base::Bind(&ConnectionHandler::SendMessage, |
| base::Unretained(this), handle)); |
| 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(); |
| } |
| } break; |
| |
| case AVRC_CLOSE_IND_EVT: { |
| LOG(INFO) << __PRETTY_FUNCTION__ << ": Connection Closed Event"; |
| |
| if (device_map_[handle] == nullptr) { |
| LOG(WARNING) |
| << "Connection Close received from device that doesn't exist"; |
| return; |
| } |
| 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. |
| 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"; |
| |
| std::shared_ptr<Device> newDevice = |
| std::make_shared<Device>(*peer_addr, false, |
| base::Bind(&ConnectionHandler::SendMessage, |
| base::Unretained(this), handle)); |
| 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(); |
| } |
| }; |
| |
| SdpLookup(*peer_addr, base::Bind(sdp_lambda, this, handle)); |
| |
| 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_[handle] == nullptr) { |
| LOG(WARNING) |
| << "Connection Close received from device that doesn't exist"; |
| return; |
| } |
| 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. |
| 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_[handle] == nullptr) { |
| 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(const RawAddress& bdaddr, SdpCallback cb, |
| tSDP_DISCOVERY_DB* disc_db, uint16_t status) { |
| LOG(INFO) << __PRETTY_FUNCTION__ << ": SDP lookup callback received"; |
| |
| 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); |
| LOG(INFO) << __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 |
| LOG(INFO) << __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 */ |
| LOG(INFO) << __PRETTY_FUNCTION__ << " Get Supported categories"; |
| tSDP_DISC_ATTR* sdp_attribute = |
| sdp_->FindAttributeInRec(sdp_record, ATTR_ID_SUPPORTED_FEATURES); |
| if (sdp_attribute != NULL) { |
| LOG(INFO) << __PRETTY_FUNCTION__ |
| << "Get Supported categories SDP ATTRIBUTES != null"; |
| uint16_t categories = sdp_attribute->attr_value.v.u16; |
| if (categories & AVRC_SUPF_CT_CAT2) { |
| LOG(INFO) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString() |
| << " supports advanced control"; |
| peer_features |= (BTA_AV_FEAT_ADV_CTRL); |
| } |
| if (categories & AVRC_SUPF_CT_BROWSE) { |
| LOG(INFO) << __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) { |
| LOG(INFO) << __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); |
| LOG(INFO) << __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 */ |
| LOG(INFO) << __PRETTY_FUNCTION__ << " Get Supported categories"; |
| tSDP_DISC_ATTR* sdp_attribute = |
| sdp_->FindAttributeInRec(sdp_record, ATTR_ID_SUPPORTED_FEATURES); |
| if (sdp_attribute != NULL) { |
| LOG(INFO) << __PRETTY_FUNCTION__ |
| << "Get Supported categories SDP ATTRIBUTES != null"; |
| uint16_t categories = sdp_attribute->attr_value.v.u16; |
| if (categories & AVRC_SUPF_CT_CAT2) { |
| LOG(INFO) << __PRETTY_FUNCTION__ << ": Device " << bdaddr.ToString() |
| << " supports advanced control"; |
| 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; |
| |
| // 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 |