Implement A2DP sink and AVRCP controller binder server

Also added bonded device management because this is needed for A2DP to
work with most devices.

Bug: 32657860
Test: Run on device, modified tests pass.
Change-Id: I862e41b6594b7cec9f61094d300577574cc8bbc6
diff --git a/service/Android.bp b/service/Android.bp
index d24e0fa..42be835 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -14,11 +14,15 @@
 // Source variables
 // ========================================================
 btserviceDaemonSrc = [
+    "a2dp_sink.cc",
     "adapter.cc",
+    "avrcp_control.cc",
     "daemon.cc",
     "gatt_client.cc",
     "gatt_server.cc",
     "gatt_server_old.cc",
+    "hal/bluetooth_av_interface.cc",
+    "hal/bluetooth_avrcp_interface.cc",
     "hal/bluetooth_gatt_interface.cc",
     "hal/bluetooth_interface.cc",
     "ipc/ipc_handler.cc",
@@ -36,6 +40,8 @@
 ]
 
 btserviceBinderDaemonSrc = [
+    "ipc/binder/bluetooth_a2dp_sink_binder_server.cc",
+    "ipc/binder/bluetooth_avrcp_control_binder_server.cc",
     "ipc/binder/bluetooth_binder_server.cc",
     "ipc/binder/bluetooth_gatt_client_binder_server.cc",
     "ipc/binder/bluetooth_gatt_server_binder_server.cc",
@@ -49,8 +55,10 @@
 // Main unit test sources. These get built for host and target.
 // ========================================================
 btserviceBaseTestSrc = [
+    "hal/fake_bluetooth_av_interface.cc",
     "hal/fake_bluetooth_gatt_interface.cc",
     "hal/fake_bluetooth_interface.cc",
+    "test/a2dp_sink_unittest.cc",
     "test/adapter_unittest.cc",
     "test/advertise_data_unittest.cc",
     "test/fake_hal_util.cc",
@@ -109,6 +117,7 @@
         "libgmock",
         "liblog",
         "libbluetooth-types",
+        "libutils",
     ],
     host_supported: true,
     target: {
@@ -124,7 +133,6 @@
             ],
             shared_libs: [
                 "libbinder",
-                "libutils",
             ],
         },
         host: {
diff --git a/service/a2dp_sink.cc b/service/a2dp_sink.cc
new file mode 100644
index 0000000..4d59c7c
--- /dev/null
+++ b/service/a2dp_sink.cc
@@ -0,0 +1,154 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "service/a2dp_sink.h"
+
+#include <base/logging.h>
+#include <base/memory/ptr_util.h>
+
+#include "service/logging_helpers.h"
+
+using bluetooth::hal::BluetoothAvInterface;
+
+namespace bluetooth {
+
+// static
+const int A2dpSink::kSingletonInstanceId = 0;
+
+A2dpSink::A2dpSink(const Uuid& uuid) : app_identifier_(uuid) {
+  hal::BluetoothAvInterface::Get()->AddA2dpSinkObserver(this);
+}
+
+A2dpSink::~A2dpSink() {
+  hal::BluetoothAvInterface::Get()->RemoveA2dpSinkObserver(this);
+}
+
+const Uuid& A2dpSink::GetAppIdentifier() const { return app_identifier_; }
+
+int A2dpSink::GetInstanceId() const { return kSingletonInstanceId; }
+
+void A2dpSink::SetDelegate(Delegate* delegate) {
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+  delegate_ = delegate;
+}
+
+bool A2dpSink::Enable() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  return hal::BluetoothAvInterface::Get()->A2dpSinkEnable();
+}
+
+void A2dpSink::Disable() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  hal::BluetoothAvInterface::Get()->A2dpSinkDisable();
+}
+
+bool A2dpSink::Connect(const std::string& device_address) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  RawAddress addr;
+  if (!RawAddress::FromString(device_address, addr)) {
+    LOG(ERROR) << "Invalid device address given: " << device_address;
+    return false;
+  }
+
+  bt_status_t status =
+      hal::BluetoothAvInterface::Get()->GetA2dpSinkHALInterface()->connect(
+          addr);
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << "Failed to connect";
+    return false;
+  }
+
+  return true;
+}
+
+bool A2dpSink::Disconnect(const std::string& device_address) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  RawAddress addr;
+  if (!RawAddress::FromString(device_address, addr)) {
+    LOG(ERROR) << "Invalid device address given: " << device_address;
+    return false;
+  }
+
+  bt_status_t status =
+      hal::BluetoothAvInterface::Get()->GetA2dpSinkHALInterface()->disconnect(
+          addr);
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << "Failed to disconnect";
+    return false;
+  }
+
+  return true;
+}
+
+void A2dpSink::SetAudioFocusState(int focus_state) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  hal::BluetoothAvInterface::Get()
+      ->GetA2dpSinkHALInterface()
+      ->set_audio_focus_state(focus_state);
+}
+
+void A2dpSink::SetAudioTrackGain(float gain) {
+  std::lock_guard<std::mutex> lock(mutex_);
+  hal::BluetoothAvInterface::Get()
+      ->GetA2dpSinkHALInterface()
+      ->set_audio_track_gain(gain);
+}
+
+void A2dpSink::ConnectionStateCallback(BluetoothAvInterface* iface,
+                                       const RawAddress& bd_addr,
+                                       btav_connection_state_t state) {
+  std::string device_address = BtAddrString(&bd_addr);
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+
+  if (delegate_)
+    delegate_->OnConnectionState(device_address, static_cast<int>(state));
+}
+
+void A2dpSink::AudioStateCallback(BluetoothAvInterface* iface,
+                                  const RawAddress& bd_addr,
+                                  btav_audio_state_t state) {
+  std::string device_address = BtAddrString(&bd_addr);
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+
+  if (delegate_)
+    delegate_->OnAudioState(device_address, static_cast<int>(state));
+}
+
+void A2dpSink::AudioConfigCallback(BluetoothAvInterface* iface,
+                                   const RawAddress& bd_addr,
+                                   uint32_t sample_rate,
+                                   uint8_t channel_count) {
+  std::string device_address = BtAddrString(&bd_addr);
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+  if (delegate_)
+    delegate_->OnAudioConfig(device_address, sample_rate, channel_count);
+}
+
+// A2dpSinkFactory implementation
+// ========================================================
+A2dpSinkFactory::A2dpSinkFactory() = default;
+A2dpSinkFactory::~A2dpSinkFactory() = default;
+
+bool A2dpSinkFactory::RegisterInstance(const Uuid& uuid,
+                                       const RegisterCallback& callback) {
+  VLOG(1) << __func__ << " - Uuid: " << uuid.ToString();
+
+  auto a2dp_sink = base::WrapUnique(new A2dpSink(uuid));
+  callback(BLE_STATUS_SUCCESS, uuid, std::move(a2dp_sink));
+  return true;
+}
+
+}  // namespace bluetooth
diff --git a/service/a2dp_sink.h b/service/a2dp_sink.h
new file mode 100644
index 0000000..7a62805
--- /dev/null
+++ b/service/a2dp_sink.h
@@ -0,0 +1,103 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <atomic>
+#include <mutex>
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+#include "service/bluetooth_instance.h"
+#include "service/hal/bluetooth_av_interface.h"
+
+namespace bluetooth {
+
+class A2dpSink : public BluetoothInstance,
+                 private hal::BluetoothAvInterface::A2dpSinkObserver {
+ public:
+  // We only allow one instance of this object at a time.
+  static const int kSingletonInstanceId;
+
+  class Delegate {
+   public:
+    virtual void OnConnectionState(const std::string& device_address,
+                                   int state) = 0;
+    virtual void OnAudioState(const std::string& device_address, int state) = 0;
+    virtual void OnAudioConfig(const std::string& device_address,
+                               uint32_t sample_rate, uint8_t channel_count) = 0;
+
+   protected:
+    virtual ~Delegate() = default;
+  };
+
+  ~A2dpSink() override;
+
+  void SetDelegate(Delegate* delegate);
+
+  // BluetoothInstance implementation:
+  const Uuid& GetAppIdentifier() const override;
+  int GetInstanceId() const override;
+
+  bool Enable();
+  void Disable();
+  bool Connect(const std::string& device_address);
+  bool Disconnect(const std::string& device_address);
+  void SetAudioFocusState(int focus_state);
+  void SetAudioTrackGain(float gain);
+
+ private:
+  friend class A2dpSinkFactory;
+
+  explicit A2dpSink(const Uuid& uuid);
+
+  // hal::bluetooth::hal::BluetoothAvInterface::Observer implementation:
+  void ConnectionStateCallback(bluetooth::hal::BluetoothAvInterface* iface,
+                               const RawAddress& bd_addr,
+                               btav_connection_state_t state) override;
+  void AudioStateCallback(bluetooth::hal::BluetoothAvInterface* iface,
+                          const RawAddress& bd_addr,
+                          btav_audio_state_t state) override;
+  void AudioConfigCallback(bluetooth::hal::BluetoothAvInterface* iface,
+                           const RawAddress& bd_addr, uint32_t sample_rate,
+                           uint8_t channel_count) override;
+
+  // See getters above for documentation.
+  const Uuid app_identifier_;
+
+  std::mutex mutex_;
+  std::mutex delegate_mutex_;
+  Delegate* delegate_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(A2dpSink);
+};
+
+class A2dpSinkFactory : public BluetoothInstanceFactory {
+ public:
+  A2dpSinkFactory();
+  ~A2dpSinkFactory() override;
+
+  // BluetoothInstanceFactory override:
+  bool RegisterInstance(const Uuid& uuid,
+                        const RegisterCallback& callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(A2dpSinkFactory);
+};
+
+}  // namespace bluetooth
diff --git a/service/adapter.cc b/service/adapter.cc
index 853ffdf..abda22d 100644
--- a/service/adapter.cc
+++ b/service/adapter.cc
@@ -24,6 +24,8 @@
 #include <base/logging.h>
 #include <base/observer_list.h>
 
+#include "service/a2dp_sink.h"
+#include "service/avrcp_control.h"
 #include "service/common/bluetooth/util/atomic_string.h"
 #include "service/gatt_client.h"
 #include "service/gatt_server.h"
@@ -33,11 +35,97 @@
 #include "service/low_energy_client.h"
 #include "service/low_energy_scanner.h"
 
+using android::String16;
 using std::lock_guard;
 using std::mutex;
 
 namespace bluetooth {
 
+namespace {
+
+RemoteDeviceProps ParseRemoteDeviceProps(int num_properties,
+                                         bt_property_t* properties) {
+  android::String16 name;
+  android::String16 address;
+  std::vector<Uuid> service_uuids;
+  int32_t device_class = 0;
+  int32_t device_type = 0;
+  int32_t rssi = 0;
+
+  for (int i = 0; i < num_properties; ++i) {
+    bt_property_t* property = properties + i;
+    switch (property->type) {
+      case BT_PROPERTY_BDNAME: {
+        if (property->len < 0) {
+          NOTREACHED() << "Invalid length for BT_PROPERTY_BDNAME";
+          break;
+        }
+        bt_bdname_t* hal_name = reinterpret_cast<bt_bdname_t*>(property->val);
+        name = String16(reinterpret_cast<char*>(hal_name->name));
+        break;
+      }
+      case BT_PROPERTY_BDADDR: {
+        if (property->len != sizeof(RawAddress)) {
+          NOTREACHED() << "Invalid length for BT_PROPERTY_BDADDR";
+          break;
+        }
+        std::string remote_bdaddr_str =
+            BtAddrString(reinterpret_cast<RawAddress*>(property->val));
+        address = String16(remote_bdaddr_str.c_str(), remote_bdaddr_str.size());
+        break;
+      }
+      case BT_PROPERTY_UUIDS: {
+        if (property->len < 0) {
+          NOTREACHED() << "Negative length on BT_PROPERTY_UUIDS:";
+          break;
+        }
+        if (property->len % sizeof(Uuid::UUID128Bit) != 0) {
+          NOTREACHED() << "Trailing bytes on BT_PROPERTY_UUIDS:";
+        }
+        auto uuids = static_cast<const Uuid::UUID128Bit*>(property->val);
+
+        for (size_t i = 0; i < property->len / sizeof(Uuid::UUID128Bit); ++i) {
+          service_uuids.push_back(Uuid::From128BitLE(uuids[i]));
+        }
+        break;
+      }
+      case BT_PROPERTY_CLASS_OF_DEVICE: {
+        if (property->len != sizeof(int32_t)) {
+          NOTREACHED() << "Invalid length for BT_PROPERTY_CLASS_OF_DEVICE";
+          break;
+        }
+        device_class = *reinterpret_cast<const int32_t*>(property->val);
+        break;
+      }
+      case BT_PROPERTY_TYPE_OF_DEVICE: {
+        if (property->len != sizeof(int32_t)) {
+          NOTREACHED() << "Invalid length for BT_PROPERTY_TYPE_OF_DEVICE";
+          break;
+        }
+        device_type = *reinterpret_cast<const int32_t*>(property->val);
+        break;
+      }
+      case BT_PROPERTY_REMOTE_RSSI: {
+        if (property->len != sizeof(int8_t)) {
+          NOTREACHED() << "Invalid length for BT_PROPERTY_REMOTE_RSSI";
+          break;
+        }
+        rssi = *reinterpret_cast<const int8_t*>(property->val);
+        break;
+      }
+      default:
+        VLOG(1) << "Unhandled adapter property: "
+                << BtPropertyText(property->type);
+        break;
+    }
+  }
+
+  return RemoteDeviceProps(name, address, service_uuids, device_class,
+                           device_type, rssi);
+}
+
+}  // namespace
+
 // static
 const char Adapter::kDefaultAddress[] = "00:00:00:00:00:00";
 // static
@@ -69,6 +157,41 @@
   // Default implementation does nothing
 }
 
+void Adapter::Observer::OnScanEnableChanged(Adapter* adapter,
+                                            bool scan_enabled) {
+  // Default implementation does nothing
+}
+
+void Adapter::Observer::OnSspRequest(Adapter* adapter,
+                                     const std::string& device_address,
+                                     const std::string& device_name, int cod,
+                                     int pairing_variant, int pass_key) {
+  // Default implementation does nothing
+}
+
+void Adapter::Observer::OnBondStateChanged(Adapter* adapter, int status,
+                                           const std::string& device_address,
+                                           int state) {
+  // Default implementation does nothing
+}
+
+void Adapter::Observer::OnGetBondedDevices(
+    Adapter* adapter, int status,
+    const std::vector<std::string>& bonded_devices) {
+  // Default implementation does nothing
+}
+
+void Adapter::Observer::OnGetRemoteDeviceProperties(
+    Adapter* adapter, int status, const std::string& device_address,
+    const RemoteDeviceProps& properties) {
+  // Default implementation does nothing
+}
+
+void Adapter::Observer::OnDeviceFound(Adapter* adapter,
+                                      const RemoteDeviceProps& properties) {
+  // Default implementation does nothing
+}
+
 // The real Adapter implementation used in production.
 class AdapterImpl : public Adapter, public hal::BluetoothInterface::Observer {
  public:
@@ -78,6 +201,8 @@
         name_(kDefaultName) {
     memset(&local_le_features_, 0, sizeof(local_le_features_));
     hal::BluetoothInterface::Get()->AddObserver(this);
+    a2dp_sink_factory_.reset(new A2dpSinkFactory);
+    avrcp_control_factory_.reset(new AvrcpControlFactory);
     ble_client_factory_.reset(new LowEnergyClientFactory(*this));
     ble_advertiser_factory_.reset(new LowEnergyAdvertiserFactory());
     ble_scanner_factory_.reset(new LowEnergyScannerFactory(*this));
@@ -183,6 +308,84 @@
 
   std::string GetAddress() const override { return address_.Get(); }
 
+  bool SetScanMode(int scan_mode) override {
+    switch (scan_mode) {
+      case BT_SCAN_MODE_NONE:
+      case BT_SCAN_MODE_CONNECTABLE:
+      case BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE:
+        break;
+      default:
+        LOG(ERROR) << "Unknown scan mode: " << scan_mode;
+        return false;
+    }
+
+    auto bd_scanmode = static_cast<bt_scan_mode_t>(scan_mode);
+
+    if (!SetAdapterProperty(BT_PROPERTY_ADAPTER_SCAN_MODE, &bd_scanmode,
+                            sizeof(bd_scanmode))) {
+      LOG(ERROR) << "Failed to set scan mode to : " << scan_mode;
+      return false;
+    }
+
+    return true;
+  }
+
+  bool SetScanEnable(bool scan_enable) override {
+    if (scan_enable) {
+      int status =
+          hal::BluetoothInterface::Get()->GetHALInterface()->start_discovery();
+      if (status != BT_STATUS_SUCCESS) {
+        LOG(ERROR) << "Failed to enable scanning";
+        return false;
+      }
+    } else {
+      int status =
+          hal::BluetoothInterface::Get()->GetHALInterface()->cancel_discovery();
+      if (status != BT_STATUS_SUCCESS) {
+        LOG(ERROR) << "Failed to disable scanning";
+        return false;
+      }
+    }
+    return true;
+  }
+
+  bool SspReply(const std::string& device_address, int variant, bool accept,
+                int32_t pass_key) override {
+    RawAddress addr;
+    if (!RawAddress::FromString(device_address, addr)) {
+      LOG(ERROR) << "Invalid device address given: " << device_address;
+      return false;
+    }
+
+    int status = hal::BluetoothInterface::Get()->GetHALInterface()->ssp_reply(
+        &addr, static_cast<bt_ssp_variant_t>(variant), accept, pass_key);
+    if (status != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to send SSP response - status: "
+                 << BtStatusText((const bt_status_t)status);
+      return false;
+    }
+
+    return true;
+  }
+
+  bool CreateBond(const std::string& device_address, int transport) override {
+    RawAddress addr;
+    if (!RawAddress::FromString(device_address, addr)) {
+      LOG(ERROR) << "Invalid device address given: " << device_address;
+      return false;
+    }
+
+    int status = hal::BluetoothInterface::Get()->GetHALInterface()->create_bond(
+        &addr, transport);
+    if (status != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to create bond - status: "
+                 << BtStatusText((const bt_status_t)status);
+      return false;
+    }
+
+    return true;
+  }
+
   bool IsMultiAdvertisementSupported() override {
     lock_guard<mutex> lock(local_le_features_lock_);
     return local_le_features_.max_adv_instance >= kMinAdvInstancesForMultiAdv;
@@ -209,6 +412,64 @@
            kMinOffloadedScanStorageBytes;
   }
 
+  bool GetBondedDevices() override {
+    int status =
+        hal::BluetoothInterface::Get()->GetHALInterface()->get_adapter_property(
+            BT_PROPERTY_ADAPTER_BONDED_DEVICES);
+    if (status != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to get bonded devices. Status: "
+                 << BtStatusText(static_cast<bt_status_t>(status));
+      return false;
+    }
+
+    return true;
+  }
+
+  bool RemoveBond(const std::string& device_address) override {
+    RawAddress addr;
+    if (!RawAddress::FromString(device_address, addr)) {
+      LOG(ERROR) << "Invalid device address given: " << device_address;
+      return false;
+    }
+
+    int status =
+        hal::BluetoothInterface::Get()->GetHALInterface()->remove_bond(&addr);
+    if (status != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to send remove bond - status: "
+                 << BtStatusText(static_cast<bt_status_t>(status));
+      return false;
+    }
+
+    return true;
+  }
+
+  bool GetRemoteDeviceProperties(const std::string& device_address) override {
+    RawAddress addr;
+    if (!RawAddress::FromString(device_address, addr)) {
+      LOG(ERROR) << "Invalid device address given: " << device_address;
+      return false;
+    }
+
+    int status = hal::BluetoothInterface::Get()
+                     ->GetHALInterface()
+                     ->get_remote_device_properties(&addr);
+    if (status != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to send GetRemoteDeviceProperties - status: "
+                 << BtStatusText((const bt_status_t)status);
+      return false;
+    }
+
+    return true;
+  }
+
+  A2dpSinkFactory* GetA2dpSinkFactory() const override {
+    return a2dp_sink_factory_.get();
+  }
+
+  AvrcpControlFactory* GetAvrcpControlFactory() const override {
+    return avrcp_control_factory_.get();
+  }
+
   LowEnergyClientFactory* GetLowEnergyClientFactory() const override {
     return ble_client_factory_.get();
   }
@@ -257,6 +518,16 @@
 
     if (status != BT_STATUS_SUCCESS) {
       LOG(ERROR) << "status: " << BtStatusText(status);
+
+      for (int i = 0; i < num_properties; ++i) {
+        bt_property_t* property = properties + i;
+        if (property->type == BT_PROPERTY_ADAPTER_BONDED_DEVICES) {
+          lock_guard<mutex> lock(observers_lock_);
+          for (auto& observer : observers_) {
+            observer.OnGetBondedDevices(this, status, {});
+          }
+        }
+      }
       return;
     }
 
@@ -290,6 +561,29 @@
           LOG(INFO) << "Supported LE features updated";
           break;
         }
+        case BT_PROPERTY_ADAPTER_BONDED_DEVICES: {
+          if (property->len < 0) {
+            NOTREACHED() << "Negative property length";
+            break;
+          }
+          auto addrs = reinterpret_cast<const RawAddress*>(property->val);
+          if (property->len % sizeof(addrs[0]) != 0) {
+            LOG(ERROR) << "Invalid property length: " << property->len;
+            // TODO(bcf): Seems to be a bug where we hit this somewhat
+            // frequently.
+            break;
+          }
+          std::vector<std::string> str_addrs;
+
+          for (size_t i = 0; i < property->len / sizeof(addrs[0]); ++i)
+            str_addrs.push_back(BtAddrString(addrs + i));
+
+          lock_guard<mutex> lock(observers_lock_);
+          for (auto& observer : observers_) {
+            observer.OnGetBondedDevices(this, status, str_addrs);
+          }
+          break;
+        }
         default:
           VLOG(1) << "Unhandled adapter property: "
                   << BtPropertyText(property->type);
@@ -300,9 +594,84 @@
     }
   }
 
-  void SSPRequestCallback(RawAddress*, bt_bdname_t*, uint32_t, bt_ssp_variant_t,
+  void RemoteDevicePropertiesCallback(bt_status_t status,
+                                      RawAddress* remote_bdaddr,
+                                      int num_properties,
+                                      bt_property_t* properties) override {
+    std::string device_address = BtAddrString(remote_bdaddr);
+    if (status != BT_STATUS_SUCCESS) {
+      lock_guard<mutex> lock(observers_lock_);
+      for (auto& observer : observers_) {
+        observer.OnGetRemoteDeviceProperties(this, status, device_address,
+                                             RemoteDeviceProps());
+      }
+      return;
+    }
+
+    RemoteDeviceProps props =
+        ParseRemoteDeviceProps(num_properties, properties);
+
+    std::string remote_bdaddr_str = BtAddrString(remote_bdaddr);
+    android::String16 address =
+        String16(remote_bdaddr_str.c_str(), remote_bdaddr_str.size());
+    props.set_address(address);
+
+    lock_guard<mutex> lock(observers_lock_);
+    for (auto& observer : observers_) {
+      observer.OnGetRemoteDeviceProperties(this, status, device_address, props);
+    }
+  }
+
+  void DeviceFoundCallback(int num_properties,
+                           bt_property_t* properties) override {
+    RemoteDeviceProps props =
+        ParseRemoteDeviceProps(num_properties, properties);
+
+    lock_guard<mutex> lock(observers_lock_);
+    for (auto& observer : observers_) {
+      observer.OnDeviceFound(this, props);
+    }
+  }
+
+  void DiscoveryStateChangedCallback(bt_discovery_state_t state) override {
+    bool enabled = false;
+    switch (state) {
+      case BT_DISCOVERY_STOPPED:
+        enabled = false;
+        break;
+      case BT_DISCOVERY_STARTED:
+        enabled = true;
+        break;
+      default:
+        NOTREACHED();
+    }
+
+    for (auto& observer : observers_) {
+      observer.OnScanEnableChanged(this, enabled);
+    }
+  }
+
+  void SSPRequestCallback(RawAddress* remote_bdaddr, bt_bdname_t* bd_name,
+                          uint32_t cod, bt_ssp_variant_t pairing_variant,
                           uint32_t pass_key) override {
-    LOG(INFO) << "Passkey is: " << pass_key;
+    std::string device_address = BtAddrString(remote_bdaddr);
+    std::string name = reinterpret_cast<char*>(bd_name->name);
+
+    lock_guard<mutex> lock(observers_lock_);
+    for (auto& observer : observers_) {
+      observer.OnSspRequest(this, device_address, name, cod, pairing_variant,
+                            pass_key);
+    }
+  }
+
+  void BondStateChangedCallback(bt_status_t status, RawAddress* remote_bdaddr,
+                                bt_bond_state_t state) override {
+    std::string device_address = BtAddrString(remote_bdaddr);
+
+    lock_guard<mutex> lock(observers_lock_);
+    for (auto& observer : observers_) {
+      observer.OnBondStateChanged(this, status, device_address, state);
+    }
   }
 
   void AclStateChangedCallback(bt_status_t status,
@@ -392,6 +761,12 @@
   std::mutex connected_devices_lock_;
   std::unordered_set<std::string> connected_devices_;
 
+  // Factory used to create per-app A2dpSink instances.
+  std::unique_ptr<A2dpSinkFactory> a2dp_sink_factory_;
+
+  // Factory used to create per-app AvrcpControl instances.
+  std::unique_ptr<AvrcpControlFactory> avrcp_control_factory_;
+
   // Factory used to create per-app LowEnergyClient instances.
   std::unique_ptr<LowEnergyClientFactory> ble_client_factory_;
 
diff --git a/service/adapter.h b/service/adapter.h
index 3becaae..032f1d9 100644
--- a/service/adapter.h
+++ b/service/adapter.h
@@ -1,5 +1,5 @@
 //
-//  Copyright 2015 Google, Inc.
+//  Copyright (C) 2015 Google, Inc.
 //
 //  Licensed under the Apache License, Version 2.0 (the "License");
 //  you may not use this file except in compliance with the License.
@@ -17,13 +17,18 @@
 #pragma once
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include <base/macros.h>
 
 #include "service/common/bluetooth/adapter_state.h"
+#include "service/common/bluetooth/remote_device_props.h"
 
 namespace bluetooth {
 
+class A2dpSinkFactory;
+class AvrcpControlFactory;
 class GattClientFactory;
 class GattServerFactory;
 class LowEnergyAdvertiserFactory;
@@ -61,6 +66,34 @@
     // true and vice versa.
     virtual void OnDeviceConnectionStateChanged(
         Adapter* adapter, const std::string& device_address, bool connected);
+
+    // Called when scanning is enabled or disabled.
+    virtual void OnScanEnableChanged(Adapter* adapter, bool scan_enabled);
+
+    // Called when a SSP pairing request comes from a remote device.
+    virtual void OnSspRequest(Adapter* adapter,
+                              const std::string& device_address,
+                              const std::string& device_name, int cod,
+                              int pairing_variant, int pass_key);
+
+    // Called when a remote device bond state changes.
+    virtual void OnBondStateChanged(Adapter* adapter, int status,
+                                    const std::string& device_address,
+                                    int state);
+
+    // Called in response to |GetBondedDevices|.
+    virtual void OnGetBondedDevices(
+        Adapter* adapter, int status,
+        const std::vector<std::string>& bonded_devices);
+
+    // Called in response to |GetRemoteDeviceProperties|.
+    virtual void OnGetRemoteDeviceProperties(Adapter* adapter, int status,
+                                             const std::string& device_address,
+                                             const RemoteDeviceProps& props);
+
+    // Called when a device is found through scanning.
+    virtual void OnDeviceFound(Adapter* adapter,
+                               const RemoteDeviceProps& props);
   };
 
   // Returns an Adapter implementation to be used in production. Don't use these
@@ -103,6 +136,19 @@
   // Returns the local adapter addess in string form (XX:XX:XX:XX:XX:XX).
   virtual std::string GetAddress() const = 0;
 
+  // Set discoverability mode.
+  virtual bool SetScanMode(int scan_mode) = 0;
+
+  // Enable or disable discoverability.
+  virtual bool SetScanEnable(bool scan_enable) = 0;
+
+  // Reply to an SSP request received in |OnSspRequest|.
+  virtual bool SspReply(const std::string& device_address, int variant,
+                        bool accept, int32_t pass_key) = 0;
+
+  // Create a bond with device specified by |device_address|.
+  virtual bool CreateBond(const std::string& device_address, int transport) = 0;
+
   // Returns true if the local adapter supports the Low-Energy
   // multi-advertisement feature.
   virtual bool IsMultiAdvertisementSupported() = 0;
@@ -122,6 +168,26 @@
   // Returns true if hardware-backed batch scanning is supported.
   virtual bool IsOffloadedScanBatchingSupported() = 0;
 
+  // When the stack call completes, |OnGetBondedDevices| will be called.
+  virtual bool GetBondedDevices() = 0;
+
+  // When the stack call completets, |OnBondStateChanged| will be called.
+  virtual bool RemoveBond(const std::string& device_address) = 0;
+
+  // When the stack call completets, |OnGetRemoteDeviceProperties| will be
+  // called.
+  virtual bool GetRemoteDeviceProperties(const std::string& device_address) = 0;
+
+  // Returns a pointer to the A2dpSinkFactory. This can be used to
+  // register per-application A2dpSinkClient instances to perform A2DP sink
+  // operations.
+  virtual A2dpSinkFactory* GetA2dpSinkFactory() const = 0;
+
+  // Returns a pointer to the AvrcpControlFactory. This can be used to register
+  // per-application AvrcpControlClient instances to perform AVRCP control
+  // operations.
+  virtual AvrcpControlFactory* GetAvrcpControlFactory() const = 0;
+
   // Returns a pointer to the LowEnergyClientFactory. This can be used to
   // register per-application LowEnergyClient instances to perform BLE GAP
   // operations.
diff --git a/service/avrcp_control.cc b/service/avrcp_control.cc
new file mode 100644
index 0000000..7692400
--- /dev/null
+++ b/service/avrcp_control.cc
@@ -0,0 +1,232 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "service/avrcp_control.h"
+
+#include <cerrno>
+#include <climits>
+#include <string>
+
+#include <base/logging.h>
+#include <base/memory/ptr_util.h>
+#include <base/strings/string_number_conversions.h>
+
+#include "service/logging_helpers.h"
+
+#include "stack/include/avrc_defs.h"
+
+using android::String16;
+
+namespace bluetooth {
+
+AvrcpControl::AvrcpControl(const Uuid& uuid, int control_id)
+    : app_identifier_(uuid), control_id_(control_id) {
+  hal::BluetoothAvrcpInterface::Get()->AddControlObserver(this);
+}
+
+AvrcpControl::~AvrcpControl() {
+  hal::BluetoothAvrcpInterface::Get()->RemoveControlObserver(this);
+}
+
+const Uuid& AvrcpControl::GetAppIdentifier() const { return app_identifier_; }
+
+int AvrcpControl::GetInstanceId() const { return control_id_; }
+
+void AvrcpControl::SetDelegate(Delegate* delegate) {
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+  delegate_ = delegate;
+}
+
+bool AvrcpControl::Enable() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  return hal::BluetoothAvrcpInterface::Get()->AvrcpControlEnable();
+}
+
+void AvrcpControl::Disable() {
+  std::lock_guard<std::mutex> lock(mutex_);
+  hal::BluetoothAvrcpInterface::Get()->AvrcpControlDisable();
+}
+
+bool AvrcpControl::SendPassThroughCommand(const std::string& device_address,
+                                          uint8_t key_code, bool key_pressed) {
+  RawAddress addr;
+  if (!RawAddress::FromString(device_address, addr)) {
+    LOG(ERROR) << "Invalid device address given: " << device_address;
+    return false;
+  }
+
+  uint8_t key_state = key_pressed ? AVRC_STATE_PRESS : AVRC_STATE_RELEASE;
+  bt_status_t status;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    status = hal::BluetoothAvrcpInterface::Get()
+                 ->GetControlHALInterface()
+                 ->send_pass_through_cmd(addr, key_code, key_state);
+  }
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << "Failed to send passthrough command";
+    return false;
+  }
+
+  return true;
+}
+
+bool AvrcpControl::SetAbsVolumeResponse(const std::string& device_address,
+                                        int32_t abs_vol, int32_t label) {
+  RawAddress addr;
+  if (!RawAddress::FromString(device_address, addr)) {
+    LOG(ERROR) << "Invalid device address given: " << device_address;
+    return false;
+  }
+
+  bt_status_t status;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    status = hal::BluetoothAvrcpInterface::Get()
+                 ->GetControlHALInterface()
+                 ->set_volume_rsp(addr, abs_vol, label);
+  }
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR) << "Failed to send set absolute volume response";
+    return false;
+  }
+
+  return true;
+}
+
+bool AvrcpControl::RegisterForAbsVolumeCallbackResponse(
+    const std::string& device_address, int32_t response_type, int32_t abs_vol,
+    int32_t label) {
+  RawAddress addr;
+  if (!RawAddress::FromString(device_address, addr)) {
+    LOG(ERROR) << "Invalid device address given: " << device_address;
+    return false;
+  }
+
+  bt_status_t status;
+  {
+    std::lock_guard<std::mutex> lock(mutex_);
+    status = hal::BluetoothAvrcpInterface::Get()
+                 ->GetControlHALInterface()
+                 ->register_abs_vol_rsp(
+                     addr, static_cast<btrc_notification_type_t>(response_type),
+                     abs_vol, label);
+  }
+  if (status != BT_STATUS_SUCCESS) {
+    LOG(ERROR)
+        << "Failed to send send register for absolute volume change callback";
+    return false;
+  }
+
+  return true;
+}
+
+void AvrcpControl::ConnectionStateCallback(bool rc_connect, bool bt_connect,
+                                           const RawAddress& bd_addr) {
+  std::string device_address = BtAddrString(&bd_addr);
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+  if (delegate_)
+    delegate_->OnConnectionState(rc_connect, bt_connect, device_address);
+}
+
+void AvrcpControl::CtrlSetabsvolCmdCallback(const RawAddress& bd_addr,
+                                            uint8_t abs_vol, uint8_t label) {
+  std::string device_address = BtAddrString(&bd_addr);
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+  if (delegate_)
+    delegate_->OnSetAbsVolumeRequest(device_address, abs_vol, label);
+}
+
+void AvrcpControl::CtrlRegisternotificationAbsVolCallback(
+    const RawAddress& bd_addr, uint8_t label) {
+  std::string device_address = BtAddrString(&bd_addr);
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+  if (delegate_)
+    delegate_->OnRegisterForAbsVolumeCallbackRequest(device_address, label);
+}
+
+void AvrcpControl::CtrlTrackChangedCallback(const RawAddress& bd_addr,
+                                            uint8_t num_attr,
+                                            btrc_element_attr_val_t* p_attrs) {
+  std::string device_address = BtAddrString(&bd_addr);
+
+  String16 title;
+  String16 artist;
+  String16 album;
+  String16 genre;
+  int track_num = -1;
+  int num_tracks = -1;
+  int play_time = -1;
+
+  for (size_t i = 0; i < num_attr; ++i) {
+    auto attr_text = reinterpret_cast<char*>(p_attrs[i].text);
+    switch (p_attrs[i].attr_id) {
+      case BTRC_MEDIA_ATTR_ID_TITLE:
+        title = String16(attr_text);
+        break;
+      case BTRC_MEDIA_ATTR_ID_ARTIST:
+        artist = String16(attr_text);
+        break;
+      case BTRC_MEDIA_ATTR_ID_ALBUM:
+        album = String16(attr_text);
+        break;
+      case BTRC_MEDIA_ATTR_ID_TRACK_NUM:
+        if (!base::StringToInt(attr_text, &track_num)) {
+          LOG(ERROR) << "Failed to parse track number";
+        }
+        break;
+      case BTRC_MEDIA_ATTR_ID_NUM_TRACKS:
+        if (!base::StringToInt(attr_text, &num_tracks)) {
+          LOG(ERROR) << "Failed to parse number of tracks";
+        }
+        break;
+      case BTRC_MEDIA_ATTR_ID_GENRE:
+        genre = String16(attr_text);
+        break;
+      case BTRC_MEDIA_ATTR_ID_PLAYING_TIME:
+        if (!base::StringToInt(attr_text, &play_time)) {
+          LOG(ERROR) << "Failed to parse playing time";
+        }
+        break;
+      default:
+        NOTREACHED();
+    }
+  }
+
+  const AvrcpMediaAttr attr(title, artist, album, genre, track_num, num_tracks,
+                            play_time);
+
+  std::lock_guard<std::mutex> lock(delegate_mutex_);
+  if (delegate_) delegate_->OnTrackChanged(device_address, attr);
+}
+
+// AvrcpControlFactory implementation
+// ========================================================
+
+AvrcpControlFactory::AvrcpControlFactory() = default;
+AvrcpControlFactory::~AvrcpControlFactory() = default;
+
+bool AvrcpControlFactory::RegisterInstance(const Uuid& uuid,
+                                           const RegisterCallback& callback) {
+  VLOG(1) << __func__ << " - Uuid: " << uuid.ToString();
+
+  int control_id = next_control_id_++;
+  std::unique_ptr<AvrcpControl> hf_client(new AvrcpControl(uuid, control_id));
+  callback(BLE_STATUS_SUCCESS, uuid, std::move(hf_client));
+  return true;
+}
+
+}  // namespace bluetooth
diff --git a/service/avrcp_control.h b/service/avrcp_control.h
new file mode 100644
index 0000000..7cde24d
--- /dev/null
+++ b/service/avrcp_control.h
@@ -0,0 +1,135 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <atomic>
+#include <mutex>
+
+#include "base/macros.h"
+
+#include "bluetooth/uuid.h"
+#include "service/bluetooth_instance.h"
+#include "service/common/bluetooth/avrcp_media_attr.h"
+#include "service/common/bluetooth/service.h"
+#include "service/hal/bluetooth_avrcp_interface.h"
+
+namespace bluetooth {
+
+class AvrcpControl : public BluetoothInstance,
+                     private hal::BluetoothAvrcpInterface::ControlObserver {
+ public:
+  class Delegate {
+   public:
+    virtual void OnConnectionState(bool rc_connect, bool bt_connect,
+                                   const std::string& device_address) = 0;
+
+    virtual void OnTrackChanged(const std::string& device_address,
+                                const AvrcpMediaAttr& attr) = 0;
+
+    virtual void OnSetAbsVolumeRequest(const std::string& device_address,
+                                       int32_t abs_vol, int32_t label) = 0;
+
+    virtual void OnRegisterForAbsVolumeCallbackRequest(
+        const std::string& device_address, int32_t label) = 0;
+
+   protected:
+    virtual ~Delegate() = default;
+  };
+
+  // The destructor automatically unregisters this instance from the stack.
+  ~AvrcpControl() override;
+
+  // Assigns a delegate to this instance. |delegate| must out-live this
+  // AvrcpControl instance.
+  void SetDelegate(Delegate* delegate);
+
+  // BluetoothClientInstace overrides:
+  const Uuid& GetAppIdentifier() const override;
+  int GetInstanceId() const override;
+
+  bool Enable();
+  void Disable();
+
+  // Send a remote control button command. Commands which can be sent
+  // are defined here:
+  // http://1394ta.org/wp-content/uploads/2015/07/2007001.pdf
+  bool SendPassThroughCommand(const std::string& device_address,
+                              uint8_t key_code, bool key_pressed);
+
+  // Send a response to a request to change absolute volume.
+  bool SetAbsVolumeResponse(const std::string& device_address, int32_t abs_vol,
+                            int32_t label);
+
+  // Send a response to a register for absolute volume change callback.
+  bool RegisterForAbsVolumeCallbackResponse(const std::string& device_address,
+                                            int32_t response_type,
+                                            int32_t abs_vol, int32_t label);
+
+ private:
+  friend class AvrcpControlFactory;
+
+  // Constructor shouldn't be called directly as instances are meant to be
+  // obtained from the factory.
+  AvrcpControl(const Uuid& uuid, int control_id);
+
+  // hal::BluetoothAvrcpInterface::ControlObserver implementation:
+  void ConnectionStateCallback(bool rc_connect, bool bt_connect,
+                               const RawAddress& bd_addr) override;
+  void CtrlSetabsvolCmdCallback(const RawAddress& bd_addr, uint8_t abs_vol,
+                                uint8_t label) override;
+  void CtrlRegisternotificationAbsVolCallback(const RawAddress& bd_addr,
+                                              uint8_t label) override;
+  void CtrlTrackChangedCallback(const RawAddress& bd_addr, uint8_t num_attr,
+                                btrc_element_attr_val_t* p_attrs) override;
+
+  // See getters for documentation.
+  const Uuid app_identifier_;
+  const int control_id_;
+
+  // Mutex that synchronizes access to the entries below.
+  std::mutex mutex_;
+
+  // Raw handle to the Delegate, which must outlive this AvrcpControl instance.
+  std::mutex delegate_mutex_;
+  Delegate* delegate_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AvrcpControl);
+};
+
+// AvrcpControlFactory is used to register and obtain a per-application
+// AvrcpControl
+// instance. Users should call RegisterClient to obtain their own unique
+// AvrcpControl instance that has been registered with the Bluetooth stack.
+class AvrcpControlFactory
+    : public BluetoothInstanceFactory,
+      private hal::BluetoothAvrcpInterface::ControlObserver {
+ public:
+  // Don't construct/destruct directly except in tests. Instead, obtain a handle
+  // from an Adapter instance.
+  AvrcpControlFactory();
+  ~AvrcpControlFactory() override;
+
+  // BluetoothInstanceFactory override:
+  bool RegisterInstance(const Uuid& uuid,
+                        const RegisterCallback& callback) override;
+
+ private:
+  std::atomic<int> next_control_id_{0};
+  DISALLOW_COPY_AND_ASSIGN(AvrcpControlFactory);
+};
+
+}  // namespace bluetooth
diff --git a/service/client/main.cc b/service/client/main.cc
index e3b4c92..e89d701 100644
--- a/service/client/main.cc
+++ b/service/client/main.cc
@@ -161,6 +161,60 @@
     return Status::ok();
   }
 
+  Status OnSspRequest(const String16& device_address,
+                      const String16& device_name, int32_t cod,
+                      int32_t pairing_variant, int32_t pass_key) override {
+    // no-op
+    return Status::ok();
+  }
+
+  Status OnGetBondedDevices(
+      int32_t status,
+      const ::std::vector<String16>& device_addresses) override {
+    BeginAsyncOut();
+    std::cout << "Bonded devices:\n";
+    for (const auto& device_address : device_addresses) {
+      std::cout << "    " << device_address << "\n";
+    }
+    EndAsyncOut();
+    return Status::ok();
+  }
+
+  Status OnBondStateChanged(int32_t status, const String16& device_address,
+                            int32_t state) override {
+    BeginAsyncOut();
+    std::cout << COLOR_BOLDWHITE "Device address: " << COLOR_BOLDYELLOW "["
+              << device_address << " bond state: " << state << " ] "
+              << COLOR_BOLDWHITE "- status: "
+              << (status == 0 ? "SUCCESS" : "FAIL") << COLOR_OFF;
+    EndAsyncOut();
+    return Status::ok();
+  }
+
+  Status OnGetRemoteDeviceProperties(
+      int32_t status, const String16& device_address,
+      const android::bluetooth::BluetoothRemoteDeviceProps& props) override {
+    // no-op
+    return Status::ok();
+  }
+
+  Status OnDeviceFound(
+      const android::bluetooth::BluetoothRemoteDeviceProps& props) override {
+    // no-op
+    return Status::ok();
+  }
+
+  Status OnDeviceConnectionStateChanged(const String16& device_address,
+                                        bool connected) override {
+    // no-op
+    return Status::ok();
+  }
+
+  Status OnScanEnableChanged(bool scan_enabled) override {
+    // no-op
+    return Status::ok();
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(CLIBluetoothCallback);
 };
diff --git a/service/common/Android.bp b/service/common/Android.bp
index b3fbfc4..b481d19 100644
--- a/service/common/Android.bp
+++ b/service/common/Android.bp
@@ -12,8 +12,10 @@
         "bluetooth/adapter_state.cc",
         "bluetooth/advertise_data.cc",
         "bluetooth/advertise_settings.cc",
+        "bluetooth/avrcp_media_attr.cc",
         "bluetooth/characteristic.cc",
         "bluetooth/descriptor.cc",
+        "bluetooth/remote_device_props.cc",
         "bluetooth/scan_filter.cc",
         "bluetooth/scan_result.cc",
         "bluetooth/scan_settings.cc",
@@ -38,6 +40,10 @@
     header_libs: [ "libbluetooth_headers" ],
     srcs: [
         "android/bluetooth/IBluetooth.aidl",
+        "android/bluetooth/IBluetoothA2dpSink.aidl",
+        "android/bluetooth/IBluetoothA2dpSinkCallback.aidl",
+        "android/bluetooth/IBluetoothAvrcpControl.aidl",
+        "android/bluetooth/IBluetoothAvrcpControlCallback.aidl",
         "android/bluetooth/IBluetoothCallback.aidl",
         "android/bluetooth/IBluetoothGattClient.aidl",
         "android/bluetooth/IBluetoothGattClientCallback.aidl",
@@ -51,10 +57,12 @@
         "android/bluetooth/IBluetoothLowEnergyCallback.aidl",
         "android/bluetooth/advertise_data.cc",
         "android/bluetooth/advertise_settings.cc",
+        "android/bluetooth/bluetooth_avrcp_media_attr.cc",
         "android/bluetooth/bluetooth_gatt_characteristic.cc",
         "android/bluetooth/bluetooth_gatt_descriptor.cc",
         "android/bluetooth/bluetooth_gatt_included_service.cc",
         "android/bluetooth/bluetooth_gatt_service.cc",
+        "android/bluetooth/bluetooth_remote_device_props.cc",
         "android/bluetooth/scan_filter.cc",
         "android/bluetooth/scan_result.cc",
         "android/bluetooth/scan_settings.cc",
diff --git a/service/common/android/bluetooth/BluetoothAvrcpMediaAttr.aidl b/service/common/android/bluetooth/BluetoothAvrcpMediaAttr.aidl
new file mode 100644
index 0000000..03355ac
--- /dev/null
+++ b/service/common/android/bluetooth/BluetoothAvrcpMediaAttr.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.bluetooth;
+
+parcelable BluetoothAvrcpMediaAttr cpp_header "android/bluetooth/bluetooth_avrcp_media_attr.h";
diff --git a/service/common/android/bluetooth/BluetoothRemoteDeviceProps.aidl b/service/common/android/bluetooth/BluetoothRemoteDeviceProps.aidl
new file mode 100644
index 0000000..db80295
--- /dev/null
+++ b/service/common/android/bluetooth/BluetoothRemoteDeviceProps.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.bluetooth;
+
+parcelable BluetoothRemoteDeviceProps cpp_header "android/bluetooth/bluetooth_remote_device_props.h";
diff --git a/service/common/android/bluetooth/IBluetooth.aidl b/service/common/android/bluetooth/IBluetooth.aidl
index cc1e015..9223afb 100644
--- a/service/common/android/bluetooth/IBluetooth.aidl
+++ b/service/common/android/bluetooth/IBluetooth.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -22,6 +22,8 @@
 import android.bluetooth.IBluetoothLeScanner;
 import android.bluetooth.IBluetoothGattClient;
 import android.bluetooth.IBluetoothGattServer;
+import android.bluetooth.IBluetoothA2dpSink;
+import android.bluetooth.IBluetoothAvrcpControl;
 
 import android.bluetooth.UUID;
 
@@ -36,6 +38,17 @@
   UUID[] GetUUIDs();
   boolean SetName(String name);
   String GetName();
+  boolean SetScanMode(int scan_mode);
+  boolean SetScanEnable(boolean scan_enable);
+  boolean SspReply(
+      String device_address,
+      int variant,
+      boolean accept,
+      int passkey);
+  boolean CreateBond(String device_address, int transport);
+  boolean GetBondedDevices();
+  boolean RemoveBond(String device_address);
+  boolean GetRemoteDeviceProperties(String device_address);
 
   void RegisterCallback(IBluetoothCallback callback);
   void UnregisterCallback(IBluetoothCallback callback);
@@ -47,4 +60,6 @@
   IBluetoothLeScanner GetLeScannerInterface();
   IBluetoothGattClient GetGattClientInterface();
   IBluetoothGattServer GetGattServerInterface();
+  IBluetoothA2dpSink GetA2dpSinkInterface();
+  IBluetoothAvrcpControl GetAvrcpControlInterface();
 }
diff --git a/service/common/android/bluetooth/IBluetoothA2dpSink.aidl b/service/common/android/bluetooth/IBluetoothA2dpSink.aidl
new file mode 100644
index 0000000..dec7d28
--- /dev/null
+++ b/service/common/android/bluetooth/IBluetoothA2dpSink.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.IBluetoothA2dpSinkCallback;
+
+interface IBluetoothA2dpSink {
+  boolean Register(in IBluetoothA2dpSinkCallback callback);
+  void Unregister();
+
+  boolean Enable();
+  boolean Disable();
+  boolean Connect(String device_address);
+  boolean Disconnect(String device_address);
+  boolean SetAudioFocusState(int state);
+  boolean SetAudioTrackGain(float gain);
+}
diff --git a/service/common/android/bluetooth/IBluetoothA2dpSinkCallback.aidl b/service/common/android/bluetooth/IBluetoothA2dpSinkCallback.aidl
new file mode 100644
index 0000000..e12be24
--- /dev/null
+++ b/service/common/android/bluetooth/IBluetoothA2dpSinkCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.bluetooth;
+
+oneway interface IBluetoothA2dpSinkCallback {
+    void OnRegistered(int status);
+    void OnConnectionState(String device_address, int state);
+    void OnAudioState(String device_address, int state);
+    void OnAudioConfig(String device_address, int sample_rate, int channel_count);
+}
diff --git a/service/common/android/bluetooth/IBluetoothAvrcpControl.aidl b/service/common/android/bluetooth/IBluetoothAvrcpControl.aidl
new file mode 100644
index 0000000..0e65a71
--- /dev/null
+++ b/service/common/android/bluetooth/IBluetoothAvrcpControl.aidl
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.IBluetoothAvrcpControlCallback;
+
+interface IBluetoothAvrcpControl {
+  boolean Register(in IBluetoothAvrcpControlCallback callback);
+  void Unregister(int id);
+  void UnregisterAll();
+
+  boolean Enable(int id);
+  boolean Disable(int id);
+
+  boolean SendPassThroughCommand(int id, String device_address,
+      int key_code, boolean key_pressed);
+
+  boolean SetAbsVolumeResponse(int id, String device_address, int abs_vol,
+      int label);
+
+  boolean RegisterForAbsVolumeCallbackResponse(int id, String device_address,
+      int response_type, int abs_vol, int label);
+}
diff --git a/service/common/android/bluetooth/IBluetoothAvrcpControlCallback.aidl b/service/common/android/bluetooth/IBluetoothAvrcpControlCallback.aidl
new file mode 100644
index 0000000..68f0db2
--- /dev/null
+++ b/service/common/android/bluetooth/IBluetoothAvrcpControlCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothAvrcpMediaAttr;
+
+oneway interface IBluetoothAvrcpControlCallback {
+  void OnRegistered(int status, int id);
+  void OnConnectionState(
+      boolean rc_connect,
+      boolean bt_connect,
+      String device_address);
+
+  void OnTrackChanged(String device_address, in BluetoothAvrcpMediaAttr track_info);
+  void OnSetAbsVolumeRequest(String device_address, int abs_vol, int label);
+  void OnRegisterForAbsVolumeCallbackRequest(String device_address, int label);
+}
diff --git a/service/common/android/bluetooth/IBluetoothCallback.aidl b/service/common/android/bluetooth/IBluetoothCallback.aidl
index 96b7cdc..a83d5d8 100644
--- a/service/common/android/bluetooth/IBluetoothCallback.aidl
+++ b/service/common/android/bluetooth/IBluetoothCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 The Android Open Source Project
+ * Copyright (C) 2016 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.
@@ -16,6 +16,26 @@
 
 package android.bluetooth;
 
+import android.bluetooth.BluetoothRemoteDeviceProps;
+
 oneway interface IBluetoothCallback {
-   void OnBluetoothStateChange(int prev_state, int new_state);
+    void OnBluetoothStateChange(int prev_state, int new_state);
+    void OnScanEnableChanged(boolean scan_enabled);
+    void OnDeviceConnectionStateChanged(
+        String device_address,
+        boolean connected);
+    void OnSspRequest(
+        String device_address,
+        String device_name,
+        int cod,
+        int pairing_variant,
+        int pass_key);
+    void OnGetBondedDevices(int status, in String[] device_addresses);
+    void OnBondStateChanged(int status, String device_address, int state);
+    void OnGetRemoteDeviceProperties(
+        int status,
+        String device_address,
+        in BluetoothRemoteDeviceProps props);
+    void OnDeviceFound(
+        in BluetoothRemoteDeviceProps props);
 }
diff --git a/service/common/android/bluetooth/bluetooth_avrcp_media_attr.cc b/service/common/android/bluetooth/bluetooth_avrcp_media_attr.cc
new file mode 100644
index 0000000..18c10b1
--- /dev/null
+++ b/service/common/android/bluetooth/bluetooth_avrcp_media_attr.cc
@@ -0,0 +1,84 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "android/bluetooth/bluetooth_avrcp_media_attr.h"
+
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+using android::OK;
+using android::String16;
+using android::String8;
+
+namespace android {
+namespace bluetooth {
+
+status_t BluetoothAvrcpMediaAttr::writeToParcel(Parcel* parcel) const {
+  status_t status = parcel->writeString16(title_);
+  if (status != OK) return status;
+
+  status = parcel->writeString16(artist_);
+  if (status != OK) return status;
+
+  status = parcel->writeString16(album_);
+  if (status != OK) return status;
+
+  status = parcel->writeString16(genre_);
+  if (status != OK) return status;
+
+  status = parcel->writeInt32(track_num_);
+  if (status != OK) return status;
+
+  status = parcel->writeInt32(num_tracks_);
+  if (status != OK) return status;
+
+  status = parcel->writeInt32(play_time_);
+  if (status != OK) return status;
+
+  return status;
+}
+
+status_t BluetoothAvrcpMediaAttr::readFromParcel(const Parcel* parcel) {
+  status_t status = parcel->readString16(&title_);
+  if (status != OK) return status;
+
+  status = parcel->readString16(&artist_);
+  if (status != OK) return status;
+
+  status = parcel->readString16(&album_);
+  if (status != OK) return status;
+
+  status = parcel->readString16(&genre_);
+  if (status != OK) return status;
+
+  int32_t tmp;
+  status = parcel->readInt32(&tmp);
+  if (status != OK) return status;
+  track_num_ = tmp;
+
+  status = parcel->readInt32(&tmp);
+  if (status != OK) return status;
+  num_tracks_ = tmp;
+
+  status = parcel->readInt32(&tmp);
+  if (status != OK) return status;
+  play_time_ = tmp;
+
+  return status;
+}
+
+}  // namespace bluetooth
+}  // namespace android
diff --git a/service/common/android/bluetooth/bluetooth_avrcp_media_attr.h b/service/common/android/bluetooth/bluetooth_avrcp_media_attr.h
new file mode 100644
index 0000000..d41fef6
--- /dev/null
+++ b/service/common/android/bluetooth/bluetooth_avrcp_media_attr.h
@@ -0,0 +1,57 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include "bluetooth/avrcp_media_attr.h"
+
+using android::Parcel;
+using android::Parcelable;
+using android::status_t;
+
+namespace android {
+namespace bluetooth {
+
+class BluetoothAvrcpMediaAttr : public Parcelable,
+                                public ::bluetooth::AvrcpMediaAttr {
+ public:
+  BluetoothAvrcpMediaAttr() = default;
+  BluetoothAvrcpMediaAttr(const ::bluetooth::AvrcpMediaAttr& other)
+      : ::bluetooth::AvrcpMediaAttr(other) {}  // NOLINT(implicit)
+  BluetoothAvrcpMediaAttr(const BluetoothAvrcpMediaAttr& other)
+      : ::bluetooth::AvrcpMediaAttr(other) {}  // NOLINT(implicit)
+  ~BluetoothAvrcpMediaAttr() = default;
+
+  // Write |this| parcelable to the given |parcel|.  Keep in mind that
+  // implementations of writeToParcel must be manually kept in sync
+  // with readFromParcel and the Java equivalent versions of these methods.
+  //
+  // Returns android::OK on success and an appropriate error otherwise.
+  status_t writeToParcel(Parcel* parcel) const override;
+
+  // Read data from the given |parcel| into |this|.  After readFromParcel
+  // completes, |this| should have equivalent state to the object that
+  // wrote itself to the parcel.
+  //
+  // Returns android::OK on success and an appropriate error otherwise.
+  status_t readFromParcel(const Parcel* parcel) override;
+};
+
+}  // namespace bluetooth
+}  // namespace android
diff --git a/service/common/android/bluetooth/bluetooth_remote_device_props.cc b/service/common/android/bluetooth/bluetooth_remote_device_props.cc
new file mode 100644
index 0000000..190fa94
--- /dev/null
+++ b/service/common/android/bluetooth/bluetooth_remote_device_props.cc
@@ -0,0 +1,86 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "android/bluetooth/bluetooth_remote_device_props.h"
+
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include "android/bluetooth/uuid.h"
+
+using android::OK;
+using android::String16;
+using android::String8;
+
+namespace android {
+namespace bluetooth {
+
+status_t BluetoothRemoteDeviceProps::writeToParcel(Parcel* parcel) const {
+  status_t status = parcel->writeString16(name_);
+  if (status != OK) return status;
+
+  status = parcel->writeString16(address_);
+  if (status != OK) return status;
+
+  std::vector<UUID> uuids;
+  for (const auto& uuid : service_uuids_) {
+    uuids.push_back(uuid);
+  }
+
+  status = parcel->writeParcelableVector(uuids);
+  if (status != OK) return status;
+
+  status = parcel->writeInt32(device_class_);
+  if (status != OK) return status;
+
+  status = parcel->writeInt32(device_type_);
+  if (status != OK) return status;
+
+  status = parcel->writeInt32(rssi_);
+  if (status != OK) return status;
+
+  return status;
+}
+
+status_t BluetoothRemoteDeviceProps::readFromParcel(const Parcel* parcel) {
+  status_t status = parcel->readString16(&name_);
+  if (status != OK) return status;
+
+  status = parcel->readString16(&address_);
+  if (status != OK) return status;
+
+  std::vector<UUID> uuids;
+  status = parcel->readParcelableVector(&uuids);
+  if (status != OK) return status;
+
+  for (const auto& uuid : uuids) {
+    service_uuids_.push_back(uuid.uuid);
+  }
+
+  status = parcel->readInt32(&device_class_);
+  if (status != OK) return status;
+
+  status = parcel->readInt32(&device_type_);
+  if (status != OK) return status;
+
+  status = parcel->readInt32(&rssi_);
+  if (status != OK) return status;
+
+  return status;
+}
+
+}  // namespace bluetooth
+}  // namespace android
diff --git a/service/common/android/bluetooth/bluetooth_remote_device_props.h b/service/common/android/bluetooth/bluetooth_remote_device_props.h
new file mode 100644
index 0000000..e2ad346
--- /dev/null
+++ b/service/common/android/bluetooth/bluetooth_remote_device_props.h
@@ -0,0 +1,57 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include "bluetooth/remote_device_props.h"
+
+using android::Parcel;
+using android::Parcelable;
+using android::status_t;
+
+namespace android {
+namespace bluetooth {
+
+class BluetoothRemoteDeviceProps : public Parcelable,
+                                   public ::bluetooth::RemoteDeviceProps {
+ public:
+  BluetoothRemoteDeviceProps() = default;
+  BluetoothRemoteDeviceProps(const ::bluetooth::RemoteDeviceProps& other)
+      : ::bluetooth::RemoteDeviceProps(other) {}  // NOLINT(implicit)
+  BluetoothRemoteDeviceProps(const BluetoothRemoteDeviceProps& other)
+      : ::bluetooth::RemoteDeviceProps(other) {}  // NOLINT(implicit)
+  ~BluetoothRemoteDeviceProps() = default;
+
+  // Write |this| parcelable to the given |parcel|.  Keep in mind that
+  // implementations of writeToParcel must be manually kept in sync
+  // with readFromParcel and the Java equivalent versions of these methods.
+  //
+  // Returns android::OK on success and an appropriate error otherwise.
+  status_t writeToParcel(Parcel* parcel) const override;
+
+  // Read data from the given |parcel| into |this|.  After readFromParcel
+  // completes, |this| should have equivalent state to the object that
+  // wrote itself to the parcel.
+  //
+  // Returns android::OK on success and an appropriate error otherwise.
+  status_t readFromParcel(const Parcel* parcel) override;
+};
+
+}  // namespace bluetooth
+}  // namespace android
diff --git a/service/common/bluetooth/avrcp_media_attr.cc b/service/common/bluetooth/avrcp_media_attr.cc
new file mode 100644
index 0000000..652ee6c
--- /dev/null
+++ b/service/common/bluetooth/avrcp_media_attr.cc
@@ -0,0 +1,38 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "bluetooth/avrcp_media_attr.h"
+
+using android::String16;
+
+namespace bluetooth {
+
+AvrcpMediaAttr::AvrcpMediaAttr() = default;
+AvrcpMediaAttr::AvrcpMediaAttr(const AvrcpMediaAttr& other) = default;
+AvrcpMediaAttr::AvrcpMediaAttr(const String16& title, const String16& artist,
+                               const String16& album, const String16& genre,
+                               int track_num, int num_tracks, int play_time)
+    : title_(title),
+      artist_(artist),
+      album_(album),
+      genre_(genre),
+      track_num_(track_num),
+      num_tracks_(num_tracks),
+      play_time_(play_time) {}
+
+AvrcpMediaAttr::~AvrcpMediaAttr() = default;
+
+}  // namespace bluetooth
diff --git a/service/common/bluetooth/avrcp_media_attr.h b/service/common/bluetooth/avrcp_media_attr.h
new file mode 100644
index 0000000..7ae69e2
--- /dev/null
+++ b/service/common/bluetooth/avrcp_media_attr.h
@@ -0,0 +1,51 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <utils/String16.h>
+
+namespace bluetooth {
+
+class AvrcpMediaAttr {
+ public:
+  AvrcpMediaAttr();
+  AvrcpMediaAttr(const AvrcpMediaAttr& other);
+  AvrcpMediaAttr(const android::String16& title,
+                 const android::String16& artist,
+                 const android::String16& album, const android::String16& genre,
+                 int track_num, int num_tracks, int play_time);
+  ~AvrcpMediaAttr();
+
+  const android::String16& title() const { return title_; }
+  const android::String16& artist() const { return artist_; }
+  const android::String16& album() const { return album_; }
+  const android::String16& genre() const { return genre_; }
+  int track_num() const { return track_num_; }
+  int num_tracks() const { return num_tracks_; }
+  int play_time() const { return play_time_; }
+
+ protected:
+  android::String16 title_;
+  android::String16 artist_;
+  android::String16 album_;
+  android::String16 genre_;
+  int track_num_ = -1;
+  int num_tracks_ = -1;
+  int play_time_ = -1;
+};
+
+}  // namespace bluetooth
diff --git a/service/common/bluetooth/remote_device_props.cc b/service/common/bluetooth/remote_device_props.cc
new file mode 100644
index 0000000..61ee462
--- /dev/null
+++ b/service/common/bluetooth/remote_device_props.cc
@@ -0,0 +1,38 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "bluetooth/remote_device_props.h"
+
+using android::String16;
+
+namespace bluetooth {
+
+RemoteDeviceProps::RemoteDeviceProps() = default;
+RemoteDeviceProps::RemoteDeviceProps(const RemoteDeviceProps& other) = default;
+RemoteDeviceProps::RemoteDeviceProps(const android::String16& name,
+                                     const android::String16& address,
+                                     const std::vector<Uuid>& service_uuids,
+                                     int32_t device_class, int32_t device_type,
+                                     int32_t rssi)
+    : name_(name),
+      address_(address),
+      service_uuids_(service_uuids),
+      device_class_(device_class),
+      device_type_(device_type),
+      rssi_(rssi) {}
+RemoteDeviceProps::~RemoteDeviceProps() = default;
+
+}  // namespace bluetooth
diff --git a/service/common/bluetooth/remote_device_props.h b/service/common/bluetooth/remote_device_props.h
new file mode 100644
index 0000000..f6997ff
--- /dev/null
+++ b/service/common/bluetooth/remote_device_props.h
@@ -0,0 +1,55 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <vector>
+
+#include <utils/String16.h>
+
+#include "bluetooth/uuid.h"
+
+namespace bluetooth {
+
+class RemoteDeviceProps {
+ public:
+  RemoteDeviceProps();
+  RemoteDeviceProps(const RemoteDeviceProps& other);
+  RemoteDeviceProps(const android::String16& name,
+                    const android::String16& address,
+                    const std::vector<Uuid>& service_uuids,
+                    int32_t device_class, int32_t device_type, int32_t rssi);
+  ~RemoteDeviceProps();
+
+  void set_address(const android::String16& address) { address_ = address; }
+
+  const android::String16& name() const { return name_; }
+  const android::String16& address() const { return address_; }
+  const std::vector<Uuid>& service_uuids() const { return service_uuids_; }
+  int32_t device_class() const { return device_class_; }
+  int32_t device_type() const { return device_type_; }
+  int32_t rssi() const { return rssi_; }
+
+ protected:
+  android::String16 name_;
+  android::String16 address_;
+  std::vector<Uuid> service_uuids_;
+  int32_t device_class_ = -1;
+  int32_t device_type_ = -1;
+  int32_t rssi_ = -1;
+};
+
+}  // namespace bluetooth
diff --git a/service/daemon.cc b/service/daemon.cc
index 86a3b5a..a64c2d8 100644
--- a/service/daemon.cc
+++ b/service/daemon.cc
@@ -22,6 +22,8 @@
 #include <base/run_loop.h>
 
 #include "service/adapter.h"
+#include "service/hal/bluetooth_av_interface.h"
+#include "service/hal/bluetooth_avrcp_interface.h"
 #include "service/hal/bluetooth_gatt_interface.h"
 #include "service/hal/bluetooth_interface.h"
 #include "service/ipc/ipc_manager.h"
@@ -69,6 +71,10 @@
 
     if (!hal::BluetoothGattInterface::Initialize()) goto failed;
 
+    if (!hal::BluetoothAvInterface::Initialize()) goto failed;
+
+    if (!hal::BluetoothAvrcpInterface::Initialize()) goto failed;
+
     return true;
 
   failed:
@@ -81,6 +87,10 @@
       hal::BluetoothGattInterface::CleanUp();
     if (hal::BluetoothInterface::IsInitialized())
       hal::BluetoothInterface::CleanUp();
+    if (hal::BluetoothAvInterface::IsInitialized())
+      hal::BluetoothAvInterface::CleanUp();
+    if (hal::BluetoothAvrcpInterface::IsInitialized())
+      hal::BluetoothAvrcpInterface::CleanUp();
   }
 
   void CleanUpBluetoothStack() {
diff --git a/service/hal/bluetooth_av_interface.cc b/service/hal/bluetooth_av_interface.cc
new file mode 100644
index 0000000..8901e73
--- /dev/null
+++ b/service/hal/bluetooth_av_interface.cc
@@ -0,0 +1,364 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "service/hal/bluetooth_av_interface.h"
+
+#include <shared_mutex>
+
+#include <base/logging.h>
+#include <base/memory/ptr_util.h>
+#include <base/observer_list.h>
+
+#include "service/hal/bluetooth_interface.h"
+
+namespace bluetooth {
+namespace hal {
+
+namespace {
+
+BluetoothAvInterface* g_interface = nullptr;
+
+#if defined(OS_GENERIC) && defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION < 3500)
+using shared_mutex_impl = std::shared_mutex;
+#else
+using shared_mutex_impl = std::shared_timed_mutex;
+#endif
+
+// Mutex used by callbacks to access |g_interface|. If we initialize or clean it
+// use unique_lock. If only accessing |g_interface| use shared lock.
+shared_mutex_impl g_instance_lock;
+
+base::ObserverList<BluetoothAvInterface::A2dpSourceObserver>*
+GetA2dpSourceObservers();
+base::ObserverList<BluetoothAvInterface::A2dpSinkObserver>*
+GetA2dpSinkObservers();
+
+#define VERIFY_INTERFACE_OR_RETURN()                                   \
+  do {                                                                 \
+    if (!g_interface) {                                                \
+      LOG(WARNING) << "Callback received while |g_interface| is NULL"; \
+      return;                                                          \
+    }                                                                  \
+  } while (0)
+
+}  // namespace
+
+void SourceConnectionStateCallback(const RawAddress& bd_addr,
+                                   btav_connection_state_t state) {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VERIFY_INTERFACE_OR_RETURN();
+
+  for (auto& observer : *GetA2dpSourceObservers()) {
+    observer.ConnectionStateCallback(g_interface, bd_addr, state);
+  }
+}
+
+void SourceAudioStateCallback(const RawAddress& bd_addr,
+                              btav_audio_state_t state) {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetA2dpSourceObservers()) {
+    observer.AudioStateCallback(g_interface, bd_addr, state);
+  }
+}
+
+void SourceAudioConfigCallback(
+    const RawAddress& bd_addr, btav_a2dp_codec_config_t codec_config,
+    std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
+    std::vector<btav_a2dp_codec_config_t> codecs_selectable_capabilities) {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetA2dpSourceObservers()) {
+    observer.AudioConfigCallback(g_interface, bd_addr, codec_config,
+                                 codecs_local_capabilities,
+                                 codecs_selectable_capabilities);
+  }
+}
+
+void SinkConnectionStateCallback(const RawAddress& bd_addr,
+                                 btav_connection_state_t state) {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetA2dpSinkObservers()) {
+    observer.ConnectionStateCallback(g_interface, bd_addr, state);
+  }
+}
+
+void SinkAudioStateCallback(const RawAddress& bd_addr,
+                            btav_audio_state_t state) {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetA2dpSinkObservers()) {
+    observer.AudioStateCallback(g_interface, bd_addr, state);
+  }
+}
+
+void SinkAudioConfigCallback(const RawAddress& bd_addr, uint32_t sample_rate,
+                             uint8_t channel_count) {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetA2dpSinkObservers()) {
+    observer.AudioConfigCallback(g_interface, bd_addr, sample_rate,
+                                 channel_count);
+  }
+}
+
+btav_source_callbacks_t av_source_callbacks = {
+    .size = sizeof(btav_source_callbacks_t),
+    .connection_state_cb = SourceConnectionStateCallback,
+    .audio_state_cb = SourceAudioStateCallback,
+    .audio_config_cb = SourceAudioConfigCallback,
+};
+
+btav_sink_callbacks_t av_sink_callbacks = {
+    .size = sizeof(btav_sink_callbacks_t),
+    .connection_state_cb = SinkConnectionStateCallback,
+    .audio_state_cb = SinkAudioStateCallback,
+    .audio_config_cb = SinkAudioConfigCallback,
+};
+
+class BluetoothAvInterfaceImpl : public BluetoothAvInterface {
+ public:
+  BluetoothAvInterfaceImpl() = default;
+  ~BluetoothAvInterfaceImpl() override {
+    A2dpSinkDisable();
+    A2dpSourceDisable();
+  }
+
+  bool A2dpSourceEnable(
+      std::vector<btav_a2dp_codec_config_t> codec_priorities) override {
+    if (source_enabled_) {
+      return true;
+    }
+
+    // Right now we only support one connected audio device.
+    int max_connected_audio_devices = 1;
+    if (hal_source_iface_->init(
+            &av_source_callbacks, max_connected_audio_devices,
+            std::move(codec_priorities)) != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to initialize HAL A2DP source interface";
+      return false;
+    }
+    source_enabled_ = true;
+    return true;
+  }
+
+  void A2dpSourceDisable() override {
+    if (!source_enabled_) {
+      return;
+    }
+
+    hal_source_iface_->cleanup();
+    source_enabled_ = false;
+  }
+
+  bool A2dpSinkEnable() override {
+    if (sink_enabled_) {
+      return true;
+    }
+    if (hal_sink_iface_->init(&av_sink_callbacks) != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to initialize HAL A2DP sink interface";
+      return false;
+    }
+    sink_enabled_ = true;
+    return true;
+  }
+
+  void A2dpSinkDisable() override {
+    if (!sink_enabled_) {
+      return;
+    }
+    hal_sink_iface_->cleanup();
+    sink_enabled_ = false;
+  }
+
+  void AddA2dpSourceObserver(A2dpSourceObserver* observer) override {
+    a2dp_source_observers_.AddObserver(observer);
+  }
+
+  void RemoveA2dpSourceObserver(A2dpSourceObserver* observer) override {
+    a2dp_source_observers_.RemoveObserver(observer);
+  }
+
+  void AddA2dpSinkObserver(A2dpSinkObserver* observer) override {
+    a2dp_sink_observers_.AddObserver(observer);
+  }
+
+  void RemoveA2dpSinkObserver(A2dpSinkObserver* observer) override {
+    a2dp_sink_observers_.RemoveObserver(observer);
+  }
+
+  const btav_source_interface_t* GetA2dpSourceHALInterface() override {
+    return hal_source_iface_;
+  }
+
+  const btav_sink_interface_t* GetA2dpSinkHALInterface() override {
+    return hal_sink_iface_;
+  }
+
+  bool Initialize() {
+    const bt_interface_t* bt_iface =
+        BluetoothInterface::Get()->GetHALInterface();
+    CHECK(bt_iface);
+
+    const auto* hal_source_iface =
+        reinterpret_cast<const btav_source_interface_t*>(
+            bt_iface->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID));
+    if (!hal_source_iface) {
+      LOG(ERROR) << "Failed to obtain A2DP source interface handle";
+      return false;
+    }
+
+    const auto* hal_sink_iface = reinterpret_cast<const btav_sink_interface_t*>(
+        bt_iface->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_SINK_ID));
+    if (!hal_sink_iface) {
+      LOG(ERROR) << "Failed to obtain A2DP sink interface handle";
+      return false;
+    }
+
+    hal_sink_iface_ = hal_sink_iface;
+    hal_source_iface_ = hal_source_iface;
+
+    // Only initialize the sink interface.
+    return A2dpSinkEnable();
+  }
+
+  base::ObserverList<A2dpSourceObserver>* source_observers() {
+    return &a2dp_source_observers_;
+  }
+
+  base::ObserverList<A2dpSinkObserver>* sink_observers() {
+    return &a2dp_sink_observers_;
+  }
+
+ private:
+  base::ObserverList<A2dpSourceObserver> a2dp_source_observers_;
+  base::ObserverList<A2dpSinkObserver> a2dp_sink_observers_;
+
+  const btav_source_interface_t* hal_source_iface_ = nullptr;
+  const btav_sink_interface_t* hal_sink_iface_ = nullptr;
+
+  bool source_enabled_ = false;
+  bool sink_enabled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothAvInterfaceImpl);
+};
+
+namespace {
+
+base::ObserverList<BluetoothAvInterface::A2dpSourceObserver>*
+GetA2dpSourceObservers() {
+  CHECK(g_interface);
+  return static_cast<BluetoothAvInterfaceImpl*>(g_interface)
+      ->source_observers();
+}
+
+base::ObserverList<BluetoothAvInterface::A2dpSinkObserver>*
+GetA2dpSinkObservers() {
+  CHECK(g_interface);
+  return static_cast<BluetoothAvInterfaceImpl*>(g_interface)->sink_observers();
+}
+
+}  // namespace
+
+void BluetoothAvInterface::A2dpSourceObserver::ConnectionStateCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr,
+    btav_connection_state_t state) {
+  // Do nothing.
+}
+
+void BluetoothAvInterface::A2dpSourceObserver::AudioStateCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr,
+    btav_audio_state_t state) {
+  // Do nothing.
+}
+
+void BluetoothAvInterface::A2dpSourceObserver::AudioConfigCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr,
+    const btav_a2dp_codec_config_t& codec_config,
+    const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
+    const std::vector<btav_a2dp_codec_config_t>
+        codecs_selectable_capabilities) {
+  // Do nothing.
+}
+
+void BluetoothAvInterface::A2dpSinkObserver::ConnectionStateCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr,
+    btav_connection_state_t state) {
+  // Do nothing.
+}
+
+void BluetoothAvInterface::A2dpSinkObserver::AudioStateCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr,
+    btav_audio_state_t state) {
+  // Do nothing.
+}
+
+void BluetoothAvInterface::A2dpSinkObserver::AudioConfigCallback(
+    BluetoothAvInterface* iface, const RawAddress& bd_addr,
+    uint32_t sample_rate, uint8_t channel_count) {
+  // Do nothing.
+}
+
+// static
+bool BluetoothAvInterface::Initialize() {
+  std::unique_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(!g_interface);
+
+  auto impl = base::MakeUnique<BluetoothAvInterfaceImpl>();
+  if (!impl->Initialize()) {
+    LOG(ERROR) << "Failed to initialize BluetoothAvInterface";
+    return false;
+  }
+
+  g_interface = impl.release();
+  return true;
+}
+
+// static
+void BluetoothAvInterface::CleanUp() {
+  std::unique_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(g_interface);
+
+  delete g_interface;
+  g_interface = nullptr;
+}
+
+// static
+bool BluetoothAvInterface::IsInitialized() {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  return g_interface != nullptr;
+}
+
+// static
+void BluetoothAvInterface::InitializeForTesting(
+    BluetoothAvInterface* test_instance) {
+  std::unique_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(test_instance);
+  CHECK(!g_interface);
+
+  g_interface = test_instance;
+}
+
+// static
+BluetoothAvInterface* BluetoothAvInterface::Get() {
+  std::shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(g_interface);
+  return g_interface;
+}
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/service/hal/bluetooth_av_interface.h b/service/hal/bluetooth_av_interface.h
new file mode 100644
index 0000000..9346143
--- /dev/null
+++ b/service/hal/bluetooth_av_interface.h
@@ -0,0 +1,96 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <base/macros.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_av.h>
+
+#include <vector>
+
+namespace bluetooth {
+namespace hal {
+
+class BluetoothAvInterface {
+ public:
+  class A2dpSourceObserver {
+   public:
+    virtual void ConnectionStateCallback(BluetoothAvInterface* iface,
+                                         const RawAddress& bd_addr,
+                                         btav_connection_state_t state);
+    virtual void AudioStateCallback(BluetoothAvInterface* iface,
+                                    const RawAddress& bd_addr,
+                                    btav_audio_state_t state);
+    virtual void AudioConfigCallback(
+        BluetoothAvInterface* iface, const RawAddress& bd_addr,
+        const btav_a2dp_codec_config_t& codec_config,
+        const std::vector<btav_a2dp_codec_config_t> codecs_local_capabilities,
+        const std::vector<btav_a2dp_codec_config_t>
+            codecs_selectable_capabilities);
+
+   protected:
+    virtual ~A2dpSourceObserver() = default;
+  };
+
+  class A2dpSinkObserver {
+   public:
+    virtual void ConnectionStateCallback(BluetoothAvInterface* iface,
+                                         const RawAddress& bd_addr,
+                                         btav_connection_state_t state);
+    virtual void AudioStateCallback(BluetoothAvInterface* iface,
+                                    const RawAddress& bd_addr,
+                                    btav_audio_state_t state);
+    virtual void AudioConfigCallback(BluetoothAvInterface* iface,
+                                     const RawAddress& bd_addr,
+                                     uint32_t sample_rate,
+                                     uint8_t channel_count);
+
+   protected:
+    virtual ~A2dpSinkObserver() = default;
+  };
+
+  static bool Initialize();
+  static void CleanUp();
+  static bool IsInitialized();
+  static void InitializeForTesting(BluetoothAvInterface* test_instance);
+
+  static BluetoothAvInterface* Get();
+
+  virtual bool A2dpSourceEnable(
+      std::vector<btav_a2dp_codec_config_t> codec_priorities) = 0;
+  virtual void A2dpSourceDisable() = 0;
+  virtual bool A2dpSinkEnable() = 0;
+  virtual void A2dpSinkDisable() = 0;
+
+  virtual void AddA2dpSourceObserver(A2dpSourceObserver* observer) = 0;
+  virtual void RemoveA2dpSourceObserver(A2dpSourceObserver* observer) = 0;
+  virtual void AddA2dpSinkObserver(A2dpSinkObserver* observer) = 0;
+  virtual void RemoveA2dpSinkObserver(A2dpSinkObserver* observer) = 0;
+
+  virtual const btav_source_interface_t* GetA2dpSourceHALInterface() = 0;
+  virtual const btav_sink_interface_t* GetA2dpSinkHALInterface() = 0;
+
+ protected:
+  BluetoothAvInterface() = default;
+  virtual ~BluetoothAvInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BluetoothAvInterface);
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/service/hal/bluetooth_avrcp_interface.cc b/service/hal/bluetooth_avrcp_interface.cc
new file mode 100644
index 0000000..02e7316
--- /dev/null
+++ b/service/hal/bluetooth_avrcp_interface.cc
@@ -0,0 +1,891 @@
+//
+//  Copyright 2015 Google, Inc.
+//
+//  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 "service/hal/bluetooth_avrcp_interface.h"
+
+#include <mutex>
+#include <shared_mutex>
+
+#include <base/logging.h>
+#include <base/observer_list.h>
+
+#include "service/hal/bluetooth_interface.h"
+#include "service/logging_helpers.h"
+
+using std::lock_guard;
+using std::mutex;
+using std::shared_lock;
+using std::unique_lock;
+#if defined(OS_GENERIC) && defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION < 3500)
+using shared_mutex_impl = std::shared_mutex;
+#else
+using shared_mutex_impl = std::shared_timed_mutex;
+#endif
+
+namespace bluetooth {
+namespace hal {
+
+namespace {
+
+// The global BluetoothAvrcpInterface instance.
+BluetoothAvrcpInterface* g_interface = nullptr;
+
+// Mutex used by callbacks to access |g_interface|. If we initialize or clean it
+// use unique_lock. If only accessing |g_interface| use shared lock.
+// TODO(jpawlowski): this should be just shared_mutex, as we currently don't use
+// timed methods. Change to shared_mutex when we upgrade to C++14
+shared_mutex_impl g_instance_lock;
+
+// Helper for obtaining the observer lists. This is forward declared here
+// and defined below since it depends on BluetoothInterfaceImpl.
+base::ObserverList<BluetoothAvrcpInterface::TargetObserver>*
+GetTargetObservers();
+
+base::ObserverList<BluetoothAvrcpInterface::ControlObserver>*
+GetControlObservers();
+
+#define VERIFY_INTERFACE_OR_RETURN()                                   \
+  do {                                                                 \
+    if (!g_interface) {                                                \
+      LOG(WARNING) << "Callback received while |g_interface| is NULL"; \
+      return;                                                          \
+    }                                                                  \
+  } while (0)
+
+void RemoteFeaturesCallback(const RawAddress& bd_addr,
+                            btrc_remote_features_t features) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.RemoteFeaturesCallback(bd_addr, features);
+  }
+}
+
+void GetPlayStatusCallback(const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetPlayStatusCallback(bd_addr);
+  }
+}
+
+void ListPlayerAppAttrCallback(const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.ListPlayerAppAttrCallback(bd_addr);
+  }
+}
+
+void ListPlayerAppValuesCallback(btrc_player_attr_t attr_id,
+                                 const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.ListPlayerAppValuesCallback(attr_id, bd_addr);
+  }
+}
+
+void GetPlayerAppValueCallback(uint8_t num_attr, btrc_player_attr_t* p_attrs,
+                               const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetPlayerAppValueCallback(num_attr, p_attrs, bd_addr);
+  }
+}
+
+void GetPlayerAppAttrsTextCallback(uint8_t num_attr,
+                                   btrc_player_attr_t* p_attrs,
+                                   const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetPlayerAppAttrsTextCallback(num_attr, p_attrs, bd_addr);
+  }
+}
+
+void GetPlayerAppValuesTextCallback(uint8_t attr_id, uint8_t num_val,
+                                    uint8_t* p_vals,
+                                    const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetPlayerAppValuesTextCallback(attr_id, num_val, p_vals, bd_addr);
+  }
+}
+
+void SetPlayerAppValueCallback(btrc_player_settings_t* p_vals,
+                               const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.SetPlayerAppValueCallback(p_vals, bd_addr);
+  }
+}
+
+void GetElementAttrCallback(uint8_t num_attr, btrc_media_attr_t* p_attrs,
+                            const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetElementAttrCallback(num_attr, p_attrs, bd_addr);
+  }
+}
+
+void RegisterNotificationCallback(btrc_event_id_t event_id, uint32_t param,
+                                  const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.RegisterNotificationCallback(event_id, param, bd_addr);
+  }
+}
+
+void VolumeChangeCallback(uint8_t volume, uint8_t ctype,
+                          const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.VolumeChangeCallback(volume, ctype, bd_addr);
+  }
+}
+
+void PassthroughCmdCallback(int id, int key_state, const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.PassthroughCmdCallback(id, key_state, bd_addr);
+  }
+}
+
+void SetAddressedPlayerCallback(uint16_t player_id, const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.SetAddressedPlayerCallback(player_id, bd_addr);
+  }
+}
+
+void SetBrowsedPlayerCallback(uint16_t player_id, const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.SetBrowsedPlayerCallback(player_id, bd_addr);
+  }
+}
+
+void GetFolderItemsCallback(uint8_t scope, uint32_t start_item,
+                            uint32_t end_item, uint8_t num_attr,
+                            uint32_t* p_attr_ids, const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetFolderItemsCallback(scope, start_item, end_item, num_attr,
+                                    p_attr_ids, bd_addr);
+  }
+}
+
+void ChangePathCallback(uint8_t direction, uint8_t* folder_uid,
+                        const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.ChangePathCallback(direction, folder_uid, bd_addr);
+  }
+}
+
+void GetItemAttrCallback(uint8_t scope, uint8_t* uid, uint16_t uid_counter,
+                         uint8_t num_attr, btrc_media_attr_t* p_attrs,
+                         const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetItemAttrCallback(scope, uid, uid_counter, num_attr, p_attrs,
+                                 bd_addr);
+  }
+}
+
+void PlayItemCallback(uint8_t scope, uint16_t uid_counter, uint8_t* uid,
+                      const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.PlayItemCallback(scope, uid_counter, uid, bd_addr);
+  }
+}
+
+void GetTotalNumOfItemsCallback(uint8_t scope, const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.GetTotalNumOfItemsCallback(scope, bd_addr);
+  }
+}
+
+void SearchCallback(uint16_t charset_id, uint16_t str_len, uint8_t* p_str,
+                    const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.SearchCallback(str_len, p_str, bd_addr);
+  }
+}
+
+void AddToNowPlayingCallback(uint8_t scope, uint8_t* uid, uint16_t uid_counter,
+                             const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetTargetObservers()) {
+    observer.AddToNowPlayingCallback(scope, uid, uid_counter, bd_addr);
+  }
+}
+
+void PassthroughRspCallback(const RawAddress& bd_addr, int id, int key_state) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.PassthroughRspCallback(bd_addr, id, key_state);
+  }
+}
+
+void GroupnavigationRspCallback(int id, int key_state) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.GroupnavigationRspCallback(id, key_state);
+  }
+}
+
+void ConnectionStateCallback(bool rc_connect, bool bt_connect,
+                             const RawAddress& bd_addr) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.ConnectionStateCallback(rc_connect, bt_connect, bd_addr);
+  }
+}
+
+void CtrlGetrcfeaturesCallback(const RawAddress& bd_addr, int features) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlGetrcfeaturesCallback(bd_addr, features);
+  }
+}
+
+void CtrlSetplayerapplicationsettingRspCallback(const RawAddress& bd_addr,
+                                                uint8_t accepted) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlSetplayerapplicationsettingRspCallback(bd_addr, accepted);
+  }
+}
+
+void CtrlPlayerapplicationsettingCallback(
+    const RawAddress& bd_addr, uint8_t num_attr,
+    btrc_player_app_attr_t* app_attrs, uint8_t num_ext_attr,
+    btrc_player_app_ext_attr_t* ext_attrs) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlPlayerapplicationsettingCallback(bd_addr, num_attr, app_attrs,
+                                                  num_ext_attr, ext_attrs);
+  }
+}
+
+void CtrlPlayerapplicationsettingChangedCallback(
+    const RawAddress& bd_addr, const btrc_player_settings_t& vals) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlPlayerapplicationsettingChangedCallback(bd_addr, vals);
+  }
+}
+
+void CtrlSetabsvolCmdCallback(const RawAddress& bd_addr, uint8_t abs_vol,
+                              uint8_t label) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlSetabsvolCmdCallback(bd_addr, abs_vol, label);
+  }
+}
+
+void CtrlRegisternotificationAbsVolCallback(const RawAddress& bd_addr,
+                                            uint8_t label) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlRegisternotificationAbsVolCallback(bd_addr, label);
+  }
+}
+
+void CtrlTrackChangedCallback(const RawAddress& bd_addr, uint8_t num_attr,
+                              btrc_element_attr_val_t* p_attrs) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlTrackChangedCallback(bd_addr, num_attr, p_attrs);
+  }
+}
+
+void CtrlPlayPositionChangedCallback(const RawAddress& bd_addr,
+                                     uint32_t song_len, uint32_t song_pos) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlPlayPositionChangedCallback(bd_addr, song_len, song_pos);
+  }
+}
+
+void CtrlPlayStatusChangedCallback(const RawAddress& bd_addr,
+                                   btrc_play_status_t play_status) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlPlayStatusChangedCallback(bd_addr, play_status);
+  }
+}
+
+void CtrlGetFolderItemsCallback(const RawAddress& bd_addr, btrc_status_t status,
+                                const btrc_folder_items_t* folder_items,
+                                uint8_t count) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlGetFolderItemsCallback(bd_addr, status, folder_items, count);
+  }
+}
+
+void CtrlChangePathCallback(const RawAddress& bd_addr, uint32_t count) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlChangePathCallback(bd_addr, count);
+  }
+}
+
+void CtrlSetBrowsedPlayerCallback(const RawAddress& bd_addr, uint8_t num_items,
+                                  uint8_t depth) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlSetBrowsedPlayerCallback(bd_addr, num_items, depth);
+  }
+}
+
+void CtrlSetAddressedPlayerCallback(const RawAddress& bd_addr, uint8_t status) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VLOG(2) << __func__;
+  VERIFY_INTERFACE_OR_RETURN();
+  for (auto& observer : *GetControlObservers()) {
+    observer.CtrlSetAddressedPlayerCallback(bd_addr, status);
+  }
+}
+
+btrc_callbacks_t target_callbacks = {
+    .size = sizeof(btrc_callbacks_t),
+    .remote_features_cb = RemoteFeaturesCallback,
+    .get_play_status_cb = GetPlayStatusCallback,
+    .list_player_app_attr_cb = ListPlayerAppAttrCallback,
+    .list_player_app_values_cb = ListPlayerAppValuesCallback,
+    .get_player_app_value_cb = GetPlayerAppValueCallback,
+    .get_player_app_attrs_text_cb = GetPlayerAppAttrsTextCallback,
+    .get_player_app_values_text_cb = GetPlayerAppValuesTextCallback,
+    .set_player_app_value_cb = SetPlayerAppValueCallback,
+    .get_element_attr_cb = GetElementAttrCallback,
+    .register_notification_cb = RegisterNotificationCallback,
+    .volume_change_cb = VolumeChangeCallback,
+    .passthrough_cmd_cb = PassthroughCmdCallback,
+    .set_addressed_player_cb = SetAddressedPlayerCallback,
+    .set_browsed_player_cb = SetBrowsedPlayerCallback,
+    .get_folder_items_cb = GetFolderItemsCallback,
+    .change_path_cb = ChangePathCallback,
+    .get_item_attr_cb = GetItemAttrCallback,
+    .play_item_cb = PlayItemCallback,
+    .get_total_num_of_items_cb = GetTotalNumOfItemsCallback,
+    .search_cb = SearchCallback,
+    .add_to_now_playing_cb = AddToNowPlayingCallback,
+};
+
+btrc_ctrl_callbacks_t control_callbacks = {
+    .size = sizeof(btrc_ctrl_callbacks_t),
+    .passthrough_rsp_cb = PassthroughRspCallback,
+    .groupnavigation_rsp_cb = GroupnavigationRspCallback,
+    .connection_state_cb = ConnectionStateCallback,
+    .getrcfeatures_cb = CtrlGetrcfeaturesCallback,
+    .setplayerappsetting_rsp_cb = CtrlSetplayerapplicationsettingRspCallback,
+    .playerapplicationsetting_cb = CtrlPlayerapplicationsettingCallback,
+    .playerapplicationsetting_changed_cb =
+        CtrlPlayerapplicationsettingChangedCallback,
+    .setabsvol_cmd_cb = CtrlSetabsvolCmdCallback,
+    .registernotification_absvol_cb = CtrlRegisternotificationAbsVolCallback,
+    .track_changed_cb = CtrlTrackChangedCallback,
+    .play_position_changed_cb = CtrlPlayPositionChangedCallback,
+    .play_status_changed_cb = CtrlPlayStatusChangedCallback,
+    .get_folder_items_cb = CtrlGetFolderItemsCallback,
+    .change_folder_path_cb = CtrlChangePathCallback,
+    .set_browsed_player_cb = CtrlSetBrowsedPlayerCallback,
+    .set_addressed_player_cb = CtrlSetAddressedPlayerCallback,
+};
+
+}  // namespace
+
+// BluetoothAvrcpInterface implementation for production.
+class BluetoothAvrcpInterfaceImpl : public BluetoothAvrcpInterface {
+ public:
+  BluetoothAvrcpInterfaceImpl() : control_iface_(nullptr) {}
+
+  ~BluetoothAvrcpInterfaceImpl() override {
+    if (control_iface_) control_iface_->cleanup();
+  }
+
+  bool AvrcpControlEnable() override {
+    if (control_enabled_) {
+      return true;
+    }
+
+    if (control_iface_->init(&control_callbacks) != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to initialize HAL AVRCP control interface";
+      return false;
+    }
+
+    control_enabled_ = true;
+    return true;
+  }
+
+  void AvrcpControlDisable() override {
+    if (!control_enabled_) {
+      return;
+    }
+
+    control_iface_->cleanup();
+    control_enabled_ = false;
+  }
+
+  bool AvrcpTargetEnable() override {
+    if (target_enabled_) {
+      return true;
+    }
+
+    if (target_iface_->init(&target_callbacks) != BT_STATUS_SUCCESS) {
+      LOG(ERROR) << "Failed to initialize HAL AVRCP target interface";
+      return false;
+    }
+
+    target_enabled_ = true;
+    return true;
+  }
+
+  void AvrcpTargetDisable() override {
+    if (!target_enabled_) {
+      return;
+    }
+
+    target_iface_->cleanup();
+    target_enabled_ = false;
+  }
+
+  void AddTargetObserver(TargetObserver* observer) override {
+    target_observers_.AddObserver(observer);
+  }
+
+  void RemoveTargetObserver(TargetObserver* observer) override {
+    target_observers_.RemoveObserver(observer);
+  }
+
+  void AddControlObserver(ControlObserver* observer) override {
+    control_observers_.AddObserver(observer);
+  }
+
+  void RemoveControlObserver(ControlObserver* observer) override {
+    control_observers_.RemoveObserver(observer);
+  }
+
+  const btrc_interface_t* GetTargetHALInterface() const override {
+    return target_iface_;
+  }
+
+  const btrc_ctrl_interface_t* GetControlHALInterface() const override {
+    return control_iface_;
+  }
+
+  // Initialize the interface.
+  bool Initialize() {
+    const bt_interface_t* bt_iface =
+        BluetoothInterface::Get()->GetHALInterface();
+    CHECK(bt_iface);
+
+    auto* target_iface = reinterpret_cast<const btrc_interface_t*>(
+        bt_iface->get_profile_interface(BT_PROFILE_AV_RC_ID));
+    if (!target_iface) {
+      LOG(ERROR) << "Failed to obtain HAL AVRCP target interface handle";
+      return false;
+    }
+
+    auto* control_iface = reinterpret_cast<const btrc_ctrl_interface_t*>(
+        bt_iface->get_profile_interface(BT_PROFILE_AV_RC_CTRL_ID));
+    if (!control_iface) {
+      LOG(ERROR) << "Failed to obtain HAL AVRCP control interface handle";
+      return false;
+    }
+
+    control_iface_ = control_iface;
+    target_iface_ = target_iface;
+
+    // Only initialize the control interface.
+    return AvrcpControlEnable();
+  }
+
+  base::ObserverList<TargetObserver>* target_observers() {
+    return &target_observers_;
+  }
+
+  base::ObserverList<ControlObserver>* control_observers() {
+    return &control_observers_;
+  }
+
+ private:
+  // List of observers that are interested in notifications from us.
+  // We're not using a base::ObserverListThreadSafe, which it posts observer
+  // events automatically on the origin threads, as we want to avoid that
+  // overhead and simply forward the events to the upper layer.
+  base::ObserverList<TargetObserver> target_observers_;
+  base::ObserverList<ControlObserver> control_observers_;
+
+  // The HAL handle obtained from the shared library. We hold a weak reference
+  // to this since the actual data resides in the shared Bluetooth library.
+  const btrc_interface_t* target_iface_ = nullptr;
+  const btrc_ctrl_interface_t* control_iface_ = nullptr;
+
+  bool control_enabled_ = false;
+  bool target_enabled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothAvrcpInterfaceImpl);
+};
+
+namespace {
+
+base::ObserverList<BluetoothAvrcpInterface::TargetObserver>*
+GetTargetObservers() {
+  CHECK(g_interface);
+  return static_cast<BluetoothAvrcpInterfaceImpl*>(g_interface)
+      ->target_observers();
+}
+
+base::ObserverList<BluetoothAvrcpInterface::ControlObserver>*
+GetControlObservers() {
+  CHECK(g_interface);
+  return static_cast<BluetoothAvrcpInterfaceImpl*>(g_interface)
+      ->control_observers();
+}
+
+}  // namespace
+
+void BluetoothAvrcpInterface::TargetObserver::RemoteFeaturesCallback(
+    const RawAddress& bd_addr, btrc_remote_features_t features) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetPlayStatusCallback(
+    const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::ListPlayerAppAttrCallback(
+    const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::ListPlayerAppValuesCallback(
+    btrc_player_attr_t attr_id, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetPlayerAppValueCallback(
+    uint8_t num_attr, btrc_player_attr_t* p_attrs, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetPlayerAppAttrsTextCallback(
+    uint8_t num_attr, btrc_player_attr_t* p_attrs, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetPlayerAppValuesTextCallback(
+    uint8_t attr_id, uint8_t num_val, uint8_t* p_vals,
+    const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::SetPlayerAppValueCallback(
+    btrc_player_settings_t* p_vals, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetElementAttrCallback(
+    uint8_t num_attr, btrc_media_attr_t* p_attrs, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::RegisterNotificationCallback(
+    btrc_event_id_t event_id, uint32_t param, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::VolumeChangeCallback(
+    uint8_t volume, uint8_t ctype, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::PassthroughCmdCallback(
+    int id, int key_state, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::SetAddressedPlayerCallback(
+    uint16_t player_id, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::SetBrowsedPlayerCallback(
+    uint16_t player_id, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetFolderItemsCallback(
+    uint8_t scope, uint32_t start_item, uint32_t end_item, uint8_t num_attr,
+    uint32_t* p_attr_ids, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::ChangePathCallback(
+    uint8_t direction, uint8_t* folder_uid, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetItemAttrCallback(
+    uint8_t scope, uint8_t* uid, uint16_t uid_counter, uint8_t num_attr,
+    btrc_media_attr_t* p_attrs, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::PlayItemCallback(
+    uint8_t scope, uint16_t uid_counter, uint8_t* uid,
+    const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::GetTotalNumOfItemsCallback(
+    uint8_t scope, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::SearchCallback(
+    uint16_t str_len, uint8_t* p_str, const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::TargetObserver::AddToNowPlayingCallback(
+    uint8_t scope, uint8_t* uid, uint16_t uid_counter,
+    const RawAddress& bd_addr) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::PassthroughRspCallback(
+    const RawAddress& /* bd_addr */, int /* id */, int /* key_state */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::GroupnavigationRspCallback(
+    int /* id */, int /* key_state */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::ConnectionStateCallback(
+    bool /* rc_connect */, bool /* bt_connect */,
+    const RawAddress& /* bd_addr */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlGetrcfeaturesCallback(
+    const RawAddress& /* bd_addr */, int /* features */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::
+    CtrlSetplayerapplicationsettingRspCallback(const RawAddress& /* bd_addr */,
+                                               uint8_t /* accepted */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::
+    CtrlPlayerapplicationsettingCallback(
+        const RawAddress& /* bd_addr */, uint8_t /* num_attr */,
+        btrc_player_app_attr_t* /* app_attrs */, uint8_t /* num_ext_attr */,
+        btrc_player_app_ext_attr_t* /* ext_attrs */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::
+    CtrlPlayerapplicationsettingChangedCallback(
+        const RawAddress& /* bd_addr*/,
+        const btrc_player_settings_t& /* vals */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlSetabsvolCmdCallback(
+    const RawAddress& /* bd_addr */, uint8_t /* abs_vol */,
+    uint8_t /* label */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::
+    CtrlRegisternotificationAbsVolCallback(const RawAddress& /* bd_addr */,
+                                           uint8_t /* label */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlTrackChangedCallback(
+    const RawAddress& /*bd_addr */, uint8_t /* num_attr */,
+    btrc_element_attr_val_t* /* p_attrs */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlPlayPositionChangedCallback(
+    const RawAddress& /* bd_addr */, uint32_t /* song_len */,
+    uint32_t /* song_pos */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlPlayStatusChangedCallback(
+    const RawAddress& /* bd_addr */, btrc_play_status_t /* play_status */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlGetFolderItemsCallback(
+    const RawAddress& /* bd_addr */, btrc_status_t /* status */,
+    const btrc_folder_items_t* /*folder_items */, uint8_t /* count */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlChangePathCallback(
+    const RawAddress& /* bd_addr */, uint32_t /* count */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlSetBrowsedPlayerCallback(
+    const RawAddress& /* bd_addr */, uint8_t /* num_items */,
+    uint8_t /* depth */) {
+  // Do nothing.
+}
+
+void BluetoothAvrcpInterface::ControlObserver::CtrlSetAddressedPlayerCallback(
+    const RawAddress& /* bd_addr */, uint8_t /* status */) {
+  // Do nothing.
+}
+
+// static
+bool BluetoothAvrcpInterface::Initialize() {
+  unique_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(!g_interface);
+
+  std::unique_ptr<BluetoothAvrcpInterfaceImpl> impl(
+      new BluetoothAvrcpInterfaceImpl());
+  if (!impl->Initialize()) {
+    LOG(ERROR) << "Failed to initialize BluetoothAvrcpInterface";
+    return false;
+  }
+
+  g_interface = impl.release();
+
+  return true;
+}
+
+// static
+void BluetoothAvrcpInterface::CleanUp() {
+  unique_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(g_interface);
+
+  delete g_interface;
+  g_interface = nullptr;
+}
+
+// static
+bool BluetoothAvrcpInterface::IsInitialized() {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+
+  return g_interface != nullptr;
+}
+
+// static
+BluetoothAvrcpInterface* BluetoothAvrcpInterface::Get() {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(g_interface);
+  return g_interface;
+}
+
+// static
+void BluetoothAvrcpInterface::InitializeForTesting(
+    BluetoothAvrcpInterface* test_instance) {
+  unique_lock<shared_mutex_impl> lock(g_instance_lock);
+  CHECK(test_instance);
+  CHECK(!g_interface);
+
+  g_interface = test_instance;
+}
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/service/hal/bluetooth_avrcp_interface.h b/service/hal/bluetooth_avrcp_interface.h
new file mode 100644
index 0000000..08d6989
--- /dev/null
+++ b/service/hal/bluetooth_avrcp_interface.h
@@ -0,0 +1,179 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <base/macros.h>
+#include <hardware/bluetooth.h>
+#include <hardware/bt_rc.h>
+
+namespace bluetooth {
+namespace hal {
+
+class BluetoothAvrcpInterface {
+ public:
+  // The HAL interface doesn't allow registering "user data" that carries
+  // context beyond the callback parameters, forcing implementations to deal
+  // with global variables. The *Observer interface is to redirect these events
+  // to interested parties in an object-oriented manner.
+  class TargetObserver {
+   public:
+    virtual ~TargetObserver() = default;
+
+    virtual void RemoteFeaturesCallback(const RawAddress& bd_addr,
+                                        btrc_remote_features_t features);
+    virtual void GetPlayStatusCallback(const RawAddress& bd_addr);
+    virtual void ListPlayerAppAttrCallback(const RawAddress& bd_addr);
+    virtual void ListPlayerAppValuesCallback(btrc_player_attr_t attr_id,
+                                             const RawAddress& bd_addr);
+    virtual void GetPlayerAppValueCallback(uint8_t num_attr,
+                                           btrc_player_attr_t* p_attrs,
+                                           const RawAddress& bd_addr);
+    virtual void GetPlayerAppAttrsTextCallback(uint8_t num_attr,
+                                               btrc_player_attr_t* p_attrs,
+                                               const RawAddress& bd_addr);
+    virtual void GetPlayerAppValuesTextCallback(uint8_t attr_id,
+                                                uint8_t num_val,
+                                                uint8_t* p_vals,
+                                                const RawAddress& bd_addr);
+    virtual void SetPlayerAppValueCallback(btrc_player_settings_t* p_vals,
+                                           const RawAddress& bd_addr);
+    virtual void GetElementAttrCallback(uint8_t num_attr,
+                                        btrc_media_attr_t* p_attrs,
+                                        const RawAddress& bd_addr);
+    virtual void RegisterNotificationCallback(btrc_event_id_t event_id,
+                                              uint32_t param,
+                                              const RawAddress& bd_addr);
+    virtual void VolumeChangeCallback(uint8_t volume, uint8_t ctype,
+                                      const RawAddress& bd_addr);
+    virtual void PassthroughCmdCallback(int id, int key_state,
+                                        const RawAddress& bd_addr);
+    virtual void SetAddressedPlayerCallback(uint16_t player_id,
+                                            const RawAddress& bd_addr);
+    virtual void SetBrowsedPlayerCallback(uint16_t player_id,
+                                          const RawAddress& bd_addr);
+    virtual void GetFolderItemsCallback(uint8_t scope, uint32_t start_item,
+                                        uint32_t end_item, uint8_t num_attr,
+                                        uint32_t* p_attr_ids,
+                                        const RawAddress& bd_addr);
+    virtual void ChangePathCallback(uint8_t direction, uint8_t* folder_uid,
+                                    const RawAddress& bd_addr);
+    virtual void GetItemAttrCallback(uint8_t scope, uint8_t* uid,
+                                     uint16_t uid_counter, uint8_t num_attr,
+                                     btrc_media_attr_t* p_attrs,
+                                     const RawAddress& bd_addr);
+    virtual void PlayItemCallback(uint8_t scope, uint16_t uid_counter,
+                                  uint8_t* uid, const RawAddress& bd_addr);
+    virtual void GetTotalNumOfItemsCallback(uint8_t scope,
+                                            const RawAddress& bd_addr);
+    virtual void SearchCallback(uint16_t str_len, uint8_t* p_str,
+                                const RawAddress& bd_addr);
+    virtual void AddToNowPlayingCallback(uint8_t scope, uint8_t* uid,
+                                         uint16_t uid_counter,
+                                         const RawAddress& bd_addr);
+  };
+
+  class ControlObserver {
+   public:
+    virtual ~ControlObserver() = default;
+
+    virtual void PassthroughRspCallback(const RawAddress& bd_addr, int id,
+                                        int key_state);
+    virtual void GroupnavigationRspCallback(int id, int key_state);
+    virtual void ConnectionStateCallback(bool rc_connect, bool bt_connect,
+                                         const RawAddress& bd_addr);
+    virtual void CtrlGetrcfeaturesCallback(const RawAddress& bd_addr,
+                                           int features);
+    virtual void CtrlSetplayerapplicationsettingRspCallback(
+        const RawAddress& bd_addr, uint8_t accepted);
+    virtual void CtrlPlayerapplicationsettingCallback(
+        const RawAddress& bd_addr, uint8_t num_attr,
+        btrc_player_app_attr_t* app_attrs, uint8_t num_ext_attr,
+        btrc_player_app_ext_attr_t* ext_attrs);
+    virtual void CtrlPlayerapplicationsettingChangedCallback(
+        const RawAddress& bd_addr, const btrc_player_settings_t& p_vals);
+    virtual void CtrlSetabsvolCmdCallback(const RawAddress& bd_addr,
+                                          uint8_t abs_vol, uint8_t label);
+    virtual void CtrlRegisternotificationAbsVolCallback(
+        const RawAddress& bd_addr, uint8_t label);
+    virtual void CtrlTrackChangedCallback(const RawAddress& bd_addr,
+                                          uint8_t num_attr,
+                                          btrc_element_attr_val_t* p_attrs);
+    virtual void CtrlPlayPositionChangedCallback(const RawAddress& bd_addr,
+                                                 uint32_t song_len,
+                                                 uint32_t song_pos);
+    virtual void CtrlPlayStatusChangedCallback(const RawAddress& bd_addr,
+                                               btrc_play_status_t play_status);
+    virtual void CtrlGetFolderItemsCallback(
+        const RawAddress& bd_addr, btrc_status_t status,
+        const btrc_folder_items_t* folder_items, uint8_t count);
+    virtual void CtrlChangePathCallback(const RawAddress& bd_addr,
+                                        uint32_t count);
+    virtual void CtrlSetBrowsedPlayerCallback(const RawAddress& bd_addr,
+                                              uint8_t num_items, uint8_t depth);
+    virtual void CtrlSetAddressedPlayerCallback(const RawAddress& bd_addr,
+                                                uint8_t status);
+  };
+
+  // Initialize and clean up the BluetoothInterface singleton. Returns false if
+  // the underlying HAL interface failed to initialize, and true on success.
+  static bool Initialize();
+
+  // Shuts down and cleans up the interface. CleanUp must be called on the same
+  // thread that called Initialize.
+  static void CleanUp();
+
+  // Returns true if the interface was initialized and a global singleton has
+  // been created.
+  static bool IsInitialized();
+
+  // Initialize for testing. Use this to inject a test version of
+  // BluetoothAvrcpInterface. To be used from unit tests only.
+  static void InitializeForTesting(BluetoothAvrcpInterface* test_instance);
+
+  // Returns the BluetoothAvrcpInterface singleton. If the interface has
+  // not been initialized, returns nullptr. This method is thread-safe, in that
+  // it will block if the internal lock is being held by another thread. Don't
+  // call this re-entrantly from an observer event as this may cause a deadlock.
+  static BluetoothAvrcpInterface* Get();
+
+  virtual bool AvrcpControlEnable() = 0;
+  virtual void AvrcpControlDisable() = 0;
+  virtual bool AvrcpTargetEnable() = 0;
+  virtual void AvrcpTargetDisable() = 0;
+
+  // Thread-safety is guaranteed by ObserverList.
+  virtual void AddTargetObserver(TargetObserver* observer) = 0;
+  virtual void RemoveTargetObserver(TargetObserver* observer) = 0;
+
+  // Thread-safety is guaranteed by ObserverList.
+  virtual void AddControlObserver(ControlObserver* observer) = 0;
+  virtual void RemoveControlObserver(ControlObserver* observer) = 0;
+
+  // The HAL module pointers provided by the shared Bluetooth library
+  virtual const btrc_interface_t* GetTargetHALInterface() const = 0;
+  virtual const btrc_ctrl_interface_t* GetControlHALInterface() const = 0;
+
+ protected:
+  BluetoothAvrcpInterface() = default;
+  virtual ~BluetoothAvrcpInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(BluetoothAvrcpInterface);
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/service/hal/bluetooth_interface.cc b/service/hal/bluetooth_interface.cc
index 9296966..a08fb7b 100644
--- a/service/hal/bluetooth_interface.cc
+++ b/service/hal/bluetooth_interface.cc
@@ -98,6 +98,13 @@
       status, remote_bd_addr, num_properties, properties));
 }
 
+void DeviceFoundCallback(int num_properties, bt_property_t* properties) {
+  shared_lock<shared_mutex_impl> lock(g_instance_lock);
+  VERIFY_INTERFACE_OR_RETURN();
+  VLOG(1) << " Device found.";
+  FOR_EACH_BLUETOOTH_OBSERVER(DeviceFoundCallback(num_properties, properties));
+}
+
 void DiscoveryStateChangedCallback(bt_discovery_state_t state) {
   shared_lock<shared_mutex_impl> lock(g_instance_lock);
   VERIFY_INTERFACE_OR_RETURN();
@@ -190,7 +197,7 @@
     AdapterStateChangedCallback,
     AdapterPropertiesCallback,
     RemoteDevicePropertiesCallback,
-    nullptr, /* device_found_cb */
+    DeviceFoundCallback,
     DiscoveryStateChangedCallback,
     PinRequestCallback,
     SSPRequestCallback,
@@ -309,6 +316,11 @@
   // Do nothing.
 }
 
+void BluetoothInterface::Observer::DeviceFoundCallback(
+    int /* num_properties */, bt_property_t* /* properties */) {
+  // Do nothing.
+}
+
 void BluetoothInterface::Observer::DiscoveryStateChangedCallback(
     bt_discovery_state_t /* state */) {
   // Do nothing.
diff --git a/service/hal/bluetooth_interface.h b/service/hal/bluetooth_interface.h
index 897835f..a1c8e62 100644
--- a/service/hal/bluetooth_interface.h
+++ b/service/hal/bluetooth_interface.h
@@ -57,6 +57,8 @@
                                                 RawAddress* remote_bd_addr,
                                                 int num_properties,
                                                 bt_property_t* properties);
+    virtual void DeviceFoundCallback(int num_properties,
+                                     bt_property_t* properties);
     virtual void DiscoveryStateChangedCallback(bt_discovery_state_t state);
     virtual void PinRequestCallback(RawAddress* remote_bd_addr,
                                     bt_bdname_t* bd_name, uint32_t cod,
diff --git a/service/hal/fake_bluetooth_av_interface.cc b/service/hal/fake_bluetooth_av_interface.cc
new file mode 100644
index 0000000..39a6331
--- /dev/null
+++ b/service/hal/fake_bluetooth_av_interface.cc
@@ -0,0 +1,151 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "service/hal/fake_bluetooth_av_interface.h"
+
+namespace bluetooth {
+namespace hal {
+namespace {
+
+// The global test handler instances. We have to have globals since the HAL
+// interface methods all have to be global and their signatures don't allow us
+// to pass in user_data.
+std::shared_ptr<FakeBluetoothAvInterface::TestA2dpSinkHandler>
+    g_a2dp_sink_handler;
+
+bt_status_t FakeInit(btav_sink_callbacks_t* callbacks) {
+  return BT_STATUS_SUCCESS;
+}
+
+bt_status_t FakeConnect(const RawAddress& bd_addr) {
+  if (g_a2dp_sink_handler) return g_a2dp_sink_handler->Connect(bd_addr);
+  return BT_STATUS_FAIL;
+}
+
+bt_status_t FakeDisconnect(const RawAddress& bd_addr) {
+  if (g_a2dp_sink_handler) return g_a2dp_sink_handler->Disconnect(bd_addr);
+  return BT_STATUS_FAIL;
+}
+
+void FakeCleanup(void) {}
+
+void FakeSetAudioFocusState(int focus_state) {
+  if (g_a2dp_sink_handler)
+    return g_a2dp_sink_handler->SetAudioFocusState(focus_state);
+}
+
+void FakeSetAudioTrackGain(float gain) {
+  if (g_a2dp_sink_handler) return g_a2dp_sink_handler->SetAudioTrackGain(gain);
+}
+
+btav_source_interface_t fake_a2dp_source_interface = {
+    .size = sizeof(btav_source_interface_t),
+    .init = nullptr,
+    .connect = nullptr,
+    .disconnect = nullptr,
+    .config_codec = nullptr,
+    .cleanup = nullptr,
+};
+
+btav_sink_interface_t fake_a2dp_sink_interface = {
+    .size = sizeof(btav_sink_interface_t),
+    .init = FakeInit,
+    .connect = FakeConnect,
+    .disconnect = FakeDisconnect,
+    .cleanup = FakeCleanup,
+    .set_audio_focus_state = FakeSetAudioFocusState,
+    .set_audio_track_gain = FakeSetAudioTrackGain,
+};
+
+}  // namespace
+
+FakeBluetoothAvInterface::FakeBluetoothAvInterface(
+    std::shared_ptr<TestA2dpSinkHandler> a2dp_sink_handler) {
+  CHECK(!g_a2dp_sink_handler);
+
+  if (a2dp_sink_handler) g_a2dp_sink_handler = a2dp_sink_handler;
+}
+
+FakeBluetoothAvInterface::~FakeBluetoothAvInterface() {
+  g_a2dp_sink_handler = nullptr;
+}
+
+void FakeBluetoothAvInterface::NotifyConnectionState(
+    const RawAddress& bda, btav_connection_state_t state) {
+  for (auto& observer : a2dp_sink_observers_) {
+    observer.ConnectionStateCallback(this, bda, state);
+  }
+}
+void FakeBluetoothAvInterface::NotifyAudioState(const RawAddress& bda,
+                                                btav_audio_state_t state) {
+  for (auto& observer : a2dp_sink_observers_) {
+    observer.AudioStateCallback(this, bda, state);
+  }
+}
+void FakeBluetoothAvInterface::NotifyAudioConfig(const RawAddress& bda,
+                                                 uint32_t sample_rate,
+                                                 uint8_t channel_count) {
+  for (auto& observer : a2dp_sink_observers_) {
+    observer.AudioConfigCallback(this, bda, sample_rate, channel_count);
+  }
+}
+
+bool FakeBluetoothAvInterface::A2dpSourceEnable(
+    std::vector<btav_a2dp_codec_config_t> codec_priorities) {
+  return true;
+}
+
+void FakeBluetoothAvInterface::A2dpSourceDisable() {}
+
+bool FakeBluetoothAvInterface::A2dpSinkEnable() { return true; }
+
+void FakeBluetoothAvInterface::A2dpSinkDisable() {}
+
+void FakeBluetoothAvInterface::AddA2dpSourceObserver(
+    A2dpSourceObserver* observer) {
+  CHECK(observer);
+  a2dp_source_observers_.AddObserver(observer);
+}
+
+void FakeBluetoothAvInterface::RemoveA2dpSourceObserver(
+    A2dpSourceObserver* observer) {
+  CHECK(observer);
+  a2dp_source_observers_.RemoveObserver(observer);
+}
+
+void FakeBluetoothAvInterface::AddA2dpSinkObserver(A2dpSinkObserver* observer) {
+  CHECK(observer);
+  a2dp_sink_observers_.AddObserver(observer);
+}
+
+void FakeBluetoothAvInterface::RemoveA2dpSinkObserver(
+    A2dpSinkObserver* observer) {
+  CHECK(observer);
+  a2dp_sink_observers_.RemoveObserver(observer);
+}
+
+const btav_source_interface_t*
+FakeBluetoothAvInterface::GetA2dpSourceHALInterface() {
+  return &fake_a2dp_source_interface;
+}
+
+const btav_sink_interface_t*
+FakeBluetoothAvInterface::GetA2dpSinkHALInterface() {
+  return &fake_a2dp_sink_interface;
+}
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/service/hal/fake_bluetooth_av_interface.h b/service/hal/fake_bluetooth_av_interface.h
new file mode 100644
index 0000000..c979434
--- /dev/null
+++ b/service/hal/fake_bluetooth_av_interface.h
@@ -0,0 +1,82 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <base/macros.h>
+#include <base/observer_list.h>
+
+#include "service/hal/bluetooth_av_interface.h"
+
+namespace bluetooth {
+namespace hal {
+
+class FakeBluetoothAvInterface : public BluetoothAvInterface {
+ public:
+  // Handles HAL Bluetooth A2DP sink API calls for testing. Test code can
+  // provide a fake or mock implementation of this and all calls will be routed
+  // to it.
+  class TestA2dpSinkHandler {
+   public:
+    virtual bt_status_t Connect(RawAddress bda) = 0;
+    virtual bt_status_t Disconnect(RawAddress bda) = 0;
+    virtual void SetAudioFocusState(int focus_state) = 0;
+    virtual void SetAudioTrackGain(float gain) = 0;
+
+   protected:
+    virtual ~TestA2dpSinkHandler() = default;
+  };
+
+  // Constructs the fake with the given handlers. Implementations can
+  // provide their own handlers or simply pass "nullptr" for the default
+  // behavior in which BT_STATUS_FAIL will be returned from all calls.
+  FakeBluetoothAvInterface(
+      std::shared_ptr<TestA2dpSinkHandler> a2dp_sink_handler);
+  ~FakeBluetoothAvInterface();
+
+  // The methods below can be used to notify observers with certain events and
+  // given parameters.
+
+  // A2DP sink callbacks
+  void NotifyConnectionState(const RawAddress& bda,
+                             btav_connection_state_t state);
+  void NotifyAudioState(const RawAddress& bda, btav_audio_state_t state);
+  void NotifyAudioConfig(const RawAddress& bda, uint32_t sample_rate,
+                         uint8_t channel_count);
+
+  // BluetoothAvInterface overrides:
+  bool A2dpSourceEnable(
+      std::vector<btav_a2dp_codec_config_t> codec_priorities) override;
+  void A2dpSourceDisable() override;
+  bool A2dpSinkEnable() override;
+  void A2dpSinkDisable() override;
+  void AddA2dpSourceObserver(A2dpSourceObserver* observer) override;
+  void RemoveA2dpSourceObserver(A2dpSourceObserver* observer) override;
+  void AddA2dpSinkObserver(A2dpSinkObserver* observer) override;
+  void RemoveA2dpSinkObserver(A2dpSinkObserver* observer) override;
+  const btav_source_interface_t* GetA2dpSourceHALInterface() override;
+  const btav_sink_interface_t* GetA2dpSinkHALInterface() override;
+
+ private:
+  base::ObserverList<A2dpSourceObserver> a2dp_source_observers_;
+  base::ObserverList<A2dpSinkObserver> a2dp_sink_observers_;
+  std::shared_ptr<TestA2dpSinkHandler> scanner_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeBluetoothAvInterface);
+};
+
+}  // namespace hal
+}  // namespace bluetooth
diff --git a/service/ipc/binder/bluetooth_a2dp_sink_binder_server.cc b/service/ipc/binder/bluetooth_a2dp_sink_binder_server.cc
new file mode 100644
index 0000000..1609aa8
--- /dev/null
+++ b/service/ipc/binder/bluetooth_a2dp_sink_binder_server.cc
@@ -0,0 +1,222 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "service/ipc/binder/bluetooth_a2dp_sink_binder_server.h"
+
+#include <base/logging.h>
+
+#include "service/adapter.h"
+
+using android::String16;
+using android::String8;
+using android::binder::Status;
+
+using android::bluetooth::IBluetoothA2dpSinkCallback;
+
+namespace ipc {
+namespace binder {
+
+BluetoothA2dpSinkBinderServer::BluetoothA2dpSinkBinderServer(
+    bluetooth::Adapter* adapter)
+    : adapter_(adapter) {
+  CHECK(adapter);
+}
+
+Status BluetoothA2dpSinkBinderServer::Register(
+    const android::sp<IBluetoothA2dpSinkCallback>& callback,
+    bool* _aidl_return) {
+  auto factory = adapter_->GetA2dpSinkFactory();
+  *_aidl_return = RegisterInstanceBase(callback, factory);
+  return Status::ok();
+}
+
+Status BluetoothA2dpSinkBinderServer::Unregister() {
+  UnregisterAllBase();
+  return Status::ok();
+}
+
+Status BluetoothA2dpSinkBinderServer::Enable(bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto a2dp_sink = GetA2dpSink();
+  if (!a2dp_sink) {
+    LOG(ERROR) << "Failed to get A2DP sink instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  if (!a2dp_sink->Enable()) {
+    LOG(ERROR) << "Failed to enable";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+Status BluetoothA2dpSinkBinderServer::Disable(bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto a2dp_sink = GetA2dpSink();
+  if (!a2dp_sink) {
+    LOG(ERROR) << "Failed to get A2DP sink instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  a2dp_sink->Disable();
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+Status BluetoothA2dpSinkBinderServer::Connect(const String16& device_address,
+                                              bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto a2dp_sink = GetA2dpSink();
+  if (!a2dp_sink) {
+    LOG(ERROR) << "Failed to get A2DP sink instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  if (!a2dp_sink->Connect(String8(device_address).string())) {
+    LOG(ERROR) << "Failed to connect";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+Status BluetoothA2dpSinkBinderServer::Disconnect(const String16& device_address,
+                                                 bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto a2dp_sink = GetA2dpSink();
+  if (!a2dp_sink) {
+    LOG(ERROR) << "Failed to get A2DP sink instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  if (!a2dp_sink->Disconnect(String8(device_address).string())) {
+    LOG(ERROR) << "Failed to connect";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+Status BluetoothA2dpSinkBinderServer::SetAudioFocusState(int focus_state,
+                                                         bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto a2dp_sink = GetA2dpSink();
+  if (!a2dp_sink) {
+    LOG(ERROR) << "Failed to get A2DP instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  a2dp_sink->SetAudioFocusState(focus_state);
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+Status BluetoothA2dpSinkBinderServer::SetAudioTrackGain(float gain,
+                                                        bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto a2dp_sink = GetA2dpSink();
+  if (!a2dp_sink) {
+    LOG(ERROR) << "Failed to get A2DP instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  a2dp_sink->SetAudioTrackGain(gain);
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+void BluetoothA2dpSinkBinderServer::OnConnectionState(
+    const std::string& device_address, int state) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto cb = GetA2dpSinkCallback();
+  if (!cb.get()) {
+    LOG(WARNING) << "Callback for this GattServer was deleted.";
+    return;
+  }
+
+  cb->OnConnectionState(String16(device_address.c_str()), state);
+}
+
+void BluetoothA2dpSinkBinderServer::OnAudioState(
+    const std::string& device_address, int state) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto cb = GetA2dpSinkCallback();
+  if (!cb.get()) {
+    LOG(WARNING) << "Callback for this GattServer was deleted.";
+    return;
+  }
+
+  cb->OnAudioState(String16(device_address.c_str()), state);
+}
+
+void BluetoothA2dpSinkBinderServer::OnAudioConfig(
+    const std::string& device_address, uint32_t sample_rate,
+    uint8_t channel_count) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto cb = GetA2dpSinkCallback();
+  if (!cb.get()) {
+    LOG(WARNING) << "Callback for this GattServer was deleted.";
+    return;
+  }
+
+  cb->OnAudioConfig(String16(device_address.c_str()), sample_rate,
+                    channel_count);
+}
+
+bool BluetoothA2dpSinkBinderServer::HasInstance() {
+  return GetA2dpSink() != nullptr;
+}
+
+android::sp<IBluetoothA2dpSinkCallback>
+BluetoothA2dpSinkBinderServer::GetA2dpSinkCallback() {
+  auto cb = GetCallback(bluetooth::A2dpSink::kSingletonInstanceId);
+  return android::sp<IBluetoothA2dpSinkCallback>(
+      static_cast<IBluetoothA2dpSinkCallback*>(cb.get()));
+}
+
+std::shared_ptr<bluetooth::A2dpSink>
+BluetoothA2dpSinkBinderServer::GetA2dpSink() {
+  return std::static_pointer_cast<bluetooth::A2dpSink>(
+      GetInstance(bluetooth::A2dpSink::kSingletonInstanceId));
+}
+
+void BluetoothA2dpSinkBinderServer::OnRegisterInstanceImpl(
+    bluetooth::BLEStatus status, android::sp<IInterface> callback,
+    bluetooth::BluetoothInstance* instance) {
+  VLOG(1) << __func__ << " instance ID: " << instance->GetInstanceId()
+          << " status: " << status;
+  bluetooth::A2dpSink* a2dp_sink = static_cast<bluetooth::A2dpSink*>(instance);
+  a2dp_sink->SetDelegate(this);
+
+  android::sp<IBluetoothA2dpSinkCallback> cb(
+      static_cast<IBluetoothA2dpSinkCallback*>(callback.get()));
+  cb->OnRegistered(status);
+}
+
+}  // namespace binder
+}  // namespace ipc
diff --git a/service/ipc/binder/bluetooth_a2dp_sink_binder_server.h b/service/ipc/binder/bluetooth_a2dp_sink_binder_server.h
new file mode 100644
index 0000000..e18af54
--- /dev/null
+++ b/service/ipc/binder/bluetooth_a2dp_sink_binder_server.h
@@ -0,0 +1,86 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <map>
+#include <string>
+
+#include <base/macros.h>
+
+#include <android/bluetooth/BnBluetoothA2dpSink.h>
+#include <android/bluetooth/IBluetoothA2dpSinkCallback.h>
+
+#include "service/a2dp_sink.h"
+#include "service/ipc/binder/interface_with_instances_base.h"
+
+namespace bluetooth {
+class Adapter;
+}  // namespace bluetooth
+
+namespace ipc {
+namespace binder {
+
+class BluetoothA2dpSinkBinderServer
+    : public InterfaceWithInstancesBase,
+      public android::bluetooth::BnBluetoothA2dpSink,
+      public bluetooth::A2dpSink::Delegate {
+ public:
+  explicit BluetoothA2dpSinkBinderServer(bluetooth::Adapter* adapter);
+  ~BluetoothA2dpSinkBinderServer() override = default;
+
+  // IBluetoothA2dpSink implementation:
+  android::binder::Status Register(
+      const android::sp<android::bluetooth::IBluetoothA2dpSinkCallback>&
+          callback,
+      bool* _aidl_return) override;
+  android::binder::Status Enable(bool* _aidl_return) override;
+  android::binder::Status Disable(bool* _aidl_return) override;
+  android::binder::Status Unregister() override;
+  android::binder::Status Connect(const android::String16& device_address,
+                                  bool* _aidl_return) override;
+  android::binder::Status Disconnect(const android::String16& device_address,
+                                     bool* _aidl_return) override;
+  android::binder::Status SetAudioFocusState(int state,
+                                             bool* _aidl_return) override;
+  android::binder::Status SetAudioTrackGain(float gain,
+                                            bool* _aidl_return) override;
+
+  // bluetooth::bluetooth::A2dpSink::Delegate implementation:
+  void OnConnectionState(const std::string& device_address, int state) override;
+  void OnAudioState(const std::string& device_address, int state) override;
+  void OnAudioConfig(const std::string& device_address, uint32_t sample_rate,
+                     uint8_t channel_count) override;
+
+  bool HasInstance();
+
+ private:
+  android::sp<android::bluetooth::IBluetoothA2dpSinkCallback>
+  GetA2dpSinkCallback();
+  std::shared_ptr<bluetooth::A2dpSink> GetA2dpSink();
+
+  // InterfaceWithInstancesBase override:
+  void OnRegisterInstanceImpl(bluetooth::BLEStatus status,
+                              android::sp<IInterface> callback,
+                              bluetooth::BluetoothInstance* instance) override;
+
+  bluetooth::Adapter* adapter_;  // weak
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothA2dpSinkBinderServer);
+};
+
+}  // namespace binder
+}  // namespace ipc
diff --git a/service/ipc/binder/bluetooth_avrcp_control_binder_server.cc b/service/ipc/binder/bluetooth_avrcp_control_binder_server.cc
new file mode 100644
index 0000000..d816859
--- /dev/null
+++ b/service/ipc/binder/bluetooth_avrcp_control_binder_server.cc
@@ -0,0 +1,236 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 "service/ipc/binder/bluetooth_avrcp_control_binder_server.h"
+
+#include <string>
+
+#include "base/logging.h"
+
+#include "service/adapter.h"
+
+using android::String16;
+using android::String8;
+using android::binder::Status;
+using android::bluetooth::IBluetoothAvrcpControlCallback;
+
+namespace ipc {
+namespace binder {
+
+namespace {
+const int kInvalidInstanceId = -1;
+}  // namespace
+
+BluetoothAvrcpControlBinderServer::BluetoothAvrcpControlBinderServer(
+    bluetooth::Adapter* adapter)
+    : adapter_(adapter) {
+  CHECK(adapter_);
+}
+
+Status BluetoothAvrcpControlBinderServer::Register(
+    const android::sp<IBluetoothAvrcpControlCallback>& callback,
+    bool* _aidl_return) {
+  VLOG(2) << __func__;
+
+  bluetooth::AvrcpControlFactory* gatt_client_factory =
+      adapter_->GetAvrcpControlFactory();
+
+  *_aidl_return = RegisterInstanceBase(callback, gatt_client_factory);
+  return Status::ok();
+}
+
+Status BluetoothAvrcpControlBinderServer::Unregister(int32_t id) {
+  VLOG(2) << __func__;
+  UnregisterInstanceBase(id);
+  return Status::ok();
+}
+
+Status BluetoothAvrcpControlBinderServer::UnregisterAll() {
+  VLOG(2) << __func__;
+  UnregisterAllBase();
+  return Status::ok();
+}
+
+Status BluetoothAvrcpControlBinderServer::Enable(int32_t id,
+                                                 bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto avrcp_control = GetAvrcpControl(id);
+  if (!avrcp_control) {
+    LOG(ERROR) << "Failed to get avrcp control instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  if (!avrcp_control->Enable()) {
+    LOG(ERROR) << "Failed to enable";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+Status BluetoothAvrcpControlBinderServer::Disable(int32_t id,
+                                                  bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto avrcp_control = GetAvrcpControl(id);
+  if (!avrcp_control) {
+    LOG(ERROR) << "Failed to get avrcp control instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  avrcp_control->Disable();
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+Status BluetoothAvrcpControlBinderServer::SendPassThroughCommand(
+    int32_t id, const String16& device_address, int32_t key_code,
+    bool key_pressed, bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto avrcp_control = GetAvrcpControl(id);
+  if (!avrcp_control) {
+    LOG(ERROR) << "Failed to get avrcp control instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  if (!avrcp_control->SendPassThroughCommand(String8(device_address).string(),
+                                             key_code, key_pressed)) {
+    LOG(ERROR) << "Failed to send pass through command";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+android::binder::Status BluetoothAvrcpControlBinderServer::SetAbsVolumeResponse(
+    int32_t id, const android::String16& device_address, int32_t abs_vol,
+    int32_t label, bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto avrcp_control = GetAvrcpControl(id);
+  if (!avrcp_control) {
+    LOG(ERROR) << "Failed to get avrcp control instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  if (!avrcp_control->SetAbsVolumeResponse(String8(device_address).string(),
+                                           abs_vol, label)) {
+    LOG(ERROR) << "Failed to send set absolute volume response";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+android::binder::Status
+BluetoothAvrcpControlBinderServer::RegisterForAbsVolumeCallbackResponse(
+    int32_t id, const android::String16& device_address, int32_t response_type,
+    int32_t abs_vol, int32_t label, bool* _aidl_return) {
+  std::lock_guard<std::mutex> lock(*maps_lock());
+  auto avrcp_control = GetAvrcpControl(id);
+  if (!avrcp_control) {
+    LOG(ERROR) << "Failed to get avrcp control instance";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+
+  if (!avrcp_control->RegisterForAbsVolumeCallbackResponse(
+          String8(device_address).string(), response_type, abs_vol, label)) {
+    LOG(ERROR)
+        << "Failed to send register for absolute volume callback response";
+    *_aidl_return = false;
+    return Status::ok();
+  }
+  *_aidl_return = true;
+  return Status::ok();
+}
+
+void BluetoothAvrcpControlBinderServer::OnConnectionState(
+    bool rc_connect, bool bt_connect, const std::string& device_address) {
+  auto func = [rc_connect, bt_connect, &device_address](IInterface* cb) {
+    auto avrcp_control_cb = static_cast<IBluetoothAvrcpControlCallback*>(cb);
+    avrcp_control_cb->OnConnectionState(rc_connect, bt_connect,
+                                        String16(device_address.c_str()));
+  };
+
+  ForEachCallback(func);
+}
+
+void BluetoothAvrcpControlBinderServer::OnTrackChanged(
+    const std::string& device_address, const bluetooth::AvrcpMediaAttr& attr) {
+  auto binder_attr = android::bluetooth::BluetoothAvrcpMediaAttr(attr);
+
+  auto func = [&device_address, &binder_attr](IInterface* cb) {
+    auto avrcp_control_cb = static_cast<IBluetoothAvrcpControlCallback*>(cb);
+    avrcp_control_cb->OnTrackChanged(String16(device_address.c_str()),
+                                     binder_attr);
+  };
+
+  ForEachCallback(func);
+}
+
+void BluetoothAvrcpControlBinderServer::OnSetAbsVolumeRequest(
+    const std::string& device_address, int32_t abs_vol, int32_t label) {
+  auto addr_s16 = String16(device_address.c_str(), device_address.size());
+  auto func = [&addr_s16, abs_vol, label](IInterface* cb) {
+    auto avrcp_control_cb = static_cast<IBluetoothAvrcpControlCallback*>(cb);
+    avrcp_control_cb->OnSetAbsVolumeRequest(addr_s16, abs_vol, label);
+  };
+
+  ForEachCallback(func);
+}
+
+void BluetoothAvrcpControlBinderServer::OnRegisterForAbsVolumeCallbackRequest(
+    const std::string& device_address, int32_t label) {
+  auto addr_s16 = String16(device_address.c_str(), device_address.size());
+  auto func = [&addr_s16, label](IInterface* cb) {
+    auto avrcp_control_cb = static_cast<IBluetoothAvrcpControlCallback*>(cb);
+    avrcp_control_cb->OnRegisterForAbsVolumeCallbackRequest(addr_s16, label);
+  };
+
+  ForEachCallback(func);
+}
+
+std::shared_ptr<bluetooth::AvrcpControl>
+BluetoothAvrcpControlBinderServer::GetAvrcpControl(int id) {
+  return std::static_pointer_cast<bluetooth::AvrcpControl>(GetInstance(id));
+}
+
+void BluetoothAvrcpControlBinderServer::OnRegisterInstanceImpl(
+    bluetooth::BLEStatus status, android::sp<IInterface> callback,
+    bluetooth::BluetoothInstance* instance) {
+  VLOG(1) << __func__ << " client ID: " << instance->GetInstanceId()
+          << " status: " << status;
+
+  bluetooth::AvrcpControl* avrcp_control =
+      static_cast<bluetooth::AvrcpControl*>(instance);
+  avrcp_control->SetDelegate(this);
+
+  android::sp<IBluetoothAvrcpControlCallback> cb(
+      static_cast<IBluetoothAvrcpControlCallback*>(callback.get()));
+  cb->OnRegistered(status, (status == bluetooth::BLE_STATUS_SUCCESS)
+                               ? instance->GetInstanceId()
+                               : kInvalidInstanceId);
+}
+
+}  // namespace binder
+}  // namespace ipc
diff --git a/service/ipc/binder/bluetooth_avrcp_control_binder_server.h b/service/ipc/binder/bluetooth_avrcp_control_binder_server.h
new file mode 100644
index 0000000..11cf1f3
--- /dev/null
+++ b/service/ipc/binder/bluetooth_avrcp_control_binder_server.h
@@ -0,0 +1,89 @@
+//
+//  Copyright (C) 2017 Google, Inc.
+//
+//  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 <map>
+#include <string>
+
+#include "base/macros.h"
+
+#include "android/bluetooth/BnBluetoothAvrcpControl.h"
+#include "android/bluetooth/IBluetoothAvrcpControlCallback.h"
+
+#include "service/avrcp_control.h"
+#include "service/ipc/binder/interface_with_instances_base.h"
+
+namespace bluetooth {
+class Adapter;
+}  // namespace bluetooth
+
+namespace ipc {
+namespace binder {
+
+class BluetoothAvrcpControlBinderServer
+    : public InterfaceWithInstancesBase,
+      public android::bluetooth::BnBluetoothAvrcpControl,
+      public bluetooth::AvrcpControl::Delegate {
+ public:
+  explicit BluetoothAvrcpControlBinderServer(bluetooth::Adapter* adapter);
+  ~BluetoothAvrcpControlBinderServer() override = default;
+
+  // IBluetoothAvrcpControl implementation:
+  android::binder::Status Register(
+      const android::sp<::android::bluetooth::IBluetoothAvrcpControlCallback>&
+          callback,
+      bool* _aidl_return) override;
+  android::binder::Status Unregister(int32_t id) override;
+  android::binder::Status UnregisterAll() override;
+  android::binder::Status Enable(int32_t id, bool* _aidl_return) override;
+  android::binder::Status Disable(int32_t id, bool* _aidl_return) override;
+  android::binder::Status SendPassThroughCommand(
+      int32_t id, const android::String16& device_address, int32_t key_code,
+      bool key_pressed, bool* _aidl_return) override;
+  android::binder::Status SetAbsVolumeResponse(
+      int32_t id, const android::String16& device_address, int32_t abs_vol,
+      int32_t label, bool* _aidl_return) override;
+  android::binder::Status RegisterForAbsVolumeCallbackResponse(
+      int32_t id, const android::String16& device_address,
+      int32_t response_type, int32_t abs_vol, int32_t label,
+      bool* _aidl_return) override;
+
+ private:
+  // bluetooth::bluetooth::AvrcpControl::Delegate implementation:
+  void OnConnectionState(bool rc_connect, bool bt_connect,
+                         const std::string& device_address) override;
+  void OnTrackChanged(const std::string& device_address,
+                      const bluetooth::AvrcpMediaAttr& attr) override;
+  void OnSetAbsVolumeRequest(const std::string& device_address, int32_t abs_vol,
+                             int32_t label) override;
+  void OnRegisterForAbsVolumeCallbackRequest(const std::string& device_address,
+                                             int32_t label) override;
+
+  // InterfaceWithInstancesBase override:
+  void OnRegisterInstanceImpl(bluetooth::BLEStatus status,
+                              android::sp<IInterface> callback,
+                              bluetooth::BluetoothInstance* instance) override;
+
+  std::shared_ptr<bluetooth::AvrcpControl> GetAvrcpControl(int id);
+
+  bluetooth::Adapter* adapter_;  // weak
+
+  DISALLOW_COPY_AND_ASSIGN(BluetoothAvrcpControlBinderServer);
+};
+
+}  // namespace binder
+}  // namespace ipc
diff --git a/service/ipc/binder/bluetooth_binder_server.cc b/service/ipc/binder/bluetooth_binder_server.cc
index 2d32cee..30090ee 100644
--- a/service/ipc/binder/bluetooth_binder_server.cc
+++ b/service/ipc/binder/bluetooth_binder_server.cc
@@ -18,6 +18,8 @@
 
 #include <base/logging.h>
 
+#include "service/ipc/binder/bluetooth_a2dp_sink_binder_server.h"
+#include "service/ipc/binder/bluetooth_avrcp_control_binder_server.h"
 #include "service/ipc/binder/bluetooth_gatt_client_binder_server.h"
 #include "service/ipc/binder/bluetooth_gatt_server_binder_server.h"
 #include "service/ipc/binder/bluetooth_le_advertiser_binder_server.h"
@@ -107,6 +109,59 @@
   return Status::ok();
 }
 
+Status BluetoothBinderServer::SetScanMode(int32_t scan_mode,
+                                          bool* _aidl_return) {
+  VLOG(2) << __func__;
+  *_aidl_return = adapter_->SetScanMode(scan_mode);
+  return Status::ok();
+}
+
+Status BluetoothBinderServer::SetScanEnable(bool scan_enable,
+                                            bool* _aidl_return) {
+  VLOG(2) << __func__;
+  *_aidl_return = adapter_->SetScanEnable(scan_enable);
+  return Status::ok();
+}
+
+Status BluetoothBinderServer::SspReply(
+    const ::android::String16& device_address, int32_t variant, bool accept,
+    int32_t passkey, bool* _aidl_return) {
+  VLOG(2) << __func__;
+  *_aidl_return = adapter_->SspReply(String8(device_address).string(), variant,
+                                     accept, passkey);
+  return Status::ok();
+}
+
+Status BluetoothBinderServer::CreateBond(
+    const ::android::String16& device_address, int32_t transport,
+    bool* _aidl_return) {
+  VLOG(2) << __func__;
+  *_aidl_return =
+      adapter_->CreateBond(String8(device_address).string(), transport);
+  return Status::ok();
+}
+
+Status BluetoothBinderServer::GetBondedDevices(bool* _aidl_return) {
+  VLOG(2) << __func__;
+  *_aidl_return = adapter_->GetBondedDevices();
+  return Status::ok();
+}
+
+Status BluetoothBinderServer::RemoveBond(
+    const ::android::String16& device_address, bool* _aidl_return) {
+  VLOG(2) << __func__;
+  *_aidl_return = adapter_->RemoveBond(String8(device_address).string());
+  return Status::ok();
+}
+
+Status BluetoothBinderServer::GetRemoteDeviceProperties(
+    const ::android::String16& device_address, bool* _aidl_return) {
+  VLOG(2) << __func__;
+  *_aidl_return =
+      adapter_->GetRemoteDeviceProperties(String8(device_address).string());
+  return Status::ok();
+}
+
 Status BluetoothBinderServer::RegisterCallback(
     const ::android::sp<IBluetoothCallback>& callback) {
   VLOG(2) << __func__;
@@ -136,6 +191,29 @@
   return Status::ok();
 }
 
+Status BluetoothBinderServer::GetA2dpSinkInterface(
+    ::android::sp<IBluetoothA2dpSink>* _aidl_return) {
+  VLOG(2) << __func__;
+
+  if (!adapter_->IsEnabled()) {
+    LOG(ERROR) << "Cannot obtain IBluetoothA2dpSink interface while disabled";
+    *_aidl_return = nullptr;
+    return Status::ok();
+  }
+
+  if (!a2dp_sink_interface_.get())
+    a2dp_sink_interface_ = new BluetoothA2dpSinkBinderServer(adapter_);
+
+  if (a2dp_sink_interface_->HasInstance()) {
+    LOG(ERROR) << "Only one A2dpSink interface allowed at a time";
+    *_aidl_return = nullptr;
+    return Status::ok();
+  }
+
+  *_aidl_return = a2dp_sink_interface_;
+  return Status::ok();
+}
+
 Status BluetoothBinderServer::GetLowEnergyInterface(
     ::android::sp<IBluetoothLowEnergy>* _aidl_return) {
   VLOG(2) << __func__;
@@ -222,6 +300,24 @@
   return Status::ok();
 }
 
+Status BluetoothBinderServer::GetAvrcpControlInterface(
+    ::android::sp<IBluetoothAvrcpControl>* _aidl_return) {
+  VLOG(2) << __func__;
+
+  if (!adapter_->IsEnabled()) {
+    LOG(ERROR)
+        << "Cannot obtain IBluetoothAvrcpControl interface while disabled";
+    *_aidl_return = NULL;
+    return Status::ok();
+  }
+
+  if (!avrcp_control_interface_.get())
+    avrcp_control_interface_ = new BluetoothAvrcpControlBinderServer(adapter_);
+
+  *_aidl_return = avrcp_control_interface_;
+  return Status::ok();
+}
+
 android::status_t BluetoothBinderServer::dump(
     int fd, const android::Vector<android::String16>& args) {
   VLOG(2) << __func__ << " called with fd " << fd;
@@ -249,5 +345,97 @@
   });
 }
 
+void BluetoothBinderServer::OnDeviceConnectionStateChanged(
+    bluetooth::Adapter* adapter, const std::string& device_address,
+    bool connected) {
+  CHECK_EQ(adapter, adapter_);
+  auto addr_s16 = String16(device_address.c_str(), device_address.size());
+  callbacks_.ForEach([&addr_s16, connected](IBluetoothCallback* callback) {
+    callback->OnDeviceConnectionStateChanged(addr_s16, connected);
+  });
+}
+
+void BluetoothBinderServer::OnScanEnableChanged(bluetooth::Adapter* adapter,
+                                                bool scan_enabled) {
+  CHECK_EQ(adapter, adapter_);
+  callbacks_.ForEach([scan_enabled](IBluetoothCallback* callback) {
+    callback->OnScanEnableChanged(scan_enabled);
+  });
+}
+
+void BluetoothBinderServer::OnSspRequest(bluetooth::Adapter* adapter,
+                                         const std::string& device_address,
+                                         const std::string& device_name,
+                                         int cod, int pairing_variant,
+                                         int pass_key) {
+  CHECK_EQ(adapter, adapter_);
+  VLOG(2) << "Received ssp request: device_address: " << device_address
+          << ", device_name: " << device_name << ", cod: " << cod
+          << ", pairing_variant: " << pairing_variant
+          << ", pass_key: " << pass_key;
+
+  android::String16 addr_s16(device_address.c_str());
+  android::String16 name_s16(device_name.c_str());
+  callbacks_.ForEach([&addr_s16, &name_s16, cod, pairing_variant,
+                      pass_key](IBluetoothCallback* callback) {
+    callback->OnSspRequest(addr_s16, name_s16, cod, pairing_variant, pass_key);
+  });
+}
+
+void BluetoothBinderServer::OnBondStateChanged(
+    bluetooth::Adapter* adapter, int status, const std::string& device_address,
+    int state) {
+  CHECK_EQ(adapter, adapter_);
+  VLOG(2) << "Received " << __func__ << " "
+          << "status: " << status << ", device_address: " << device_address
+          << ", state: " << state;
+  android::String16 addr_s16(device_address.c_str(), device_address.size());
+  callbacks_.ForEach([status, &addr_s16, state](IBluetoothCallback* callback) {
+    callback->OnBondStateChanged(status, addr_s16, state);
+  });
+}
+
+void BluetoothBinderServer::OnGetBondedDevices(
+    bluetooth::Adapter* adapter, int status,
+    const std::vector<std::string>& bonded_devices) {
+  CHECK_EQ(adapter, adapter_);
+  VLOG(2) << "Received " << __func__;
+  std::vector<android::String16> devices_s16;
+  devices_s16.reserve(bonded_devices.size());
+  for (const auto& device : bonded_devices)
+    devices_s16.emplace_back(device.c_str(), device.size());
+
+  callbacks_.ForEach([status, &devices_s16](IBluetoothCallback* callback) {
+    callback->OnGetBondedDevices(status, devices_s16);
+  });
+}
+
+void BluetoothBinderServer::OnGetRemoteDeviceProperties(
+    bluetooth::Adapter* adapter, int status, const std::string& device_address,
+    const bluetooth::RemoteDeviceProps& properties) {
+  CHECK_EQ(adapter, adapter_);
+  VLOG(2) << "Received " << __func__ << " "
+          << "status: " << status << ", device_address: " << device_address;
+  android::String16 addr_s16(device_address.c_str(), device_address.size());
+  auto binder_props =
+      android::bluetooth::BluetoothRemoteDeviceProps(properties);
+  callbacks_.ForEach(
+      [status, &addr_s16, &binder_props](IBluetoothCallback* callback) {
+        callback->OnGetRemoteDeviceProperties(status, addr_s16, binder_props);
+      });
+}
+
+void BluetoothBinderServer::OnDeviceFound(
+    bluetooth::Adapter* adapter,
+    const bluetooth::RemoteDeviceProps& properties) {
+  CHECK_EQ(adapter, adapter_);
+  VLOG(2) << "Received " << __func__ << " ";
+  auto binder_props =
+      android::bluetooth::BluetoothRemoteDeviceProps(properties);
+  callbacks_.ForEach([&binder_props](IBluetoothCallback* callback) {
+    callback->OnDeviceFound(binder_props);
+  });
+}
+
 }  // namespace binder
 }  // namespace ipc
diff --git a/service/ipc/binder/bluetooth_binder_server.h b/service/ipc/binder/bluetooth_binder_server.h
index b03a815..1388626 100644
--- a/service/ipc/binder/bluetooth_binder_server.h
+++ b/service/ipc/binder/bluetooth_binder_server.h
@@ -1,5 +1,5 @@
 //
-//  Copyright 2015 Google, Inc.
+//  Copyright (C) 2015 Google, Inc.
 //
 //  Licensed under the Apache License, Version 2.0 (the "License");
 //  you may not use this file except in compliance with the License.
@@ -24,14 +24,16 @@
 #include <utils/Vector.h>
 
 #include <android/bluetooth/BnBluetooth.h>
+#include <android/bluetooth/IBluetoothA2dpSink.h>
+#include <android/bluetooth/IBluetoothAvrcpControl.h>
 #include <android/bluetooth/IBluetoothCallback.h>
 #include <android/bluetooth/IBluetoothGattClient.h>
 #include <android/bluetooth/IBluetoothGattServer.h>
 #include <android/bluetooth/IBluetoothLeAdvertiser.h>
 #include <android/bluetooth/IBluetoothLeScanner.h>
 #include <android/bluetooth/IBluetoothLowEnergy.h>
-#include <bluetooth/uuid.h>
 
+#include "bluetooth/uuid.h"
 #include "service/adapter.h"
 #include "service/ipc/binder/remote_callback_list.h"
 
@@ -39,16 +41,20 @@
 using android::binder::Status;
 
 using android::bluetooth::BnBluetooth;
+using android::bluetooth::IBluetoothA2dpSink;
+using android::bluetooth::IBluetoothAvrcpControl;
 using android::bluetooth::IBluetoothCallback;
 using android::bluetooth::IBluetoothGattClient;
 using android::bluetooth::IBluetoothGattServer;
-using android::bluetooth::IBluetoothLowEnergy;
 using android::bluetooth::IBluetoothLeAdvertiser;
 using android::bluetooth::IBluetoothLeScanner;
+using android::bluetooth::IBluetoothLowEnergy;
 
 namespace ipc {
 namespace binder {
 
+class BluetoothA2dpSinkBinderServer;
+
 // Implements the server side of the IBluetooth Binder interface.
 class BluetoothBinderServer : public BnBluetooth,
                               public bluetooth::Adapter::Observer {
@@ -68,12 +74,25 @@
       ::std::vector<::android::bluetooth::UUID>* _aidl_return) override;
   Status SetName(const ::android::String16& name, bool* _aidl_return) override;
   Status GetName(::android::String16* _aidl_return) override;
+  Status SetScanMode(int32_t scan_mode, bool* _aidl_return) override;
+  Status SetScanEnable(bool scan_enable, bool* _aidl_return) override;
+  Status SspReply(const ::android::String16& device_address, int32_t variant,
+                  bool accept, int32_t passkey, bool* _aidl_return) override;
+  Status CreateBond(const ::android::String16& device_address,
+                    int32_t transport, bool* _aidl_return) override;
+  Status GetBondedDevices(bool* _aidl_return) override;
+  Status RemoveBond(const ::android::String16& device_address,
+                    bool* _aidl_return) override;
+  Status GetRemoteDeviceProperties(const ::android::String16& device_address,
+                                   bool* _aidl_return) override;
 
   Status RegisterCallback(
       const ::android::sp<IBluetoothCallback>& callback) override;
   Status UnregisterCallback(
       const ::android::sp<IBluetoothCallback>& callback) override;
   Status IsMultiAdvertisementSupported(bool* _aidl_return) override;
+  Status GetA2dpSinkInterface(
+      ::android::sp<IBluetoothA2dpSink>* _aidl_return) override;
   Status GetLowEnergyInterface(
       ::android::sp<IBluetoothLowEnergy>* _aidl_return) override;
   Status GetLeAdvertiserInterface(
@@ -84,6 +103,8 @@
       ::android::sp<IBluetoothGattClient>* _aidl_return) override;
   Status GetGattServerInterface(
       ::android::sp<IBluetoothGattServer>* _aidl_return) override;
+  Status GetAvrcpControlInterface(
+      ::android::sp<IBluetoothAvrcpControl>* _aidl_return) override;
 
   android::status_t dump(
       int fd, const android::Vector<android::String16>& args) override;
@@ -93,10 +114,39 @@
                              bluetooth::AdapterState prev_state,
                              bluetooth::AdapterState new_state) override;
 
+  void OnDeviceConnectionStateChanged(bluetooth::Adapter* adapter,
+                                      const std::string& device_address,
+                                      bool connected) override;
+
+  void OnScanEnableChanged(bluetooth::Adapter* adapter,
+                           bool scan_enabled) override;
+
+  void OnSspRequest(bluetooth::Adapter* adapter,
+                    const std::string& device_address,
+                    const std::string& device_name, int cod,
+                    int pairing_variant, int pass_key) override;
+
+  void OnBondStateChanged(bluetooth::Adapter* adapter, int status,
+                          const std::string& device_address,
+                          int state) override;
+  void OnGetBondedDevices(
+      bluetooth::Adapter* adapter, int status,
+      const std::vector<std::string>& bonded_devices) override;
+  void OnGetRemoteDeviceProperties(
+      bluetooth::Adapter* adapter, int status,
+      const std::string& device_address,
+      const bluetooth::RemoteDeviceProps& properties) override;
+  void OnDeviceFound(bluetooth::Adapter* adapter,
+                     const bluetooth::RemoteDeviceProps& properties) override;
+
  private:
   bluetooth::Adapter* adapter_;  // weak
   RemoteCallbackList<IBluetoothCallback> callbacks_;
 
+  // The IBluetoothA2dpSink interface handle. This is lazily initialized on the
+  // first call to GetA2dpSinkInterface().
+  android::sp<BluetoothA2dpSinkBinderServer> a2dp_sink_interface_;
+
   // The IBluetoothLowEnergy interface handle. This is lazily initialized on the
   // first call to GetLowEnergyInterface().
   android::sp<IBluetoothLowEnergy> low_energy_interface_;
@@ -117,6 +167,10 @@
   // the first call to GetGattServerInterface().
   android::sp<IBluetoothGattServer> gatt_server_interface_;
 
+  // The IBluetoothAvrcpControl interface handle. This is lazily initialized on
+  // the first call to GetAvrcpControlInterface().
+  android::sp<IBluetoothAvrcpControl> avrcp_control_interface_;
+
   DISALLOW_COPY_AND_ASSIGN(BluetoothBinderServer);
 };
 
diff --git a/service/ipc/binder/interface_with_instances_base.cc b/service/ipc/binder/interface_with_instances_base.cc
index ba24657..1716bbd 100644
--- a/service/ipc/binder/interface_with_instances_base.cc
+++ b/service/ipc/binder/interface_with_instances_base.cc
@@ -86,6 +86,16 @@
   id_to_instance_.clear();
 }
 
+void InterfaceWithInstancesBase::ForEachCallback(
+    const std::function<void(IInterface*)>& func) {
+  VLOG(2) << __func__;
+  std::lock_guard<std::mutex> lock(maps_lock_);
+  for (auto& pair : id_to_instance_) {
+    auto cb = id_to_cb_.Get(pair.first);
+    func(cb.get());
+  }
+}
+
 android::sp<IInterface> InterfaceWithInstancesBase::GetCallback(
     int instance_id) {
   return id_to_cb_.Get(instance_id);
diff --git a/service/ipc/binder/interface_with_instances_base.h b/service/ipc/binder/interface_with_instances_base.h
index 90224b6..05f2817 100644
--- a/service/ipc/binder/interface_with_instances_base.h
+++ b/service/ipc/binder/interface_with_instances_base.h
@@ -1,5 +1,5 @@
 //
-//  Copyright 2015 Google, Inc.
+//  Copyright (C) 2015 Google, Inc.
 //
 //  Licensed under the Apache License, Version 2.0 (the "License");
 //  you may not use this file except in compliance with the License.
@@ -20,8 +20,8 @@
 #include <unordered_map>
 
 #include <base/macros.h>
-#include <bluetooth/uuid.h>
 
+#include "bluetooth/uuid.h"
 #include "service/bluetooth_instance.h"
 #include "service/ipc/binder/remote_callback_map.h"
 
@@ -57,6 +57,8 @@
   // Unregisters all registered instances.
   void UnregisterAllBase();
 
+  void ForEachCallback(const std::function<void(IInterface*)>& func);
+
   // Returns a handle to the lock used to synchronize access to the internal
   // data structures. Subclasses should acquire this before accessing the maps.
   std::mutex* maps_lock() { return &maps_lock_; }
diff --git a/service/test/a2dp_sink_unittest.cc b/service/test/a2dp_sink_unittest.cc
new file mode 100644
index 0000000..06fb88e
--- /dev/null
+++ b/service/test/a2dp_sink_unittest.cc
@@ -0,0 +1,271 @@
+//
+//  Copyright 2017 Google, Inc.
+//
+//  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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "service/a2dp_sink.h"
+#include "service/hal/fake_bluetooth_av_interface.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace bluetooth {
+namespace {
+
+class MockA2dpSinkHandler
+    : public hal::FakeBluetoothAvInterface::TestA2dpSinkHandler {
+ public:
+  MockA2dpSinkHandler() = default;
+  ~MockA2dpSinkHandler() override = default;
+
+  MOCK_METHOD1(Connect, bt_status_t(RawAddress));
+  MOCK_METHOD1(Disconnect, bt_status_t(RawAddress));
+  MOCK_METHOD1(SetAudioFocusState, void(int));
+  MOCK_METHOD1(SetAudioTrackGain, void(float));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockA2dpSinkHandler);
+};
+
+class TestDelegate : public A2dpSink::Delegate {
+ public:
+  TestDelegate() = default;
+  ~TestDelegate() override = default;
+
+  struct RequestData {
+    std::string device_address;
+    int state = -1;
+    uint32_t sample_rate = 0;
+    uint8_t channel_count = 0;
+    int count = 0;
+  };
+
+  // A2dpSink::Delegate implementation:
+  void OnConnectionState(const std::string& device_address,
+                         int state) override {
+    ++connection_state_.count;
+    connection_state_.device_address = device_address;
+    connection_state_.state = state;
+  }
+  void OnAudioState(const std::string& device_address, int state) override {
+    ++audio_state_.count;
+    audio_state_.device_address = device_address;
+    audio_state_.state = state;
+  }
+  void OnAudioConfig(const std::string& device_address, uint32_t sample_rate,
+                     uint8_t channel_count) override {
+    ++audio_config_.count;
+    audio_config_.device_address = device_address;
+    audio_config_.sample_rate = sample_rate;
+    audio_config_.channel_count = channel_count;
+  }
+
+  const RequestData& connection_state() const { return connection_state_; }
+  const RequestData& audio_state() const { return audio_state_; }
+  const RequestData& audio_config() const { return audio_config_; }
+
+ private:
+  RequestData connection_state_;
+  RequestData audio_state_;
+  RequestData audio_config_;
+};
+
+class A2dpSinkTest : public ::testing::Test {
+ public:
+  A2dpSinkTest() = default;
+  ~A2dpSinkTest() override = default;
+
+  void SetUp() override {
+    mock_handler_.reset(new MockA2dpSinkHandler());
+    fake_hal_av_iface_ = new hal::FakeBluetoothAvInterface(mock_handler_);
+    hal::BluetoothAvInterface::InitializeForTesting(fake_hal_av_iface_);
+    factory_.reset(new A2dpSinkFactory());
+  }
+
+  void TearDown() override {
+    factory_.reset();
+    hal::BluetoothAvInterface::CleanUp();
+  }
+
+ protected:
+  hal::FakeBluetoothAvInterface* fake_hal_av_iface_;
+  std::shared_ptr<MockA2dpSinkHandler> mock_handler_;
+  std::unique_ptr<A2dpSinkFactory> factory_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(A2dpSinkTest);
+};
+
+class A2dpSinkPostRegisterTest : public A2dpSinkTest {
+ public:
+  A2dpSinkPostRegisterTest() = default;
+  ~A2dpSinkPostRegisterTest() override = default;
+
+  void SetUp() override {
+    A2dpSinkTest::SetUp();
+    Uuid uuid = Uuid::GetRandom();
+    auto callback = [&](BLEStatus status, const Uuid& in_uuid,
+                        std::unique_ptr<BluetoothInstance> in_client) {
+      CHECK(in_uuid == uuid);
+      CHECK(in_client.get());
+      CHECK(status == BLE_STATUS_SUCCESS);
+
+      a2dp_sink_ = std::unique_ptr<A2dpSink>(
+          static_cast<A2dpSink*>(in_client.release()));
+    };
+
+    factory_->RegisterInstance(uuid, callback);
+  }
+
+  void TearDown() override {
+    a2dp_sink_ = nullptr;
+    A2dpSinkTest::TearDown();
+  }
+
+ protected:
+  void Connect(const std::string& addr) {
+    RawAddress hal_addr;
+    ASSERT_TRUE(RawAddress::FromString(addr, hal_addr));
+
+    EXPECT_CALL(*mock_handler_, Connect(hal_addr))
+        .WillOnce(Return(BT_STATUS_SUCCESS));
+
+    EXPECT_TRUE(a2dp_sink_->Connect(addr));
+  }
+
+  void Disconnect(const std::string& addr) {
+    RawAddress hal_addr;
+    ASSERT_TRUE(RawAddress::FromString(addr, hal_addr));
+
+    EXPECT_CALL(*mock_handler_, Disconnect(hal_addr))
+        .WillOnce(Return(BT_STATUS_SUCCESS));
+
+    EXPECT_TRUE(a2dp_sink_->Disconnect(addr));
+  }
+
+  std::unique_ptr<A2dpSink> a2dp_sink_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(A2dpSinkPostRegisterTest);
+};
+
+TEST_F(A2dpSinkTest, RegisterA2dpSink) {
+  // These will be asynchronously populate with a result when the callback
+  // executes.
+  BLEStatus status = BLE_STATUS_SUCCESS;
+  Uuid cb_uuid;
+  std::unique_ptr<A2dpSink> a2dp_sink;
+  int callback_count = 0;
+
+  auto callback = [&](BLEStatus in_status, const Uuid& uuid,
+                      std::unique_ptr<BluetoothInstance> in_a2dp_sink) {
+    status = in_status;
+    cb_uuid = uuid;
+    a2dp_sink = std::unique_ptr<A2dpSink>(
+        static_cast<A2dpSink*>(in_a2dp_sink.release()));
+    callback_count++;
+  };
+
+  Uuid uuid0 = Uuid::GetRandom();
+
+  // This should always succeed.
+  EXPECT_TRUE(factory_->RegisterInstance(uuid0, callback));
+  EXPECT_EQ(1, callback_count);
+
+  testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+
+  ASSERT_TRUE(a2dp_sink.get() !=
+              nullptr);  // Assert to terminate in case of error
+  EXPECT_EQ(BLE_STATUS_SUCCESS, status);
+  EXPECT_EQ(bluetooth::A2dpSink::kSingletonInstanceId,
+            a2dp_sink->GetInstanceId());
+  EXPECT_EQ(uuid0, a2dp_sink->GetAppIdentifier());
+  EXPECT_EQ(uuid0, cb_uuid);
+
+  testing::Mock::VerifyAndClearExpectations(mock_handler_.get());
+}
+
+TEST_F(A2dpSinkPostRegisterTest, Connect) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  Connect(kTestAddr);
+  Disconnect(kTestAddr);
+}
+
+TEST_F(A2dpSinkPostRegisterTest, SetAudioFocusState) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  static const int kFocusState = 2;
+  Connect(kTestAddr);
+
+  EXPECT_CALL(*mock_handler_, SetAudioFocusState(kFocusState));
+  a2dp_sink_->SetAudioFocusState(kFocusState);
+
+  Disconnect(kTestAddr);
+}
+
+TEST_F(A2dpSinkPostRegisterTest, SetAudioTrackGain) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  static const float kTrackGain = 0.5;
+  Connect(kTestAddr);
+
+  EXPECT_CALL(*mock_handler_, SetAudioTrackGain(kTrackGain));
+  a2dp_sink_->SetAudioTrackGain(kTrackGain);
+
+  Disconnect(kTestAddr);
+}
+
+TEST_F(A2dpSinkPostRegisterTest, CallbackTest) {
+  static const char kTestAddr[] = "AA:BB:CC:DD:EE:FF";
+  RawAddress hal_addr;
+  ASSERT_TRUE(RawAddress::FromString(kTestAddr, hal_addr));
+
+  TestDelegate delegate;
+  a2dp_sink_->SetDelegate(&delegate);
+  Connect(kTestAddr);
+
+  // OnConnectionState
+  const int kConnectionState = 2;
+  EXPECT_EQ(0, delegate.connection_state().count);
+  fake_hal_av_iface_->NotifyConnectionState(
+      hal_addr, static_cast<btav_connection_state_t>(kConnectionState));
+  EXPECT_EQ(1, delegate.connection_state().count);
+  EXPECT_EQ(kTestAddr, delegate.connection_state().device_address);
+  EXPECT_EQ(kConnectionState, delegate.connection_state().state);
+
+  // OnAudioState
+  const int kAudioState = 1;
+  EXPECT_EQ(0, delegate.audio_state().count);
+  fake_hal_av_iface_->NotifyAudioState(
+      hal_addr, static_cast<btav_audio_state_t>(kAudioState));
+  EXPECT_EQ(1, delegate.audio_state().count);
+  EXPECT_EQ(kTestAddr, delegate.audio_state().device_address);
+  EXPECT_EQ(kAudioState, delegate.audio_state().state);
+
+  // OnAudioConfig
+  const uint32_t kSampleRate = 44100;
+  const uint32_t kChannelCount = 2;
+  EXPECT_EQ(0, delegate.audio_config().count);
+  fake_hal_av_iface_->NotifyAudioConfig(hal_addr, kSampleRate, kChannelCount);
+  EXPECT_EQ(1, delegate.audio_config().count);
+  EXPECT_EQ(kTestAddr, delegate.audio_config().device_address);
+  EXPECT_EQ(kSampleRate, delegate.audio_config().sample_rate);
+  EXPECT_EQ(kChannelCount, delegate.audio_config().channel_count);
+
+  Disconnect(kTestAddr);
+}
+
+}  // namespace
+}  // namespace bluetooth
diff --git a/service/test/mock_adapter.h b/service/test/mock_adapter.h
index 4b93792..ff64ab1 100644
--- a/service/test/mock_adapter.h
+++ b/service/test/mock_adapter.h
@@ -37,11 +37,22 @@
   MOCK_CONST_METHOD0(GetName, std::string());
   MOCK_METHOD1(SetName, bool(const std::string&));
   MOCK_CONST_METHOD0(GetAddress, std::string());
+  MOCK_METHOD1(SetScanMode, bool(int));
+  MOCK_METHOD1(SetScanEnable, bool(bool));
+  MOCK_METHOD4(SspReply, bool(const std::string&, int, bool, int32_t));
+  MOCK_METHOD2(CreateBond,
+               bool(const std::string& device_address, int transport));
   MOCK_METHOD0(IsMultiAdvertisementSupported, bool());
   MOCK_METHOD1(IsDeviceConnected, bool(const std::string&));
   MOCK_METHOD0(GetTotalNumberOfTrackableAdvertisements, int());
   MOCK_METHOD0(IsOffloadedFilteringSupported, bool());
   MOCK_METHOD0(IsOffloadedScanBatchingSupported, bool());
+  MOCK_METHOD0(GetBondedDevices, bool());
+  MOCK_METHOD1(RemoveBond, bool(const std::string&));
+  MOCK_METHOD1(GetRemoteDeviceProperties,
+               bool(const std::string& device_address));
+  MOCK_CONST_METHOD0(GetA2dpSinkFactory, A2dpSinkFactory*());
+  MOCK_CONST_METHOD0(GetAvrcpControlFactory, AvrcpControlFactory*());
   MOCK_CONST_METHOD0(GetLowEnergyClientFactory, LowEnergyClientFactory*());
   MOCK_CONST_METHOD0(GetLeAdvertiserFactory, LowEnergyAdvertiserFactory*());
   MOCK_CONST_METHOD0(GetLeScannerFactory, LowEnergyScannerFactory*());