Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16dd23f4 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add the AVRCP Connection Handler" into pi-dev

parents f61687c2 84f9c4c5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ cc_library_static {
    ],
    export_include_dirs: ["./"],
    srcs: [
        "connection_handler.cc",
        "device.cc",
    ],
    static_libs: [
@@ -28,6 +29,7 @@ cc_test {
        "packages/modules/Bluetooth/system/stack/include",
    ],
    srcs: [
        "tests/avrcp_connection_handler_test.cc",
        "tests/avrcp_device_test.cc",
    ],
    static_libs: [
+106 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#pragma once

#include <iostream>
#include <vector>

#include "avrcp_packet.h"

// These classes are temporary placeholders to easily switch between BT_HDR and
// packets.
class VectorPacket : public ::bluetooth::Packet {
 public:
  using Packet::Packet;  // Inherit constructors

  static std::shared_ptr<VectorPacket> Make() {
    return std::shared_ptr<VectorPacket>(new VectorPacket());
  };

  static std::shared_ptr<VectorPacket> Make(std::vector<uint8_t> payload) {
    auto pkt = VectorPacket::Make();
    pkt->packet_start_index_ = 0;
    pkt->packet_end_index_ = payload.size();
    pkt->data_ = std::make_shared<std::vector<uint8_t>>(std::move(payload));
    return pkt;
  };

  const std::vector<uint8_t>& GetData() { return *data_; };

  virtual std::string ToString() const {
    std::stringstream ss;
    ss << "VectorPacket:" << std::endl;
    ss << "  └ Payload =";
    for (auto it = begin(); it != end(); it++) {
      ss << " " << loghex(*it);
    }
    ss << std::endl;

    return ss.str();
  };

  virtual std::pair<size_t, size_t> GetPayloadIndecies() const override {
    return std::pair<size_t, size_t>(packet_start_index_, packet_end_index_);
  }

  virtual bool IsValid() const override { return true; }
};

// TODO (apanicke): When deleting the old AVRCP Stack, remove this class and
// instead create a BT_HDR Parsing packet.
class AvrcpMessageConverter {
 public:
  static std::shared_ptr<::bluetooth::Packet> Parse(tAVRC_MSG* m) {
    std::vector<uint8_t> data;

    switch (m->hdr.opcode) {
      case AVRC_OP_VENDOR: {
        tAVRC_MSG_VENDOR* msg = (tAVRC_MSG_VENDOR*)m;
        data.push_back(m->hdr.ctype);
        data.push_back((m->hdr.subunit_type << 3) | m->hdr.subunit_id);
        data.push_back(m->hdr.opcode);
        for (int i = 2; i >= 0; i--) {
          data.push_back((uint8_t)((msg->company_id >> i * 8) & 0xff));
        }
        for (uint8_t i = 0; i < msg->vendor_len; i++) {
          data.push_back(msg->p_vendor_data[i]);
        }
      } break;
      case AVRC_OP_PASS_THRU: {
        tAVRC_MSG_PASS* msg = (tAVRC_MSG_PASS*)m;
        data.push_back(m->hdr.ctype);
        data.push_back((m->hdr.subunit_type << 3) | m->hdr.subunit_id);
        data.push_back(m->hdr.opcode);
        data.push_back((msg->state << 7) | msg->op_id);
        data.push_back(0x00);
      } break;
      case AVRC_OP_BROWSE: {
        tAVRC_MSG_BROWSE* msg = (tAVRC_MSG_BROWSE*)m;
        // The first 3 bytes are header bytes that aren't actually in AVRCP
        // packets
        for (int i = 0; i < msg->browse_len; i++) {
          data.push_back(msg->p_browse_data[i]);
        }
      } break;
      default:
        LOG(ERROR) << "Unknown opcode for AVRCP message";
        break;
    }

    return VectorPacket::Make(data);
  }
};
 No newline at end of file
+509 −0
Original line number Diff line number Diff line
/*
 * 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());
  CHECK(peer_addr != nullptr);  // FIXME: Maybe this should change? I'm not
                                // totally when this can be 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(*peer_addr);
      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());
  CHECK(peer_addr != nullptr);  // FIXME: Maybe this should change? I'm not
                                // totally when this can be 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(*peer_addr);
      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
 No newline at end of file
+159 −0

File added.

Preview size limit exceeded, changes collapsed.

+7 −3
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ namespace avrcp {

Device::Device(
    const RawAddress& bdaddr, bool avrcp13_compatibility,
    base::Callback<bool(uint8_t label, bool browse,
    base::Callback<void(uint8_t label, bool browse,
                        std::unique_ptr<::bluetooth::PacketBuilder> message)>
        send_msg_cb)
    : address_(bdaddr),
@@ -1022,8 +1022,12 @@ void Device::HandlePlayPosUpdate() {
void Device::DeviceDisconnected() {
  DEVICE_LOG(INFO) << "Device was disconnected";
  play_pos_update_cb_.Cancel();

  // TODO (apanicke): Once the interfaces are set in the Device construction,
  // remove these conditionals.
  if (volume_interface_ != nullptr)
    volume_interface_->DeviceDisconnected(GetAddress());
  a2dp_interface_->event_close(address_);
  if (a2dp_interface_ != nullptr) a2dp_interface_->event_close(address_);
}

std::ostream& operator<<(std::ostream& out, const Device& d) {
Loading