Merge "Enable 2-EV3 packets for Wide Band HFP client"
diff --git a/binder/Android.bp b/binder/Android.bp
index 18428c2..faae3a8 100644
--- a/binder/Android.bp
+++ b/binder/Android.bp
@@ -8,7 +8,6 @@
     srcs: [
         "android/bluetooth/bluetooth_device.cc",
         "android/bluetooth/IBluetoothSocketManager.aidl",
-        "android/os/parcel_file_descriptor.cc",
         "android/os/parcel_uuid.cc",
 /* TODO: Uncomment this files as they get converted one-by-one into native implementation
         "android/bluetooth/IBluetooth.aidl",
diff --git a/binder/android/os/parcel_file_descriptor.cc b/binder/android/os/parcel_file_descriptor.cc
deleted file mode 100644
index 37bef6d..0000000
--- a/binder/android/os/parcel_file_descriptor.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-//
-//  Copyright 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.
-//
-
-#include "android/os/parcel_file_descriptor.h"
-#include <base/logging.h>
-
-using android::OK;
-using android::Parcel;
-using android::status_t;
-
-namespace android {
-namespace os {
-
-status_t ParcelFileDescriptor::writeToParcel(Parcel* parcel) const {
-  CHECK(fd_ >= 0);
-  return parcel->writeParcelFileDescriptor(fd_, takeOwnership_);
-}
-
-status_t ParcelFileDescriptor::readFromParcel(const Parcel* parcel) {
-  LOG(FATAL) << "Don't know how to read ParcelFileDescriptor";
-  return OK;
-}
-
-void ParcelFileDescriptor::setFileDescriptor(int fd, bool takeOwnership) {
-  fd_ = fd;
-  takeOwnership_ = takeOwnership;
-}
-
-}  // namespace os
-}  // namespace android
diff --git a/binder/android/os/parcel_file_descriptor.h b/binder/android/os/parcel_file_descriptor.h
deleted file mode 100644
index a37b49c..0000000
--- a/binder/android/os/parcel_file_descriptor.h
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-//  Copyright 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.
-//
-
-#pragma once
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace os {
-
-class ParcelFileDescriptor : public android::Parcelable {
- public:
-  ParcelFileDescriptor() : fd_(-1), takeOwnership_(false) {}
-  ~ParcelFileDescriptor() = 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.
-  android::status_t writeToParcel(android::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.
-  android::status_t readFromParcel(const android::Parcel* parcel) override;
-
-  void setFileDescriptor(int fd, bool takeOwnership);
-
- private:
-  int fd_;
-  bool takeOwnership_;
-};
-
-}  // namespace os
-}  // namespace android
diff --git a/bta/Android.bp b/bta/Android.bp
index d6919de..57f2b61 100644
--- a/bta/Android.bp
+++ b/bta/Android.bp
@@ -68,6 +68,8 @@
         "gatt/bta_gatts_api.cc",
         "gatt/bta_gatts_main.cc",
         "gatt/bta_gatts_utils.cc",
+        "gatt/database.cc",
+        "gatt/database_builder.cc",
         "hearing_aid/hearing_aid.cc",
         "hearing_aid/hearing_aid_audio_source.cc",
         "hf_client/bta_hf_client_act.cc",
@@ -127,7 +129,9 @@
     defaults: ["fluoride_bta_defaults"],
     srcs: [
         "test/bta_hf_client_test.cc",
-        "test/gatt_cache_file_test.cc",
+        "test/gatt/database_builder_test.cc",
+        "test/gatt/database_builder_sample_device_test.cc",
+        "test/gatt/database_test.cc",
     ],
     shared_libs: [
         "liblog",
diff --git a/bta/BUILD.gn b/bta/BUILD.gn
index e519b69..648322f 100644
--- a/bta/BUILD.gn
+++ b/bta/BUILD.gn
@@ -49,6 +49,8 @@
     "gatt/bta_gatts_api.cc",
     "gatt/bta_gatts_main.cc",
     "gatt/bta_gatts_utils.cc",
+    "gatt/database.cc",
+    "gatt/database_builder.cc",
     "hearing_aid/hearing_aid.cc",
     "hearing_aid/hearing_aid_audio_source.cc",
     "hf_client/bta_hf_client_act.cc",
@@ -101,6 +103,8 @@
     "include",
     "sys",
     "//",
+    "//linux_include",
+    "//bta",
     "//internal_include",
     "//btcore/include",
     "//hci/include",
@@ -121,5 +125,31 @@
   deps = [
     "//third_party/libchrome:base"
   ]
-
 }
+
+executable("net_test_bta") {
+  testonly = true
+  sources = [
+        "test/gatt/database_builder_test.cc",
+        "test/gatt/database_builder_sample_device_test.cc",
+        "test/gatt/database_test.cc",
+        "gatt/database.cc",
+        "gatt/database_builder.cc",
+  ]
+
+  include_dirs = [
+    "include",
+    "//",
+    "//bta",
+    "//btcore/include",
+    "//hci/include",
+    "//internal_include",
+    "//stack/btm",
+  ]
+
+  deps = [
+    "//types",
+    "//third_party/googletest:gmock_main",
+    "//third_party/libchrome:base",
+  ]
+}
\ No newline at end of file
diff --git a/bta/ag/bta_ag_sco.cc b/bta/ag/bta_ag_sco.cc
index 0e3f30a..1ff4c7e 100644
--- a/bta/ag/bta_ag_sco.cc
+++ b/bta/ag/bta_ag_sco.cc
@@ -370,6 +370,12 @@
   if (!bta_ag_sco_is_active_device(p_scb->peer_addr)) {
     LOG(WARNING) << __func__ << ": device " << p_scb->peer_addr
                  << " is not active, active_device=" << active_device_addr;
+    if (bta_ag_cb.sco.p_curr_scb != nullptr &&
+        bta_ag_cb.sco.p_curr_scb->in_use && p_scb == bta_ag_cb.sco.p_curr_scb) {
+      do_in_bta_thread(
+          FROM_HERE, base::Bind(&bta_ag_sm_execute, p_scb, BTA_AG_SCO_CLOSE_EVT,
+                                tBTA_AG_DATA::kEmpty));
+    }
     return;
   }
   /* Make sure this SCO handle is not already in use */
@@ -555,6 +561,14 @@
   APPL_TRACE_DEBUG("%s", __func__);
   bta_ag_cb.sco.p_curr_scb = p_scb;
 
+  // Workaround for misbehaving HFs such as Sony XAV AX100 car kit and Sony
+  // MW600 Headset, which indicate WBS support in SDP, but no codec
+  // negotiation support in BRSF. In this case, using mSBC codec can result
+  // background noise or no audio. Thus, defaulting to CVSD instead.
+  if (!(p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) {
+    p_scb->sco_codec = UUID_CODEC_CVSD;
+  }
+
   if ((p_scb->codec_updated || p_scb->codec_fallback) &&
       (p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) {
     /* Change the power mode to Active until SCO open is completed. */
diff --git a/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index 997f021..297bba4 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -294,6 +294,9 @@
  *
  ******************************************************************************/
 static void notify_start_failed(tBTA_AV_SCB* p_scb) {
+  LOG_ERROR(LOG_TAG, "%s: peer %s role:0x%x channel:%d handle:0x%x", __func__,
+            p_scb->PeerAddress().ToString().c_str(), p_scb->role, p_scb->chnl,
+            p_scb->hndl);
   tBTA_AV_START start;
   /* if start failed, clear role */
   p_scb->role &= ~BTA_AV_ROLE_START_INT;
@@ -675,7 +678,8 @@
 void bta_av_role_res(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
   bool initiator = false;
 
-  APPL_TRACE_DEBUG("%s: q_tag:%d, wait:0x%x, role:0x%x", __func__, p_scb->q_tag,
+  APPL_TRACE_DEBUG("%s: peer %s q_tag:%d, wait:0x%x, role:0x%x", __func__,
+                   p_scb->PeerAddress().ToString().c_str(), p_scb->q_tag,
                    p_scb->wait, p_scb->role);
   if (p_scb->role & BTA_AV_ROLE_START_INT) initiator = true;
 
@@ -726,12 +730,14 @@
       }
     } else {
       APPL_TRACE_WARNING(
-          "%s: unexpected role switch event: q_tag = %d wait = %d", __func__,
-          p_scb->q_tag, p_scb->wait);
+          "%s: peer %s unexpected role switch event: q_tag = %d wait = 0x%x",
+          __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->q_tag,
+          p_scb->wait);
     }
   }
 
-  APPL_TRACE_DEBUG("%s: wait:0x%x, role:0x%x", __func__, p_scb->wait,
+  APPL_TRACE_DEBUG("%s: peer %s wait:0x%x, role:0x%x", __func__,
+                   p_scb->PeerAddress().ToString().c_str(), p_scb->wait,
                    p_scb->role);
 }
 
@@ -886,7 +892,8 @@
   tBTA_AV_CONN_CHG msg;
   uint8_t role = BTA_AV_ROLE_AD_INT;
 
-  APPL_TRACE_DEBUG("%s", __func__);
+  LOG_INFO(LOG_TAG, "%s peer %s", __func__,
+           p_scb->PeerAddress().ToString().c_str());
 
   /* free any buffers */
   p_scb->sdp_discovery_started = false;
@@ -1830,8 +1837,11 @@
   uint8_t clear_policy = 0;
   uint8_t cur_role;
 
-  APPL_TRACE_DEBUG("%s: sco_occupied:%d, role:0x%x, started:%d", __func__,
-                   bta_av_cb.sco_occupied, p_scb->role, p_scb->started);
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s sco_occupied:%s role:0x%x started:%s wait:0x%x",
+           __func__, p_scb->PeerAddress().ToString().c_str(),
+           logbool(bta_av_cb.sco_occupied).c_str(), p_scb->role,
+           logbool(p_scb->started).c_str(), p_scb->wait);
   if (bta_av_cb.sco_occupied) {
     bta_av_start_failed(p_scb, p_data);
     return;
@@ -1847,23 +1857,50 @@
 
   bta_sys_clear_policy(BTA_ID_AV, clear_policy, p_scb->PeerAddress());
 
-  if ((!p_scb->started) && ((p_scb->role & BTA_AV_ROLE_START_INT) == 0)) {
+  if (p_scb->started) {
     p_scb->role |= BTA_AV_ROLE_START_INT;
-    bta_sys_busy(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());
-
-    AVDT_StartReq(&p_scb->avdt_handle, 1);
-  } else if (p_scb->started) {
-    p_scb->role |= BTA_AV_ROLE_START_INT;
-    if (p_scb->wait == 0) {
-      if (p_scb->role & BTA_AV_ROLE_SUSPEND) {
-        notify_start_failed(p_scb);
-      } else {
-        bta_av_start_ok(p_scb, NULL);
-      }
+    if (p_scb->wait != 0) {
+      LOG_WARN(
+          LOG_TAG,
+          "%s: peer %s start stream request ignored: "
+          "already waiting: sco_occupied:%s role:0x%x started:%s wait:0x%x",
+          __func__, p_scb->PeerAddress().ToString().c_str(),
+          logbool(bta_av_cb.sco_occupied).c_str(), p_scb->role,
+          logbool(p_scb->started).c_str(), p_scb->wait);
+      return;
     }
+    if (p_scb->role & BTA_AV_ROLE_SUSPEND) {
+      notify_start_failed(p_scb);
+    } else {
+      bta_av_start_ok(p_scb, NULL);
+    }
+    return;
   }
-  APPL_TRACE_DEBUG("%s: started %d role:0x%x", __func__, p_scb->started,
-                   p_scb->role);
+
+  if ((p_scb->role & BTA_AV_ROLE_START_INT) != 0) {
+    LOG_WARN(
+        LOG_TAG,
+        "%s: peer %s start stream request ignored: "
+        "already initiated: sco_occupied:%s role:0x%x started:%s wait:0x%x",
+        __func__, p_scb->PeerAddress().ToString().c_str(),
+        logbool(bta_av_cb.sco_occupied).c_str(), p_scb->role,
+        logbool(p_scb->started).c_str(), p_scb->wait);
+    return;
+  }
+
+  p_scb->role |= BTA_AV_ROLE_START_INT;
+  bta_sys_busy(BTA_ID_AV, bta_av_cb.audio_open_cnt, p_scb->PeerAddress());
+  uint16_t result = AVDT_StartReq(&p_scb->avdt_handle, 1);
+  if (result != AVDT_SUCCESS) {
+    LOG_ERROR(LOG_TAG, "%s: AVDT_StartReq failed for peer %s result:%d",
+              __func__, p_scb->PeerAddress().ToString().c_str(), result);
+  }
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s start requested: sco_occupied:%s role:0x%x "
+           "started:%s wait:0x%x",
+           __func__, p_scb->PeerAddress().ToString().c_str(),
+           logbool(bta_av_cb.sco_occupied).c_str(), p_scb->role,
+           logbool(p_scb->started).c_str(), p_scb->wait);
 }
 
 /*******************************************************************************
@@ -1924,7 +1961,8 @@
   suspend_rsp.hndl = p_scb->hndl;
 
   if (p_data && p_data->api_stop.suspend) {
-    APPL_TRACE_DEBUG("%s: suspending: %d, sup:%d", __func__, start,
+    APPL_TRACE_DEBUG("%s: peer %s suspending: %d, sup:%d", __func__,
+                     p_scb->PeerAddress().ToString().c_str(), start,
                      p_scb->suspend_sup);
     if ((start) && (p_scb->suspend_sup)) {
       sus_evt = false;
@@ -2190,9 +2228,9 @@
   uint8_t cur_role;
   uint8_t local_tsep = p_scb->seps[p_scb->sep_idx].tsep;
 
-  APPL_TRACE_DEBUG("%s: peer %s handle:%d wait:0x%x role:0x%x local_tsep:%d",
-                   __func__, p_scb->PeerAddress().ToString().c_str(),
-                   p_scb->hndl, p_scb->wait, p_scb->role, local_tsep);
+  LOG_INFO(LOG_TAG, "%s: peer %s handle:%d wait:0x%x role:0x%x local_tsep:%d",
+           __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl,
+           p_scb->wait, p_scb->role, local_tsep);
 
   p_scb->started = true;
 
@@ -2870,6 +2908,12 @@
  ******************************************************************************/
 void bta_av_chk_2nd_start(tBTA_AV_SCB* p_scb,
                           UNUSED_ATTR tBTA_AV_DATA* p_data) {
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s channel:%d bta_av_cb.audio_open_cnt:%d role:0x%x "
+           "features:0x%x",
+           __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->chnl,
+           bta_av_cb.audio_open_cnt, p_scb->role, bta_av_cb.features);
+
   if ((p_scb->chnl == BTA_AV_CHNL_AUDIO) && (bta_av_cb.audio_open_cnt >= 2) &&
       (((p_scb->role & BTA_AV_ROLE_AD_ACP) == 0) ||  // Outgoing connection or
        (bta_av_cb.features & BTA_AV_FEAT_ACP_START))) {  // Auto-starting option
@@ -2880,10 +2924,18 @@
       bool new_started = false;
       for (int i = 0; i < BTA_AV_NUM_STRS; i++) {
         tBTA_AV_SCB* p_scbi = bta_av_cb.p_scb[i];
+        if (p_scb == p_scbi) {
+          continue;
+        }
         if (p_scbi && p_scbi->chnl == BTA_AV_CHNL_AUDIO && p_scbi->co_started) {
           if (!new_started) {
             // Start the new stream
             new_started = true;
+            LOG_INFO(LOG_TAG,
+                     "%s: starting new stream for peer %s because peer %s "
+                     "already started",
+                     __func__, p_scb->PeerAddress().ToString().c_str(),
+                     p_scbi->PeerAddress().ToString().c_str());
             bta_av_ssm_execute(p_scb, BTA_AV_AP_START_EVT, NULL);
           }
           // May need to update the flush timeout of this already started stream
diff --git a/bta/av/bta_av_act.cc b/bta/av/bta_av_act.cc
index bdfbb65..dfa85a2 100644
--- a/bta/av/bta_av_act.cc
+++ b/bta/av/bta_av_act.cc
@@ -1075,8 +1075,9 @@
 void bta_av_stream_chg(tBTA_AV_SCB* p_scb, bool started) {
   uint8_t started_msk = BTA_AV_HNDL_TO_MSK(p_scb->hdi);
 
-  APPL_TRACE_DEBUG("%s: started:%d started_msk:x%x", __func__, started,
-                   started_msk);
+  APPL_TRACE_DEBUG("%s: peer %s started:%s started_msk:0x%x", __func__,
+                   p_scb->PeerAddress().ToString().c_str(),
+                   logbool(started).c_str(), started_msk);
 
   if (started) {
     bta_av_cb.audio_streams |= started_msk;
diff --git a/bta/av/bta_av_api.cc b/bta/av/bta_av_api.cc
index 73ffc8d..bb5cca4 100644
--- a/bta/av/bta_av_api.cc
+++ b/bta/av/bta_av_api.cc
@@ -24,6 +24,8 @@
  *
  ******************************************************************************/
 
+#define LOG_TAG "bt_bta_av"
+
 #include <base/logging.h>
 
 #include "bt_target.h"
@@ -36,6 +38,7 @@
 #include "bta_sys.h"
 
 #include "osi/include/allocator.h"
+#include "osi/include/log.h"
 
 /*****************************************************************************
  *  Constants
@@ -153,9 +156,9 @@
  ******************************************************************************/
 void BTA_AvOpen(const RawAddress& bd_addr, tBTA_AV_HNDL handle, bool use_rc,
                 tBTA_SEC sec_mask, uint16_t uuid) {
-  APPL_TRACE_DEBUG("%s: peer %s handle:0x%x use_rc=%s sec_mask=0x%x uuid=0x%x",
-                   __func__, bd_addr.ToString().c_str(), handle,
-                   (use_rc) ? "true" : "false", sec_mask, uuid)
+  LOG_INFO(LOG_TAG, "%s: peer %s handle:0x%x use_rc=%s sec_mask=0x%x uuid=0x%x",
+           __func__, bd_addr.ToString().c_str(), handle,
+           (use_rc) ? "true" : "false", sec_mask, uuid);
 
   tBTA_AV_API_OPEN* p_buf =
       (tBTA_AV_API_OPEN*)osi_malloc(sizeof(tBTA_AV_API_OPEN));
@@ -181,7 +184,7 @@
  *
  ******************************************************************************/
 void BTA_AvClose(tBTA_AV_HNDL handle) {
-  APPL_TRACE_DEBUG("%s: handle:0x%x", __func__, handle);
+  LOG_INFO(LOG_TAG, "%s: handle:0x%x", __func__, handle);
 
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
@@ -201,6 +204,8 @@
  *
  ******************************************************************************/
 void BTA_AvDisconnect(const RawAddress& bd_addr) {
+  LOG_INFO(LOG_TAG, "%s: peer %s", __func__, bd_addr.ToString().c_str());
+
   tBTA_AV_API_DISCNT* p_buf =
       (tBTA_AV_API_DISCNT*)osi_malloc(sizeof(tBTA_AV_API_DISCNT));
 
@@ -220,6 +225,8 @@
  *
  ******************************************************************************/
 void BTA_AvStart(tBTA_AV_HNDL handle) {
+  LOG_INFO(LOG_TAG, "%s: handle=%d", __func__, handle);
+
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
   p_buf->event = BTA_AV_API_START_EVT;
@@ -238,6 +245,8 @@
  *
  ******************************************************************************/
 void BTA_AvOffloadStart(tBTA_AV_HNDL hndl) {
+  LOG_INFO(LOG_TAG, "%s: handle=%d", __func__, hndl);
+
   BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR));
 
   p_buf->event = BTA_AV_API_OFFLOAD_START_EVT;
@@ -278,6 +287,9 @@
  *
  ******************************************************************************/
 void BTA_AvStop(tBTA_AV_HNDL handle, bool suspend) {
+  LOG_INFO(LOG_TAG, "%s: handle=%d suspend=%s", __func__, handle,
+           logbool(suspend).c_str());
+
   tBTA_AV_API_STOP* p_buf =
       (tBTA_AV_API_STOP*)osi_malloc(sizeof(tBTA_AV_API_STOP));
 
@@ -306,6 +318,9 @@
 void BTA_AvReconfig(tBTA_AV_HNDL hndl, bool suspend, uint8_t sep_info_idx,
                     uint8_t* p_codec_info, uint8_t num_protect,
                     const uint8_t* p_protect_info) {
+  LOG_INFO(LOG_TAG, "%s: handle=%d suspend=%s sep_info_idx=%d", __func__, hndl,
+           logbool(suspend).c_str(), sep_info_idx);
+
   tBTA_AV_API_RCFG* p_buf =
       (tBTA_AV_API_RCFG*)osi_malloc(sizeof(tBTA_AV_API_RCFG) + num_protect);
 
diff --git a/bta/av/bta_av_main.cc b/bta/av/bta_av_main.cc
index e3b74db..6f11dca 100644
--- a/bta/av/bta_av_main.cc
+++ b/bta/av/bta_av_main.cc
@@ -839,6 +839,13 @@
       }
     }
   }
+
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s channel:%d bta_av_cb.audio_open_cnt:%d role:0x%x "
+           "features:0x%x start:%s",
+           __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->chnl,
+           bta_av_cb.audio_open_cnt, p_scb->role, bta_av_cb.features,
+           logbool(start).c_str());
   return start;
 }
 
@@ -1078,10 +1085,11 @@
   bool is_ok = true;
 
   if (BTM_GetRole(p_scb->PeerAddress(), &role) == BTM_SUCCESS) {
-    LOG_INFO(LOG_TAG,
-             "%s: hndl:x%x role:%d conn_audio:x%x bits:%d features:x%x",
-             __func__, p_scb->hndl, role, bta_av_cb.conn_audio, bits,
-             bta_av_cb.features);
+    LOG_INFO(
+        LOG_TAG,
+        "%s: peer %s hndl:0x%x role:%d conn_audio:0x%x bits:%d features:0x%x",
+        __func__, p_scb->PeerAddress().ToString().c_str(), p_scb->hndl, role,
+        bta_av_cb.conn_audio, bits, bta_av_cb.features);
     if (BTM_ROLE_MASTER != role &&
         (A2DP_BitsSet(bta_av_cb.conn_audio) > bits ||
          (bta_av_cb.features & BTA_AV_FEAT_MASTER))) {
@@ -1089,9 +1097,13 @@
         bta_sys_clear_policy(BTA_ID_AV, HCI_ENABLE_MASTER_SLAVE_SWITCH,
                              p_scb->PeerAddress());
 
-      if (BTM_CMD_STARTED !=
-          BTM_SwitchRole(p_scb->PeerAddress(), BTM_ROLE_MASTER, NULL)) {
+      tBTM_STATUS status =
+          BTM_SwitchRole(p_scb->PeerAddress(), BTM_ROLE_MASTER, NULL);
+      if (status != BTM_CMD_STARTED) {
         /* can not switch role on SCB - start the timer on SCB */
+        LOG_ERROR(LOG_TAG,
+                  "%s: peer %s BTM_SwitchRole(BTM_ROLE_MASTER) error: %d",
+                  __func__, p_scb->PeerAddress().ToString().c_str(), status);
       }
       is_ok = false;
       p_scb->wait |= BTA_AV_WAIT_ROLE_SW_RES_START;
diff --git a/bta/dm/bta_dm_act.cc b/bta/dm/bta_dm_act.cc
index a78a944..3460ecc 100644
--- a/bta/dm/bta_dm_act.cc
+++ b/bta/dm/bta_dm_act.cc
@@ -72,7 +72,7 @@
                                 BD_NAME bd_name, bool min_16_digit);
 static uint8_t bta_dm_new_link_key_cback(const RawAddress& bd_addr,
                                          DEV_CLASS dev_class, BD_NAME bd_name,
-                                         LINK_KEY key, uint8_t key_type);
+                                         const LinkKey& key, uint8_t key_type);
 static uint8_t bta_dm_authentication_complete_cback(const RawAddress& bd_addr,
                                                     DEV_CLASS dev_class,
                                                     BD_NAME bd_name,
@@ -343,7 +343,6 @@
   DEV_CLASS dev_class;
   tBTA_DM_SEC_CBACK* temp_cback;
   uint8_t key_mask = 0;
-  BT_OCTET16 er;
   tBTA_BLE_LOCAL_ID_KEYS id_key;
 
   APPL_TRACE_DEBUG("%s with event: %i", __func__, status);
@@ -406,7 +405,8 @@
     BTM_SetDeviceClass(dev_class);
 
     /* load BLE local information: ID keys, ER if available */
-    bta_dm_co_ble_load_local_keys(&key_mask, er, &id_key);
+    Octet16 er;
+    bta_dm_co_ble_load_local_keys(&key_mask, &er, &id_key);
 
     if (key_mask & BTA_BLE_LOCAL_KEY_TYPE_ER) {
       BTM_BleLoadLocalKeys(BTA_BLE_LOCAL_KEY_TYPE_ER,
@@ -705,11 +705,10 @@
  * Description      This function adds a Link Key to an security database entry.
  *                  It is normally called during host startup to restore all
  *                  required information stored in the NVRAM.
- ***
  ******************************************************************************/
 void bta_dm_add_device(std::unique_ptr<tBTA_DM_API_ADD_DEVICE> msg) {
   uint8_t* p_dc = NULL;
-  uint8_t* p_lc = NULL;
+  LinkKey* p_lc = NULL;
   uint32_t trusted_services_mask[BTM_SEC_SERVICE_ARRAY_SIZE];
   uint8_t index = 0;
   uint8_t btm_mask_index = 0;
@@ -719,7 +718,7 @@
   /* If not all zeros, the device class has been specified */
   if (msg->dc_known) p_dc = (uint8_t*)msg->dc;
 
-  if (msg->link_key_known) p_lc = (uint8_t*)msg->link_key;
+  if (msg->link_key_known) p_lc = &msg->link_key;
 
   if (msg->is_trusted) {
     /* covert BTA service mask to BTM mask */
@@ -2365,7 +2364,7 @@
  ******************************************************************************/
 static uint8_t bta_dm_new_link_key_cback(const RawAddress& bd_addr,
                                          UNUSED_ATTR DEV_CLASS dev_class,
-                                         BD_NAME bd_name, LINK_KEY key,
+                                         BD_NAME bd_name, const LinkKey& key,
                                          uint8_t key_type) {
   tBTA_DM_SEC sec_event;
   tBTA_DM_AUTH_CMPL* p_auth_cmpl;
@@ -2382,12 +2381,10 @@
 
     memcpy(p_auth_cmpl->bd_name, bd_name, (BD_NAME_LEN - 1));
     p_auth_cmpl->bd_name[BD_NAME_LEN - 1] = 0;
-
     p_auth_cmpl->key_present = true;
     p_auth_cmpl->key_type = key_type;
     p_auth_cmpl->success = true;
-
-    memcpy(p_auth_cmpl->key, key, LINK_KEY_LEN);
+    p_auth_cmpl->key = key;
     sec_event.auth_cmpl.fail_reason = HCI_SUCCESS;
 
     // Report the BR link key based on the BR/EDR address and type
@@ -2659,8 +2656,10 @@
 
   tBTA_DM_PEER_DEVICE* p_dev = bta_dm_find_peer_device(bd_addr);
   if (!p_dev) return;
-  APPL_TRACE_DEBUG("role chg info:x%x new_role:%d dev count:%d", p_dev->info,
-                   new_role, bta_dm_cb.device_list.count);
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s info:0x%x new_role:0x%x dev count:%d hci_status=%d",
+           __func__, bd_addr.ToString().c_str(), p_dev->info, new_role,
+           bta_dm_cb.device_list.count, hci_status);
   if (p_dev->info & BTA_DM_DI_AV_ACTIVE) {
     bool need_policy_change = false;
 
@@ -3850,6 +3849,8 @@
         bta_dm_remove_sec_dev_entry(bda);
       } else {
         sec_event.auth_cmpl.success = true;
+        if (!p_data->complt.smp_over_br)
+          GATT_ConfigServiceChangeCCC(bda, true, BT_TRANSPORT_LE);
       }
 
       if (bta_dm_cb.p_sec_cback) {
diff --git a/bta/dm/bta_dm_api.cc b/bta/dm/bta_dm_api.cc
index e968bd1..0740da0 100644
--- a/bta/dm/bta_dm_api.cc
+++ b/bta/dm/bta_dm_api.cc
@@ -296,7 +296,7 @@
  *
  ******************************************************************************/
 void BTA_DmAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                     LINK_KEY link_key, tBTA_SERVICE_MASK trusted_mask,
+                     const LinkKey& link_key, tBTA_SERVICE_MASK trusted_mask,
                      bool is_trusted, uint8_t key_type, tBTA_IO_CAP io_cap,
                      uint8_t pin_length) {
   std::unique_ptr<tBTA_DM_API_ADD_DEVICE> msg =
@@ -306,12 +306,9 @@
   msg->tm = trusted_mask;
   msg->is_trusted = is_trusted;
   msg->io_cap = io_cap;
-
-  if (link_key) {
-    msg->link_key_known = true;
-    msg->key_type = key_type;
-    memcpy(msg->link_key, link_key, LINK_KEY_LEN);
-  }
+  msg->link_key_known = true;
+  msg->key_type = key_type;
+  msg->link_key = link_key;
 
   /* Load device class if specified */
   if (dev_class) {
diff --git a/bta/dm/bta_dm_ci.cc b/bta/dm/bta_dm_ci.cc
index 3d40480..16174c0 100644
--- a/bta/dm/bta_dm_ci.cc
+++ b/bta/dm/bta_dm_ci.cc
@@ -60,15 +60,15 @@
  * Returns          void
  *
  ******************************************************************************/
-void bta_dm_ci_rmt_oob(bool accept, const RawAddress& bd_addr, BT_OCTET16 c,
-                       BT_OCTET16 r) {
+void bta_dm_ci_rmt_oob(bool accept, const RawAddress& bd_addr, const Octet16& c,
+                       const Octet16& r) {
   std::unique_ptr<tBTA_DM_CI_RMT_OOB> msg =
       std::make_unique<tBTA_DM_CI_RMT_OOB>();
 
   msg->bd_addr = bd_addr;
   msg->accept = accept;
-  memcpy(msg->c, c, BT_OCTET16_LEN);
-  memcpy(msg->r, r, BT_OCTET16_LEN);
+  msg->c = c;
+  msg->r = r;
 
   do_in_bta_thread(FROM_HERE,
                    base::Bind(bta_dm_ci_rmt_oob_act, base::Passed(&msg)));
diff --git a/bta/dm/bta_dm_int.h b/bta/dm/bta_dm_int.h
index 6aafd7d..0e2e9e3 100644
--- a/bta/dm/bta_dm_int.h
+++ b/bta/dm/bta_dm_int.h
@@ -118,8 +118,8 @@
 
 typedef struct {
   RawAddress bd_addr;
-  BT_OCTET16 c;
-  BT_OCTET16 r;
+  Octet16 c;
+  Octet16 r;
   bool accept;
 } tBTA_DM_CI_RMT_OOB;
 
@@ -150,7 +150,7 @@
 typedef struct {
   RawAddress bd_addr;
   DEV_CLASS dc;
-  LINK_KEY link_key;
+  LinkKey link_key;
   tBTA_SERVICE_MASK tm;
   bool is_trusted;
   uint8_t key_type;
@@ -250,7 +250,7 @@
 } tBTA_DM_SRVCS;
 
 #ifndef BTA_DM_NUM_CONN_SRVS
-#define BTA_DM_NUM_CONN_SRVS 10
+#define BTA_DM_NUM_CONN_SRVS 30
 #endif
 
 typedef struct {
diff --git a/bta/gatt/bta_gattc_act.cc b/bta/gatt/bta_gattc_act.cc
index eedd964..cb9d158 100644
--- a/bta/gatt/bta_gattc_act.cc
+++ b/bta/gatt/bta_gattc_act.cc
@@ -473,7 +473,7 @@
   if (p_clcb->p_srcb->mtu == 0) p_clcb->p_srcb->mtu = GATT_DEF_BLE_MTU_SIZE;
 
   /* start database cache if needed */
-  if (p_clcb->p_srcb->srvc_cache.empty() ||
+  if (p_clcb->p_srcb->gatt_database.IsEmpty() ||
       p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE) {
     if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) {
       p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD;
@@ -654,11 +654,9 @@
       /* set all srcb related clcb into discovery ST */
       bta_gattc_set_discover_st(p_clcb->p_srcb);
 
-      p_clcb->status = bta_gattc_init_cache(p_clcb->p_srcb);
-      if (p_clcb->status == GATT_SUCCESS) {
-        p_clcb->status = bta_gattc_discover_pri_service(
-            p_clcb->bta_conn_id, p_clcb->p_srcb, GATT_DISC_SRVC_ALL);
-      }
+      bta_gattc_init_cache(p_clcb->p_srcb);
+      p_clcb->status = bta_gattc_discover_pri_service(
+          p_clcb->bta_conn_id, p_clcb->p_srcb, GATT_DISC_SRVC_ALL);
       if (p_clcb->status != GATT_SUCCESS) {
         LOG(ERROR) << "discovery on server failed";
         bta_gattc_reset_discover_st(p_clcb->p_srcb, p_clcb->status);
@@ -692,16 +690,15 @@
   if (p_clcb->status != GATT_SUCCESS) {
     /* clean up cache */
     if (p_clcb->p_srcb) {
-      // clear reallocating
-      std::vector<tBTA_GATTC_SERVICE>().swap(p_clcb->p_srcb->srvc_cache);
+      p_clcb->p_srcb->gatt_database.Clear();
     }
 
     /* used to reset cache in application */
     bta_gattc_cache_reset(p_clcb->p_srcb->server_bda);
   }
+
   if (p_clcb->p_srcb) {
-    /* release pending attribute list buffer */
-    p_clcb->p_srcb->pending_discovery.clear();
+    p_clcb->p_srcb->pending_discovery.Clear();
   }
 
   if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) {
@@ -981,7 +978,7 @@
   tGATT_STATUS status = GATT_INTERNAL_ERROR;
   tBTA_GATTC cb_data;
   VLOG(1) << __func__ << ": conn_id=" << +p_clcb->bta_conn_id;
-  if (p_clcb->p_srcb && !p_clcb->p_srcb->srvc_cache.empty()) {
+  if (p_clcb->p_srcb && !p_clcb->p_srcb->gatt_database.IsEmpty()) {
     status = GATT_SUCCESS;
     /* search the local cache of a server device */
     bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid);
@@ -1105,8 +1102,7 @@
     }
     /* in all other cases, mark it and delete the cache */
 
-    // clear reallocating
-    std::vector<tBTA_GATTC_SERVICE>().swap(p_srvc_cb->srvc_cache);
+    p_srvc_cb->gatt_database.Clear();
   }
 
   /* used to reset cache in application */
@@ -1123,10 +1119,10 @@
   Uuid gattp_uuid = Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER);
   Uuid srvc_chg_uuid = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
 
-  const tBTA_GATTC_CHARACTERISTIC* p_char =
+  const gatt::Characteristic* p_char =
       bta_gattc_get_characteristic_srcb(p_srcb, p_notify->handle);
   if (!p_char) return false;
-  const tBTA_GATTC_SERVICE* p_svc =
+  const gatt::Service* p_svc =
       bta_gattc_get_service_for_handle_srcb(p_srcb, p_char->value_handle);
   if (!p_svc || p_svc->uuid != gattp_uuid || p_char->uuid != srvc_chg_uuid) {
     return false;
diff --git a/bta/gatt/bta_gattc_api.cc b/bta/gatt/bta_gattc_api.cc
index bec0081..c60d299 100644
--- a/bta/gatt/bta_gattc_api.cc
+++ b/bta/gatt/bta_gattc_api.cc
@@ -246,15 +246,14 @@
   bta_sys_sendmsg(p_buf);
 }
 
-void BTA_GATTC_DiscoverServiceByUuid(uint16_t conn_id,
-                                     const Uuid& p_srvc_uuid) {
-  tGATT_DISC_PARAM* param = new tGATT_DISC_PARAM;
-  param->s_handle = 0x0001;
-  param->e_handle = 0xFFFF;
-  param->service = p_srvc_uuid;
-  do_in_bta_thread(FROM_HERE,
-                   base::Bind(base::IgnoreResult(&GATTC_Discover), conn_id,
-                              GATT_DISC_SRVC_BY_UUID, base::Owned(param)));
+void BTA_GATTC_DiscoverServiceByUuid(uint16_t conn_id, const Uuid& srvc_uuid) {
+  do_in_bta_thread(
+      FROM_HERE,
+      base::Bind(
+          base::IgnoreResult<tGATT_STATUS (*)(uint16_t, tGATT_DISC_TYPE,
+                                              uint16_t, uint16_t, const Uuid&)>(
+              &GATTC_Discover),
+          conn_id, GATT_DISC_SRVC_BY_UUID, 0x0001, 0xFFFF, srvc_uuid));
 }
 
 /*******************************************************************************
@@ -266,10 +265,10 @@
  *
  * Parameters       conn_id: connection ID which identify the server.
  *
- * Returns          returns list of tBTA_GATTC_SERVICE or NULL.
+ * Returns          returns list of gatt::Service or NULL.
  *
  ******************************************************************************/
-const std::vector<tBTA_GATTC_SERVICE>* BTA_GATTC_GetServices(uint16_t conn_id) {
+const std::vector<gatt::Service>* BTA_GATTC_GetServices(uint16_t conn_id) {
   return bta_gattc_get_services(conn_id);
 }
 
@@ -283,11 +282,11 @@
  * Parameters       conn_id - connection ID which identify the server.
  *                  handle - characteristic handle
  *
- * Returns          returns pointer to tBTA_GATTC_CHARACTERISTIC or NULL.
+ * Returns          returns pointer to gatt::Characteristic or NULL.
  *
  ******************************************************************************/
-const tBTA_GATTC_CHARACTERISTIC* BTA_GATTC_GetCharacteristic(uint16_t conn_id,
-                                                             uint16_t handle) {
+const gatt::Characteristic* BTA_GATTC_GetCharacteristic(uint16_t conn_id,
+                                                        uint16_t handle) {
   return bta_gattc_get_characteristic(conn_id, handle);
 }
 
@@ -301,25 +300,25 @@
  * Parameters       conn_id - connection ID which identify the server.
  *                  handle - descriptor handle
  *
- * Returns          returns pointer to tBTA_GATTC_DESCRIPTOR or NULL.
+ * Returns          returns pointer to gatt::Descriptor or NULL.
  *
  ******************************************************************************/
-const tBTA_GATTC_DESCRIPTOR* BTA_GATTC_GetDescriptor(uint16_t conn_id,
-                                                     uint16_t handle) {
+const gatt::Descriptor* BTA_GATTC_GetDescriptor(uint16_t conn_id,
+                                                uint16_t handle) {
   return bta_gattc_get_descriptor(conn_id, handle);
 }
 
 /* Return characteristic that owns descriptor with handle equal to |handle|, or
  * NULL */
-const tBTA_GATTC_CHARACTERISTIC* BTA_GATTC_GetOwningCharacteristic(
-    uint16_t conn_id, uint16_t handle) {
+const gatt::Characteristic* BTA_GATTC_GetOwningCharacteristic(uint16_t conn_id,
+                                                              uint16_t handle) {
   return bta_gattc_get_owning_characteristic(conn_id, handle);
 }
 
 /* Return service that owns descriptor or characteristic with handle equal to
  * |handle|, or NULL */
-const tBTA_GATTC_SERVICE* BTA_GATTC_GetOwningService(uint16_t conn_id,
-                                                     uint16_t handle) {
+const gatt::Service* BTA_GATTC_GetOwningService(uint16_t conn_id,
+                                                uint16_t handle) {
   return bta_gattc_get_service_for_handle(conn_id, handle);
 }
 
diff --git a/bta/gatt/bta_gattc_cache.cc b/bta/gatt/bta_gattc_cache.cc
index 9f8c969..6c793f1 100644
--- a/bta/gatt/bta_gattc_cache.cc
+++ b/bta/gatt/bta_gattc_cache.cc
@@ -31,6 +31,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
+#include <sstream>
 
 #include "bt_common.h"
 #include "bta_gattc_int.h"
@@ -38,25 +39,34 @@
 #include "btm_api.h"
 #include "btm_ble_api.h"
 #include "btm_int.h"
+#include "database.h"
+#include "database_builder.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
 #include "sdp_api.h"
 #include "sdpdefs.h"
 #include "utl.h"
 
-using bluetooth::Uuid;
 using base::StringPrintf;
+using bluetooth::Uuid;
+using gatt::Characteristic;
+using gatt::Database;
+using gatt::DatabaseBuilder;
+using gatt::Descriptor;
+using gatt::IncludedService;
+using gatt::Service;
+using gatt::StoredAttribute;
 
 static void bta_gattc_cache_write(const RawAddress& server_bda,
-                                  uint16_t num_attr, tBTA_GATTC_NV_ATTR* attr);
-static void bta_gattc_char_dscpt_disc_cmpl(uint16_t conn_id,
-                                           tBTA_GATTC_SERV* p_srvc_cb);
+                                  const std::vector<StoredAttribute>& attr);
 static tGATT_STATUS bta_gattc_sdp_service_disc(uint16_t conn_id,
                                                tBTA_GATTC_SERV* p_server_cb);
-const tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor_srcb(
-    tBTA_GATTC_SERV* p_srcb, uint16_t handle);
-tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic_srcb(
-    tBTA_GATTC_SERV* p_srcb, uint16_t handle);
+const Descriptor* bta_gattc_get_descriptor_srcb(tBTA_GATTC_SERV* p_srcb,
+                                                uint16_t handle);
+const Characteristic* bta_gattc_get_characteristic_srcb(tBTA_GATTC_SERV* p_srcb,
+                                                        uint16_t handle);
+static void bta_gattc_explore_srvc_finished(uint16_t conn_id,
+                                            tBTA_GATTC_SERV* p_srvc_cb);
 
 #define BTA_GATT_SDP_DB_SIZE 4096
 
@@ -83,195 +93,42 @@
 /* utility functions */
 
 /* debug function to display the server cache */
-static void display_db(const std::vector<tBTA_GATTC_SERVICE>& cache) {
-  for (const tBTA_GATTC_SERVICE& service : cache) {
-    LOG(ERROR) << "Service: s_handle=" << loghex(service.s_handle)
-               << ", e_handle=" << loghex(service.e_handle)
-               << ", inst=" << loghex(service.handle)
-               << ", uuid=" << service.uuid;
-
-    if (service.characteristics.empty()) {
-      LOG(ERROR) << "\t No characteristics";
-      continue;
-    }
-
-    for (const tBTA_GATTC_CHARACTERISTIC& c : service.characteristics) {
-      LOG(ERROR) << "\t Characteristic value_handle=" << loghex(c.value_handle)
-                 << ", uuid=" << c.uuid << ", prop=" << loghex(c.properties);
-
-      if (c.descriptors.empty()) {
-        LOG(ERROR) << "\t\t No descriptors";
-        continue;
-      }
-
-      for (const tBTA_GATTC_DESCRIPTOR& d : c.descriptors) {
-        LOG(ERROR) << "\t\t Descriptor handle=" << loghex(d.handle)
-                   << ", uuid=" << d.uuid;
-      }
-    }
+static void bta_gattc_display_cache_server(const Database& database) {
+  LOG(INFO) << "<================Start Server Cache =============>";
+  std::istringstream iss(database.ToString());
+  for (std::string line; std::getline(iss, line);) {
+    LOG(INFO) << line;
   }
-}
-
-/* debug function to display the server cache */
-static void bta_gattc_display_cache_server(
-    const std::vector<tBTA_GATTC_SERVICE>& cache) {
-  LOG(ERROR) << "<================Start Server Cache =============>";
-  display_db(cache);
-  LOG(ERROR) << "<================End Server Cache =============>";
-  LOG(ERROR) << " ";
+  LOG(INFO) << "<================End Server Cache =============>";
 }
 
 /** debug function to display the exploration list */
-static void bta_gattc_display_explore_record(
-    const std::vector<tBTA_GATTC_SERVICE>& cache) {
-  LOG(ERROR) << "<================Start Explore Queue =============>";
-  display_db(cache);
-  LOG(ERROR) << "<================ End Explore Queue =============>";
-  LOG(ERROR) << " ";
+static void bta_gattc_display_explore_record(const DatabaseBuilder& database) {
+  LOG(INFO) << "<================Start Explore Queue =============>";
+  std::istringstream iss(database.ToString());
+  for (std::string line; std::getline(iss, line);) {
+    LOG(INFO) << line;
+  }
+  LOG(INFO) << "<================ End Explore Queue =============>";
 }
 #endif /* BTA_GATT_DEBUG == TRUE */
 
-/*******************************************************************************
- *
- * Function         bta_gattc_init_cache
- *
- * Description      Initialize the database cache and discovery related
- *                  resources.
- *
- * Returns          status
- *
- ******************************************************************************/
-tGATT_STATUS bta_gattc_init_cache(tBTA_GATTC_SERV* p_srvc_cb) {
-  // clear reallocating
-  std::vector<tBTA_GATTC_SERVICE>().swap(p_srvc_cb->srvc_cache);
-  std::vector<tBTA_GATTC_SERVICE>().swap(p_srvc_cb->pending_discovery);
-  return GATT_SUCCESS;
+/** Initialize the database cache and discovery related resources */
+void bta_gattc_init_cache(tBTA_GATTC_SERV* p_srvc_cb) {
+  p_srvc_cb->gatt_database = gatt::Database();
+  p_srvc_cb->pending_discovery.Clear();
 }
 
-tBTA_GATTC_SERVICE* bta_gattc_find_matching_service(
-    std::vector<tBTA_GATTC_SERVICE>& services, uint16_t handle) {
-  for (tBTA_GATTC_SERVICE& service : services) {
-    if (handle >= service.s_handle && handle <= service.e_handle)
+const Service* bta_gattc_find_matching_service(
+    const std::vector<Service>& services, uint16_t handle) {
+  for (const Service& service : services) {
+    if (handle >= service.handle && handle <= service.end_handle)
       return &service;
   }
 
   return nullptr;
 }
 
-/** Add a service into GATT database */
-static void add_service_to_gatt_db(std::vector<tBTA_GATTC_SERVICE>& gatt_db,
-                                   uint16_t s_handle, uint16_t e_handle,
-                                   const Uuid& uuid, bool is_primary) {
-#if (BTA_GATT_DEBUG == TRUE)
-  VLOG(1) << "Add a service into GATT DB";
-#endif
-
-  gatt_db.emplace_back(tBTA_GATTC_SERVICE{
-      .s_handle = s_handle,
-      .e_handle = e_handle,
-      .is_primary = is_primary,
-      .uuid = uuid,
-      .handle = s_handle,
-  });
-}
-
-/** Add a characteristic into GATT database */
-static void add_characteristic_to_gatt_db(
-    std::vector<tBTA_GATTC_SERVICE>& gatt_db, uint16_t attribute_handle,
-    uint16_t value_handle, const Uuid& uuid, uint8_t property) {
-#if (BTA_GATT_DEBUG == TRUE)
-  VLOG(1) << __func__
-          << ": Add a characteristic into service. handle:" << +value_handle
-          << " uuid:" << uuid << " property=0x" << std::hex << +property;
-#endif
-
-  tBTA_GATTC_SERVICE* service =
-      bta_gattc_find_matching_service(gatt_db, attribute_handle);
-  if (!service) {
-    LOG(ERROR) << "Illegal action to add char/descr/incl srvc for non-existing "
-                  "service!";
-    return;
-  }
-
-  /* TODO(jpawlowski): We should use attribute handle, not value handle to refer
-     to characteristic.
-     This is just a temporary workaround.
-  */
-  if (service->e_handle < value_handle) service->e_handle = value_handle;
-
-  service->characteristics.emplace_back(
-      tBTA_GATTC_CHARACTERISTIC{.declaration_handle = attribute_handle,
-                                .value_handle = value_handle,
-                                .properties = property,
-                                .uuid = uuid});
-  return;
-}
-
-/* Add an descriptor into database cache buffer */
-static void add_descriptor_to_gatt_db(std::vector<tBTA_GATTC_SERVICE>& gatt_db,
-                                      uint16_t handle, const Uuid& uuid) {
-#if (BTA_GATT_DEBUG == TRUE)
-  VLOG(1) << __func__ << ": add descriptor, handle=" << loghex(handle)
-          << ", uuid=" << uuid;
-#endif
-
-  tBTA_GATTC_SERVICE* service =
-      bta_gattc_find_matching_service(gatt_db, handle);
-  if (!service) {
-    LOG(ERROR) << "Illegal action to add descriptor for non-existing service!";
-    return;
-  }
-
-  if (service->characteristics.empty()) {
-    LOG(ERROR) << __func__
-               << ": Illegal action to add descriptor before adding a "
-                  "characteristic!";
-    return;
-  }
-
-  tBTA_GATTC_CHARACTERISTIC* char_node = &service->characteristics.front();
-  for (auto it = service->characteristics.begin();
-       it != service->characteristics.end(); it++) {
-    if (it->value_handle > handle) break;
-    char_node = &(*it);
-  }
-
-  char_node->descriptors.emplace_back(
-      tBTA_GATTC_DESCRIPTOR{.handle = handle, .uuid = uuid});
-}
-
-/* Add an attribute into database cache buffer */
-static void add_incl_srvc_to_gatt_db(std::vector<tBTA_GATTC_SERVICE>& gatt_db,
-                                     uint16_t handle, const Uuid& uuid,
-                                     uint16_t incl_srvc_s_handle) {
-#if (BTA_GATT_DEBUG == TRUE)
-  VLOG(1) << __func__ << ": add included service, handle=" << loghex(handle)
-          << ", uuid=" << uuid;
-#endif
-
-  tBTA_GATTC_SERVICE* service =
-      bta_gattc_find_matching_service(gatt_db, handle);
-  if (!service) {
-    LOG(ERROR) << "Illegal action to add incl srvc for non-existing service!";
-    return;
-  }
-
-  tBTA_GATTC_SERVICE* included_service =
-      bta_gattc_find_matching_service(gatt_db, incl_srvc_s_handle);
-  if (!included_service) {
-    LOG(ERROR) << __func__
-               << ": Illegal action to add non-existing included service!";
-    return;
-  }
-
-  service->included_svc.emplace_back(tBTA_GATTC_INCLUDED_SVC{
-      .handle = handle,
-      .uuid = uuid,
-      .owning_service = service,
-      .included_service = included_service,
-  });
-}
-
 /** Start primary service discovery */
 tGATT_STATUS bta_gattc_discover_pri_service(uint16_t conn_id,
                                             tBTA_GATTC_SERV* p_server_cb,
@@ -280,111 +137,85 @@
   if (!p_clcb) return GATT_ERROR;
 
   if (p_clcb->transport == BTA_TRANSPORT_LE) {
-    tGATT_DISC_PARAM param{.s_handle = 0x0001, .e_handle = 0xFFFF};
-    return GATTC_Discover(conn_id, disc_type, &param);
+    return GATTC_Discover(conn_id, disc_type, 0x0001, 0xFFFF);
   }
 
+  // only for Classic transport
   return bta_gattc_sdp_service_disc(conn_id, p_server_cb);
 }
 
+/** start exploring next service, or finish discovery if no more services left
+ */
+static void bta_gattc_explore_next_service(uint16_t conn_id,
+                                           tBTA_GATTC_SERV* p_srvc_cb) {
+  tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
+  if (!p_clcb) {
+    LOG(ERROR) << "unknown conn_id=" << loghex(conn_id);
+    return;
+  }
+
+  if (!p_srvc_cb->pending_discovery.StartNextServiceExploration()) {
+    bta_gattc_explore_srvc_finished(conn_id, p_srvc_cb);
+    return;
+  }
+
+  const auto& service = p_srvc_cb->pending_discovery.CurrentlyExploredService();
+  VLOG(1) << "Start service discovery";
+
+  /* start discovering included services */
+  GATTC_Discover(conn_id, GATT_DISC_INC_SRVC, service.first, service.second);
+}
+
+static void bta_gattc_explore_srvc_finished(uint16_t conn_id,
+                                            tBTA_GATTC_SERV* p_srvc_cb) {
+  tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
+  if (!p_clcb) {
+    LOG(ERROR) << "unknown conn_id=" << loghex(conn_id);
+    return;
+  }
+
+  /* no service found at all, the end of server discovery*/
+  LOG(INFO) << __func__ << ": service discovery finished";
+
+  p_srvc_cb->gatt_database = p_srvc_cb->pending_discovery.Build();
+
+#if (BTA_GATT_DEBUG == TRUE)
+  bta_gattc_display_cache_server(p_srvc_cb->gatt_database);
+#endif
+  /* save cache to NV */
+  p_clcb->p_srcb->state = BTA_GATTC_SERV_SAVE;
+
+  if (btm_sec_is_a_bonded_dev(p_srvc_cb->server_bda)) {
+    bta_gattc_cache_write(p_clcb->p_srcb->server_bda,
+                          p_clcb->p_srcb->gatt_database.Serialize());
+  }
+
+  bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
+}
+
 /** Start discovery for characteristic descriptor */
 void bta_gattc_start_disc_char_dscp(uint16_t conn_id,
                                     tBTA_GATTC_SERV* p_srvc_cb) {
   VLOG(1) << "starting discover characteristics descriptor";
-  auto& characteristic = p_srvc_cb->pending_char;
 
-  uint16_t end_handle = 0xFFFF;
-  // if there are more characteristics in the service
-  if (std::next(p_srvc_cb->pending_char) !=
-      p_srvc_cb->pending_service->characteristics.end()) {
-    // end at beginning of next characteristic
-    end_handle = std::next(p_srvc_cb->pending_char)->declaration_handle - 1;
-  } else {
-    // end at the end of current service
-    end_handle = p_srvc_cb->pending_service->e_handle;
+  std::pair<uint16_t, uint16_t> range =
+      p_srvc_cb->pending_discovery.NextDescriptorRangeToExplore();
+  if (range == DatabaseBuilder::EXPLORE_END) {
+    goto descriptor_discovery_done;
   }
 
-  tGATT_DISC_PARAM param{
-      .s_handle = (uint16_t)(characteristic->value_handle + 1),
-      .e_handle = end_handle};
-  if (GATTC_Discover(conn_id, GATT_DISC_CHAR_DSCPT, &param) != 0) {
-    bta_gattc_char_dscpt_disc_cmpl(conn_id, p_srvc_cb);
+  if (GATTC_Discover(conn_id, GATT_DISC_CHAR_DSCPT, range.first,
+                     range.second) != 0) {
+    goto descriptor_discovery_done;
   }
-}
+  return;
 
-/** process the service discovery complete event */
-static void bta_gattc_explore_srvc(uint16_t conn_id,
-                                   tBTA_GATTC_SERV* p_srvc_cb) {
-  tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
-  if (!p_clcb) {
-    LOG(ERROR) << "unknown conn_id=" << +conn_id;
-    return;
-  }
-
-  /* start expore a service if there is service not been explored */
-  if (p_srvc_cb->pending_service != p_srvc_cb->pending_discovery.end()) {
-    auto& service = *p_srvc_cb->pending_service;
-    VLOG(1) << "Start service discovery";
-
-    /* start discovering included services */
-    tGATT_DISC_PARAM param = {.s_handle = service.s_handle,
-                              .e_handle = service.e_handle};
-    GATTC_Discover(conn_id, GATT_DISC_INC_SRVC, &param);
-    return;
-  }
-
-  /* no service found at all, the end of server discovery*/
-  LOG(INFO) << __func__ << ": no more services found";
-
-  p_srvc_cb->srvc_cache.swap(p_srvc_cb->pending_discovery);
-  std::vector<tBTA_GATTC_SERVICE>().swap(p_srvc_cb->pending_discovery);
-
-#if (BTA_GATT_DEBUG == TRUE)
-  bta_gattc_display_cache_server(p_srvc_cb->srvc_cache);
-#endif
-  /* save cache to NV */
-  p_clcb->p_srcb->state = BTA_GATTC_SERV_SAVE;
-
-  if (btm_sec_is_a_bonded_dev(p_srvc_cb->server_bda)) {
-    bta_gattc_cache_save(p_clcb->p_srcb, p_clcb->bta_conn_id);
-  }
-
-  bta_gattc_reset_discover_st(p_clcb->p_srcb, GATT_SUCCESS);
-}
-
-/** process the char descriptor discovery complete event */
-static void bta_gattc_char_dscpt_disc_cmpl(uint16_t conn_id,
-                                           tBTA_GATTC_SERV* p_srvc_cb) {
-  ++p_srvc_cb->pending_char;
-  if (p_srvc_cb->pending_char !=
-      p_srvc_cb->pending_service->characteristics.end()) {
-    /* start discoverying next characteristic for char descriptor */
-    bta_gattc_start_disc_char_dscp(conn_id, p_srvc_cb);
-    return;
-  }
-
+descriptor_discovery_done:
   /* all characteristic has been explored, start with next service if any */
-#if (BTA_GATT_DEBUG == TRUE)
-  LOG(ERROR) << "all char has been explored";
-#endif
-  p_srvc_cb->pending_service++;
-  bta_gattc_explore_srvc(conn_id, p_srvc_cb);
-}
+  DVLOG(3) << "all characteristics explored";
 
-static bool bta_gattc_srvc_in_list(std::vector<tBTA_GATTC_SERVICE>& services,
-                                   uint16_t s_handle, uint16_t e_handle, Uuid) {
-  if (!GATT_HANDLE_IS_VALID(s_handle) || !GATT_HANDLE_IS_VALID(e_handle)) {
-    LOG(ERROR) << "invalid included service s_handle=" << loghex(s_handle)
-               << ", e_handle=" << loghex(e_handle);
-    return true;
-  }
-
-  for (tBTA_GATTC_SERVICE& service : services) {
-    if (service.s_handle == s_handle || service.e_handle == e_handle)
-      return true;
-  }
-
-  return false;
+  bta_gattc_explore_next_service(conn_id, p_srvc_cb);
+  return;
 }
 
 /* Process the discovery result from sdp */
@@ -399,64 +230,61 @@
     return;
   }
 
-  bool no_pending_disc = p_srvc_cb->pending_discovery.empty();
+  if ((sdp_status != SDP_SUCCESS) && (sdp_status != SDP_DB_FULL)) {
+    bta_gattc_explore_srvc_finished(cb_data->sdp_conn_id, p_srvc_cb);
 
-  if ((sdp_status == SDP_SUCCESS) || (sdp_status == SDP_DB_FULL)) {
-    tSDP_DISC_REC* p_sdp_rec =
-        SDP_FindServiceInDb(cb_data->p_sdp_db, 0, nullptr);
-    while (p_sdp_rec != nullptr) {
-      /* find a service record, report it */
-      Uuid service_uuid;
-      if (!SDP_FindServiceUUIDInRec(p_sdp_rec, &service_uuid)) continue;
+    /* allocated in bta_gattc_sdp_service_disc */
+    osi_free(cb_data);
+    return;
+  }
 
-      tSDP_PROTOCOL_ELEM pe;
-      if (!SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_ATT, &pe))
-        continue;
+  bool no_pending_disc = !p_srvc_cb->pending_discovery.InProgress();
 
-      uint16_t start_handle = (uint16_t)pe.params[0];
-      uint16_t end_handle = (uint16_t)pe.params[1];
+  tSDP_DISC_REC* p_sdp_rec = SDP_FindServiceInDb(cb_data->p_sdp_db, 0, nullptr);
+  while (p_sdp_rec != nullptr) {
+    /* find a service record, report it */
+    Uuid service_uuid;
+    if (!SDP_FindServiceUUIDInRec(p_sdp_rec, &service_uuid)) continue;
+
+    tSDP_PROTOCOL_ELEM pe;
+    if (!SDP_FindProtocolListElemInRec(p_sdp_rec, UUID_PROTOCOL_ATT, &pe))
+      continue;
+
+    uint16_t start_handle = (uint16_t)pe.params[0];
+    uint16_t end_handle = (uint16_t)pe.params[1];
 
 #if (BTA_GATT_DEBUG == TRUE)
-      VLOG(1) << "Found ATT service uuid=" << service_uuid
-              << ", s_handle=" << loghex(start_handle)
-              << ", e_handle=" << loghex(end_handle);
+    VLOG(1) << "Found ATT service uuid=" << service_uuid
+            << ", s_handle=" << loghex(start_handle)
+            << ", e_handle=" << loghex(end_handle);
 #endif
 
-      if (!GATT_HANDLE_IS_VALID(start_handle) ||
-          !GATT_HANDLE_IS_VALID(end_handle)) {
-        LOG(ERROR) << "invalid start_handle=" << loghex(start_handle)
-                   << ", end_handle=" << loghex(end_handle);
-        continue;
-      }
-
-      /* discover services result, add services into a service list */
-      add_service_to_gatt_db(p_srvc_cb->pending_discovery, start_handle,
-                             end_handle, service_uuid, true);
-
-      p_sdp_rec = SDP_FindServiceInDb(cb_data->p_sdp_db, 0, p_sdp_rec);
+    if (!GATT_HANDLE_IS_VALID(start_handle) ||
+        !GATT_HANDLE_IS_VALID(end_handle)) {
+      LOG(ERROR) << "invalid start_handle=" << loghex(start_handle)
+                 << ", end_handle=" << loghex(end_handle);
+      continue;
     }
+
+    /* discover services result, add services into a service list */
+    p_srvc_cb->pending_discovery.AddService(start_handle, end_handle,
+                                            service_uuid, true);
+
+    p_sdp_rec = SDP_FindServiceInDb(cb_data->p_sdp_db, 0, p_sdp_rec);
   }
 
+  // If discovery is already pending, no need to call
+  // bta_gattc_explore_next_service. Next service will be picked up to discovery
+  // once current one is discovered. If discovery is not pending, start one
   if (no_pending_disc) {
-    p_srvc_cb->pending_service = p_srvc_cb->pending_discovery.begin();
+    bta_gattc_explore_next_service(cb_data->sdp_conn_id, p_srvc_cb);
   }
 
-  /* start discover primary service */
-  bta_gattc_explore_srvc(cb_data->sdp_conn_id, p_srvc_cb);
-
   /* allocated in bta_gattc_sdp_service_disc */
   osi_free(cb_data);
 }
 
-/*******************************************************************************
- *
- * Function         bta_gattc_sdp_service_disc
- *
- * Description      Start DSP Service Discovert
- *
- * Returns          void
- *
- ******************************************************************************/
+/* Start DSP Service Discovery */
 static tGATT_STATUS bta_gattc_sdp_service_disc(uint16_t conn_id,
                                                tBTA_GATTC_SERV* p_server_cb) {
   uint16_t num_attrs = 2;
@@ -499,43 +327,27 @@
   switch (disc_type) {
     case GATT_DISC_SRVC_ALL:
     case GATT_DISC_SRVC_BY_UUID:
-      /* discover services result, add services into a service list */
-      add_service_to_gatt_db(p_srvc_cb->pending_discovery, p_data->handle,
-                             p_data->value.group_value.e_handle,
-                             p_data->value.group_value.service_type, true);
+      p_srvc_cb->pending_discovery.AddService(
+          p_data->handle, p_data->value.group_value.e_handle,
+          p_data->value.group_value.service_type, true);
       break;
 
     case GATT_DISC_INC_SRVC:
-      /* add included service into service list if it's secondary or it never
-         showed up in the primary service search */
-      if (!bta_gattc_srvc_in_list(p_srvc_cb->pending_discovery,
-                                  p_data->value.incl_service.s_handle,
-                                  p_data->value.incl_service.e_handle,
-                                  p_data->value.incl_service.service_type)) {
-        add_service_to_gatt_db(p_srvc_cb->pending_discovery,
-                               p_data->value.incl_service.s_handle,
-                               p_data->value.incl_service.e_handle,
-                               p_data->value.incl_service.service_type, false);
-      }
-
-      /* add into database */
-      add_incl_srvc_to_gatt_db(p_srvc_cb->pending_discovery, p_data->handle,
-                               p_data->value.incl_service.service_type,
-                               p_data->value.incl_service.s_handle);
+      p_srvc_cb->pending_discovery.AddIncludedService(
+          p_data->handle, p_data->value.incl_service.service_type,
+          p_data->value.incl_service.s_handle,
+          p_data->value.incl_service.e_handle);
       break;
 
     case GATT_DISC_CHAR:
-      /* add char value into database */
-      add_characteristic_to_gatt_db(p_srvc_cb->pending_discovery,
-                                    p_data->handle,
-                                    p_data->value.dclr_value.val_handle,
-                                    p_data->value.dclr_value.char_uuid,
-                                    p_data->value.dclr_value.char_prop);
+      p_srvc_cb->pending_discovery.AddCharacteristic(
+          p_data->handle, p_data->value.dclr_value.val_handle,
+          p_data->value.dclr_value.char_uuid,
+          p_data->value.dclr_value.char_prop);
       break;
 
     case GATT_DISC_CHAR_DSCPT:
-      add_descriptor_to_gatt_db(p_srvc_cb->pending_discovery, p_data->handle,
-                                p_data->type);
+      p_srvc_cb->pending_discovery.AddDescriptor(p_data->handle, p_data->type);
       break;
   }
 }
@@ -561,18 +373,13 @@
 #if (BTA_GATT_DEBUG == TRUE)
       bta_gattc_display_explore_record(p_srvc_cb->pending_discovery);
 #endif
-      p_srvc_cb->pending_service = p_srvc_cb->pending_discovery.begin();
-      bta_gattc_explore_srvc(conn_id, p_srvc_cb);
+      bta_gattc_explore_next_service(conn_id, p_srvc_cb);
       break;
 
     case GATT_DISC_INC_SRVC: {
-      auto& service = *p_srvc_cb->pending_service;
-
-      /* start discoverying characteristic */
-
-      tGATT_DISC_PARAM param = {.s_handle = service.s_handle,
-                                .e_handle = service.e_handle};
-      GATTC_Discover(conn_id, GATT_DISC_CHAR, &param);
+      auto& service = p_srvc_cb->pending_discovery.CurrentlyExploredService();
+      /* start discovering characteristic */
+      GATTC_Discover(conn_id, GATT_DISC_CHAR, service.first, service.second);
       break;
     }
 
@@ -580,33 +387,25 @@
 #if (BTA_GATT_DEBUG == TRUE)
       bta_gattc_display_explore_record(p_srvc_cb->pending_discovery);
 #endif
-      auto& service = *p_srvc_cb->pending_service;
-      if (!service.characteristics.empty()) {
-        /* discover descriptors */
-        p_srvc_cb->pending_char = service.characteristics.begin();
-        bta_gattc_start_disc_char_dscp(conn_id, p_srvc_cb);
-        return;
-      }
-      /* start next service */
-      ++p_srvc_cb->pending_service;
-      bta_gattc_explore_srvc(conn_id, p_srvc_cb);
+      bta_gattc_start_disc_char_dscp(conn_id, p_srvc_cb);
       break;
     }
 
     case GATT_DISC_CHAR_DSCPT:
-      bta_gattc_char_dscpt_disc_cmpl(conn_id, p_srvc_cb);
+      /* start discovering next characteristic for char descriptor */
+      bta_gattc_start_disc_char_dscp(conn_id, p_srvc_cb);
       break;
   }
 }
 
 /** search local cache for matching service record */
 void bta_gattc_search_service(tBTA_GATTC_CLCB* p_clcb, Uuid* p_uuid) {
-  for (const tBTA_GATTC_SERVICE& service : p_clcb->p_srcb->srvc_cache) {
+  for (const Service& service : p_clcb->p_srcb->gatt_database.Services()) {
     if (p_uuid && *p_uuid != service.uuid) continue;
 
 #if (BTA_GATT_DEBUG == TRUE)
     VLOG(1) << __func__ << "found service " << service.uuid
-            << ", inst:" << +service.handle << " handle:" << +service.s_handle;
+            << " handle:" << +service.handle;
 #endif
     if (!p_clcb->p_rcb->p_cback) continue;
 
@@ -620,14 +419,14 @@
   }
 }
 
-std::vector<tBTA_GATTC_SERVICE>* bta_gattc_get_services_srcb(
+const std::vector<Service>* bta_gattc_get_services_srcb(
     tBTA_GATTC_SERV* p_srcb) {
-  if (!p_srcb || p_srcb->srvc_cache.empty()) return NULL;
+  if (!p_srcb || p_srcb->gatt_database.IsEmpty()) return NULL;
 
-  return &p_srcb->srvc_cache;
+  return &p_srcb->gatt_database.Services();
 }
 
-std::vector<tBTA_GATTC_SERVICE>* bta_gattc_get_services(uint16_t conn_id) {
+const std::vector<Service>* bta_gattc_get_services(uint16_t conn_id) {
   tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
 
   if (p_clcb == NULL) return NULL;
@@ -637,38 +436,37 @@
   return bta_gattc_get_services_srcb(p_srcb);
 }
 
-tBTA_GATTC_SERVICE* bta_gattc_get_service_for_handle_srcb(
-    tBTA_GATTC_SERV* p_srcb, uint16_t handle) {
-  std::vector<tBTA_GATTC_SERVICE>* services =
-      bta_gattc_get_services_srcb(p_srcb);
+const Service* bta_gattc_get_service_for_handle_srcb(tBTA_GATTC_SERV* p_srcb,
+                                                     uint16_t handle) {
+  const std::vector<Service>* services = bta_gattc_get_services_srcb(p_srcb);
   if (services == NULL) return NULL;
   return bta_gattc_find_matching_service(*services, handle);
 }
 
-const tBTA_GATTC_SERVICE* bta_gattc_get_service_for_handle(uint16_t conn_id,
-                                                           uint16_t handle) {
-  std::vector<tBTA_GATTC_SERVICE>* services = bta_gattc_get_services(conn_id);
+const Service* bta_gattc_get_service_for_handle(uint16_t conn_id,
+                                                uint16_t handle) {
+  const std::vector<Service>* services = bta_gattc_get_services(conn_id);
   if (services == NULL) return NULL;
 
   return bta_gattc_find_matching_service(*services, handle);
 }
 
-tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic_srcb(
-    tBTA_GATTC_SERV* p_srcb, uint16_t handle) {
-  tBTA_GATTC_SERVICE* service =
+const Characteristic* bta_gattc_get_characteristic_srcb(tBTA_GATTC_SERV* p_srcb,
+                                                        uint16_t handle) {
+  const Service* service =
       bta_gattc_get_service_for_handle_srcb(p_srcb, handle);
 
   if (!service) return NULL;
 
-  for (tBTA_GATTC_CHARACTERISTIC& charac : service->characteristics) {
+  for (const Characteristic& charac : service->characteristics) {
     if (handle == charac.value_handle) return &charac;
   }
 
   return NULL;
 }
 
-tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic(uint16_t conn_id,
-                                                        uint16_t handle) {
+const Characteristic* bta_gattc_get_characteristic(uint16_t conn_id,
+                                                   uint16_t handle) {
   tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
 
   if (p_clcb == NULL) return NULL;
@@ -677,17 +475,17 @@
   return bta_gattc_get_characteristic_srcb(p_srcb, handle);
 }
 
-const tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor_srcb(
-    tBTA_GATTC_SERV* p_srcb, uint16_t handle) {
-  const tBTA_GATTC_SERVICE* service =
+const Descriptor* bta_gattc_get_descriptor_srcb(tBTA_GATTC_SERV* p_srcb,
+                                                uint16_t handle) {
+  const Service* service =
       bta_gattc_get_service_for_handle_srcb(p_srcb, handle);
 
   if (!service) {
     return NULL;
   }
 
-  for (const tBTA_GATTC_CHARACTERISTIC& charac : service->characteristics) {
-    for (const tBTA_GATTC_DESCRIPTOR& desc : charac.descriptors) {
+  for (const Characteristic& charac : service->characteristics) {
+    for (const Descriptor& desc : charac.descriptors) {
       if (handle == desc.handle) return &desc;
     }
   }
@@ -695,8 +493,7 @@
   return NULL;
 }
 
-const tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor(uint16_t conn_id,
-                                                      uint16_t handle) {
+const Descriptor* bta_gattc_get_descriptor(uint16_t conn_id, uint16_t handle) {
   tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
 
   if (p_clcb == NULL) return NULL;
@@ -705,15 +502,15 @@
   return bta_gattc_get_descriptor_srcb(p_srcb, handle);
 }
 
-tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_owning_characteristic_srcb(
+const Characteristic* bta_gattc_get_owning_characteristic_srcb(
     tBTA_GATTC_SERV* p_srcb, uint16_t handle) {
-  tBTA_GATTC_SERVICE* service =
+  const Service* service =
       bta_gattc_get_service_for_handle_srcb(p_srcb, handle);
 
   if (!service) return NULL;
 
-  for (tBTA_GATTC_CHARACTERISTIC& charac : service->characteristics) {
-    for (const tBTA_GATTC_DESCRIPTOR& desc : charac.descriptors) {
+  for (const Characteristic& charac : service->characteristics) {
+    for (const Descriptor& desc : charac.descriptors) {
       if (handle == desc.handle) return &charac;
     }
   }
@@ -721,8 +518,8 @@
   return NULL;
 }
 
-const tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_owning_characteristic(
-    uint16_t conn_id, uint16_t handle) {
+const Characteristic* bta_gattc_get_owning_characteristic(uint16_t conn_id,
+                                                          uint16_t handle) {
   tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
   if (!p_clcb) return NULL;
 
@@ -759,27 +556,27 @@
 /*******************************************************************************
  * Returns          number of elements inside db from start_handle to end_handle
  ******************************************************************************/
-static size_t bta_gattc_get_db_size(
-    const std::vector<tBTA_GATTC_SERVICE>& services, uint16_t start_handle,
-    uint16_t end_handle) {
+static size_t bta_gattc_get_db_size(const std::vector<Service>& services,
+                                    uint16_t start_handle,
+                                    uint16_t end_handle) {
   if (services.empty()) return 0;
 
   size_t db_size = 0;
 
-  for (const tBTA_GATTC_SERVICE& service : services) {
-    if (service.s_handle < start_handle) continue;
+  for (const Service& service : services) {
+    if (service.handle < start_handle) continue;
 
-    if (service.e_handle > end_handle) break;
+    if (service.end_handle > end_handle) break;
 
     db_size++;
 
-    for (const tBTA_GATTC_CHARACTERISTIC& charac : service.characteristics) {
+    for (const Characteristic& charac : service.characteristics) {
       db_size++;
 
       db_size += charac.descriptors.size();
     }
 
-    db_size += service.included_svc.size();
+    db_size += service.included_services.size();
   }
 
   return db_size;
@@ -808,39 +605,39 @@
           << StringPrintf(": start_handle 0x%04x, end_handle 0x%04x",
                           start_handle, end_handle);
 
-  if (p_srvc_cb->srvc_cache.empty()) {
+  if (p_srvc_cb->gatt_database.IsEmpty()) {
     *count = 0;
     *db = NULL;
     return;
   }
 
-  size_t db_size =
-      bta_gattc_get_db_size(p_srvc_cb->srvc_cache, start_handle, end_handle);
+  size_t db_size = bta_gattc_get_db_size(p_srvc_cb->gatt_database.Services(),
+                                         start_handle, end_handle);
 
   void* buffer = osi_malloc(db_size * sizeof(btgatt_db_element_t));
   btgatt_db_element_t* curr_db_attr = (btgatt_db_element_t*)buffer;
 
-  for (const tBTA_GATTC_SERVICE& service : p_srvc_cb->srvc_cache) {
-    if (service.s_handle < start_handle) continue;
+  for (const Service& service : p_srvc_cb->gatt_database.Services()) {
+    if (service.handle < start_handle) continue;
 
-    if (service.e_handle > end_handle) break;
+    if (service.end_handle > end_handle) break;
 
     bta_gattc_fill_gatt_db_el(curr_db_attr,
                               service.is_primary ? BTGATT_DB_PRIMARY_SERVICE
                                                  : BTGATT_DB_SECONDARY_SERVICE,
-                              0 /* att_handle */, service.s_handle,
-                              service.e_handle, service.s_handle, service.uuid,
+                              0 /* att_handle */, service.handle,
+                              service.end_handle, service.handle, service.uuid,
                               0 /* prop */);
     curr_db_attr++;
 
-    for (const tBTA_GATTC_CHARACTERISTIC& charac : service.characteristics) {
+    for (const Characteristic& charac : service.characteristics) {
       bta_gattc_fill_gatt_db_el(curr_db_attr, BTGATT_DB_CHARACTERISTIC,
                                 charac.value_handle, 0 /* s_handle */,
                                 0 /* e_handle */, charac.value_handle,
                                 charac.uuid, charac.properties);
       curr_db_attr++;
 
-      for (const tBTA_GATTC_DESCRIPTOR& desc : charac.descriptors) {
+      for (const Descriptor& desc : charac.descriptors) {
         bta_gattc_fill_gatt_db_el(
             curr_db_attr, BTGATT_DB_DESCRIPTOR, desc.handle, 0 /* s_handle */,
             0 /* e_handle */, desc.handle, desc.uuid, 0 /* property */);
@@ -848,11 +645,11 @@
       }
     }
 
-    for (const tBTA_GATTC_INCLUDED_SVC& p_isvc : service.included_svc) {
-      bta_gattc_fill_gatt_db_el(
-          curr_db_attr, BTGATT_DB_INCLUDED_SERVICE, p_isvc.handle,
-          p_isvc.included_service ? p_isvc.included_service->s_handle : 0,
-          0 /* e_handle */, p_isvc.handle, p_isvc.uuid, 0 /* property */);
+    for (const IncludedService& p_isvc : service.included_services) {
+      bta_gattc_fill_gatt_db_el(curr_db_attr, BTGATT_DB_INCLUDED_SERVICE,
+                                p_isvc.handle, p_isvc.start_handle,
+                                0 /* e_handle */, p_isvc.handle, p_isvc.uuid,
+                                0 /* property */);
       curr_db_attr++;
     }
   }
@@ -891,9 +688,8 @@
     return;
   }
 
-  if (!p_clcb->p_srcb ||
-      !p_clcb->p_srcb->pending_discovery.empty() || /* no active discovery */
-      p_clcb->p_srcb->srvc_cache.empty()) {
+  if (!p_clcb->p_srcb || p_clcb->p_srcb->pending_discovery.InProgress() ||
+      p_clcb->p_srcb->gatt_database.IsEmpty()) {
     LOG(ERROR) << "No server cache available";
     return;
   }
@@ -902,89 +698,6 @@
                              count);
 }
 
-namespace {
-const Uuid PRIMARY_SERVICE = Uuid::From16Bit(GATT_UUID_PRI_SERVICE);
-const Uuid SECONDARY_SERVICE = Uuid::From16Bit(GATT_UUID_SEC_SERVICE);
-const Uuid INCLUDE = Uuid::From16Bit(GATT_UUID_INCLUDE_SERVICE);
-const Uuid CHARACTERISTIC = Uuid::From16Bit(GATT_UUID_CHAR_DECLARE);
-}  // namespace
-
-/* rebuild server cache from NV cache */
-void bta_gattc_rebuild_cache(tBTA_GATTC_SERV* p_srvc_cb, uint16_t num_attr,
-                             tBTA_GATTC_NV_ATTR* p_attr) {
-  /* first attribute loading, initialize buffer */
-  LOG(INFO) << __func__ << " " << num_attr;
-
-  // clear reallocating
-  std::vector<tBTA_GATTC_SERVICE>().swap(p_srvc_cb->srvc_cache);
-
-  while (num_attr > 0 && p_attr != NULL) {
-    if (p_attr->type == PRIMARY_SERVICE || p_attr->type == SECONDARY_SERVICE) {
-      add_service_to_gatt_db(
-          p_srvc_cb->srvc_cache, p_attr->handle, p_attr->value.service.e_handle,
-          p_attr->value.service.uuid, (p_attr->type == PRIMARY_SERVICE));
-    } else if (p_attr->type == INCLUDE) {
-      add_incl_srvc_to_gatt_db(p_srvc_cb->srvc_cache, p_attr->handle,
-                               p_attr->value.included_service.uuid,
-                               p_attr->value.included_service.s_handle);
-    } else if (p_attr->type == CHARACTERISTIC) {
-      add_characteristic_to_gatt_db(p_srvc_cb->srvc_cache, p_attr->handle,
-                                    p_attr->value.characteristic.value_handle,
-                                    p_attr->value.characteristic.uuid,
-                                    p_attr->value.characteristic.properties);
-    } else {
-      add_descriptor_to_gatt_db(p_srvc_cb->srvc_cache, p_attr->handle,
-                                p_attr->type);
-    }
-
-    p_attr++;
-    num_attr--;
-  }
-}
-
-/** save the server cache into NV */
-void bta_gattc_cache_save(tBTA_GATTC_SERV* p_srvc_cb, uint16_t conn_id) {
-  if (p_srvc_cb->srvc_cache.empty()) return;
-
-  int i = 0;
-  size_t db_size = bta_gattc_get_db_size(p_srvc_cb->srvc_cache, 0x0000, 0xFFFF);
-  tBTA_GATTC_NV_ATTR* nv_attr =
-      (tBTA_GATTC_NV_ATTR*)osi_malloc(db_size * sizeof(tBTA_GATTC_NV_ATTR));
-
-  for (const tBTA_GATTC_SERVICE& service : p_srvc_cb->srvc_cache) {
-    nv_attr[i++] = {
-        service.s_handle,
-        service.is_primary ? PRIMARY_SERVICE : SECONDARY_SERVICE,
-        {.service = {.uuid = service.uuid, .e_handle = service.e_handle}}};
-  }
-
-  for (const tBTA_GATTC_SERVICE& service : p_srvc_cb->srvc_cache) {
-    for (const tBTA_GATTC_INCLUDED_SVC& p_isvc : service.included_svc) {
-      nv_attr[i++] = {
-          p_isvc.handle,
-          INCLUDE,
-          {.included_service = {.s_handle = p_isvc.included_service->s_handle,
-                                .e_handle = p_isvc.included_service->e_handle,
-                                .uuid = p_isvc.uuid}}};
-    }
-
-    for (const tBTA_GATTC_CHARACTERISTIC& charac : service.characteristics) {
-      nv_attr[i++] = {charac.declaration_handle,
-                      CHARACTERISTIC,
-                      {.characteristic = {.properties = charac.properties,
-                                          .value_handle = charac.value_handle,
-                                          .uuid = charac.uuid}}};
-
-      for (const tBTA_GATTC_DESCRIPTOR& desc : charac.descriptors) {
-        nv_attr[i++] = {desc.handle, desc.uuid, {}};
-      }
-    }
-  }
-
-  bta_gattc_cache_write(p_srvc_cb->server_bda, db_size, nv_attr);
-  osi_free(nv_attr);
-}
-
 /*******************************************************************************
  *
  * Function         bta_gattc_cache_load
@@ -1009,7 +722,6 @@
   }
 
   uint16_t cache_ver = 0;
-  tBTA_GATTC_NV_ATTR* attr = NULL;
   bool success = false;
   uint16_t num_attr = 0;
 
@@ -1029,19 +741,18 @@
     goto done;
   }
 
-  attr = (tBTA_GATTC_NV_ATTR*)osi_malloc(sizeof(tBTA_GATTC_NV_ATTR) * num_attr);
+  {
+    std::vector<StoredAttribute> attr(num_attr);
 
-  if (fread(attr, sizeof(tBTA_GATTC_NV_ATTR), num_attr, fd) != num_attr) {
-    LOG(ERROR) << __func__ << "s: can't read GATT attributes: " << fname;
-    goto done;
+    if (fread(attr.data(), sizeof(StoredAttribute), num_attr, fd) != num_attr) {
+      LOG(ERROR) << __func__ << "s: can't read GATT attributes: " << fname;
+      goto done;
+    }
+
+    p_clcb->p_srcb->gatt_database = gatt::Database::Deserialize(attr, &success);
   }
 
-  bta_gattc_rebuild_cache(p_clcb->p_srcb, num_attr, attr);
-
-  success = true;
-
 done:
-  osi_free(attr);
   fclose(fd);
   return success;
 }
@@ -1054,13 +765,12 @@
  *                  cache is available to save.
  *
  * Parameter        server_bda: server bd address of this cache belongs to
- *                  num_attr: number of attribute to be save.
- *                  attr: pointer to the list of attributes to save.
+ *                  attr: attributes to save.
  * Returns
  *
  ******************************************************************************/
 static void bta_gattc_cache_write(const RawAddress& server_bda,
-                                  uint16_t num_attr, tBTA_GATTC_NV_ATTR* attr) {
+                                  const std::vector<StoredAttribute>& attr) {
   char fname[255] = {0};
   bta_gattc_generate_cache_file_name(fname, sizeof(fname), server_bda);
 
@@ -1078,6 +788,7 @@
     return;
   }
 
+  uint16_t num_attr = attr.size();
   if (fwrite(&num_attr, sizeof(uint16_t), 1, fd) != 1) {
     LOG(ERROR) << __func__
                << ": can't write GATT cache attribute count: " << fname;
@@ -1085,7 +796,7 @@
     return;
   }
 
-  if (fwrite(attr, sizeof(tBTA_GATTC_NV_ATTR), num_attr, fd) != num_attr) {
+  if (fwrite(attr.data(), sizeof(StoredAttribute), num_attr, fd) != num_attr) {
     LOG(ERROR) << __func__ << ": can't write GATT cache attributes: " << fname;
     fclose(fd);
     return;
diff --git a/bta/gatt/bta_gattc_int.h b/bta/gatt/bta_gattc_int.h
index 5b33eae..2b60ac72 100644
--- a/bta/gatt/bta_gattc_int.h
+++ b/bta/gatt/bta_gattc_int.h
@@ -28,6 +28,7 @@
 
 #include "bta_gatt_api.h"
 #include "bta_sys.h"
+#include "database_builder.h"
 #include "osi/include/fixed_queue.h"
 
 #include "bt_common.h"
@@ -206,13 +207,11 @@
 
   uint8_t state;
 
-  std::vector<tBTA_GATTC_SERVICE> srvc_cache;
+  gatt::Database gatt_database;
   uint8_t update_count; /* indication received */
   uint8_t num_clcb;     /* number of associated CLCB */
 
-  std::vector<tBTA_GATTC_SERVICE> pending_discovery;
-  std::vector<tBTA_GATTC_SERVICE>::iterator pending_service;
-  std::vector<tBTA_GATTC_CHARACTERISTIC>::iterator pending_char;
+  gatt::DatabaseBuilder pending_discovery;
 
   uint8_t srvc_hdl_chg; /* service handle change indication pending */
   uint16_t attr_index;  /* cahce NV saving/loading attribute index */
@@ -427,27 +426,24 @@
                                                    uint8_t disc_type);
 extern void bta_gattc_search_service(tBTA_GATTC_CLCB* p_clcb,
                                      bluetooth::Uuid* p_uuid);
-extern std::vector<tBTA_GATTC_SERVICE>* bta_gattc_get_services(
+extern const std::vector<gatt::Service>* bta_gattc_get_services(
     uint16_t conn_id);
-extern const tBTA_GATTC_SERVICE* bta_gattc_get_service_for_handle(
-    uint16_t conn_id, uint16_t handle);
-tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic_srcb(
-    tBTA_GATTC_SERV* p_srcb, uint16_t handle);
-extern tBTA_GATTC_SERVICE* bta_gattc_get_service_for_handle_srcb(
-    tBTA_GATTC_SERV* p_srcb, uint16_t handle);
-extern tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_characteristic(uint16_t conn_id,
-                                                               uint16_t handle);
-extern const tBTA_GATTC_DESCRIPTOR* bta_gattc_get_descriptor(uint16_t conn_id,
+extern const gatt::Service* bta_gattc_get_service_for_handle(uint16_t conn_id,
                                                              uint16_t handle);
-extern const tBTA_GATTC_CHARACTERISTIC* bta_gattc_get_owning_characteristic(
+const gatt::Characteristic* bta_gattc_get_characteristic_srcb(
+    tBTA_GATTC_SERV* p_srcb, uint16_t handle);
+extern const gatt::Service* bta_gattc_get_service_for_handle_srcb(
+    tBTA_GATTC_SERV* p_srcb, uint16_t handle);
+extern const gatt::Characteristic* bta_gattc_get_characteristic(
+    uint16_t conn_id, uint16_t handle);
+extern const gatt::Descriptor* bta_gattc_get_descriptor(uint16_t conn_id,
+                                                        uint16_t handle);
+extern const gatt::Characteristic* bta_gattc_get_owning_characteristic(
     uint16_t conn_id, uint16_t handle);
 extern void bta_gattc_get_gatt_db(uint16_t conn_id, uint16_t start_handle,
                                   uint16_t end_handle, btgatt_db_element_t** db,
                                   int* count);
-extern tGATT_STATUS bta_gattc_init_cache(tBTA_GATTC_SERV* p_srvc_cb);
-extern void bta_gattc_rebuild_cache(tBTA_GATTC_SERV* p_srcv, uint16_t num_attr,
-                                    tBTA_GATTC_NV_ATTR* attr);
-extern void bta_gattc_cache_save(tBTA_GATTC_SERV* p_srvc_cb, uint16_t conn_id);
+extern void bta_gattc_init_cache(tBTA_GATTC_SERV* p_srvc_cb);
 extern void bta_gattc_reset_discover_st(tBTA_GATTC_SERV* p_srcb,
                                         tGATT_STATUS status);
 
diff --git a/bta/gatt/bta_gattc_utils.cc b/bta/gatt/bta_gattc_utils.cc
index 21b63c0..2c8e5fd 100644
--- a/bta/gatt/bta_gattc_utils.cc
+++ b/bta/gatt/bta_gattc_utils.cc
@@ -204,7 +204,7 @@
     p_srcb->mtu = 0;
 
     // clear reallocating
-    std::vector<tBTA_GATTC_SERVICE>().swap(p_srcb->srvc_cache);
+    p_srcb->gatt_database.Clear();
   }
 
   osi_free_and_reset((void**)&p_clcb->p_q_cmd);
@@ -296,8 +296,8 @@
 
   if (p_tcb != NULL) {
     // clear reallocating
-    std::vector<tBTA_GATTC_SERVICE>().swap(p_tcb->srvc_cache);
-    std::vector<tBTA_GATTC_SERVICE>().swap(p_tcb->pending_discovery);
+    p_tcb->gatt_database.Clear();
+    p_tcb->pending_discovery.Clear();
     *p_tcb = tBTA_GATTC_SERV();
 
     p_tcb->in_use = true;
diff --git a/bta/gatt/database.cc b/bta/gatt/database.cc
new file mode 100644
index 0000000..5f99d55
--- /dev/null
+++ b/bta/gatt/database.cc
@@ -0,0 +1,185 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "database.h"
+#include "bt_trace.h"
+#include "stack/include/gattdefs.h"
+
+#include <base/logging.h>
+#include <memory>
+#include <sstream>
+
+using bluetooth::Uuid;
+
+namespace gatt {
+
+namespace {
+const Uuid PRIMARY_SERVICE = Uuid::From16Bit(GATT_UUID_PRI_SERVICE);
+const Uuid SECONDARY_SERVICE = Uuid::From16Bit(GATT_UUID_SEC_SERVICE);
+const Uuid INCLUDE = Uuid::From16Bit(GATT_UUID_INCLUDE_SERVICE);
+const Uuid CHARACTERISTIC = Uuid::From16Bit(GATT_UUID_CHAR_DECLARE);
+
+bool HandleInRange(const Service& svc, uint16_t handle) {
+  return handle >= svc.handle && handle <= svc.end_handle;
+}
+}  // namespace
+
+Service* FindService(std::vector<Service>& services, uint16_t handle) {
+  for (Service& service : services) {
+    if (handle >= service.handle && handle <= service.end_handle)
+      return &service;
+  }
+
+  return nullptr;
+}
+
+std::string Database::ToString() const {
+  std::stringstream tmp;
+
+  for (const Service& service : services) {
+    tmp << "Service: handle=" << loghex(service.handle)
+        << ", end_handle=" << loghex(service.end_handle)
+        << ", uuid=" << service.uuid << "\n";
+
+    for (const auto& is : service.included_services) {
+      tmp << "\t Included service: handle=" << loghex(is.handle)
+          << ", start_handle=" << loghex(is.start_handle)
+          << ", end_handle=" << loghex(is.end_handle) << ", uuid=" << is.uuid
+          << "\n";
+    }
+
+    for (const Characteristic& c : service.characteristics) {
+      tmp << "\t Characteristic: declaration_handle="
+          << loghex(c.declaration_handle)
+          << ", value_handle=" << loghex(c.value_handle) << ", uuid=" << c.uuid
+          << ", prop=" << loghex(c.properties) << "\n";
+
+      for (const Descriptor& d : c.descriptors) {
+        tmp << "\t\t Descriptor: handle=" << loghex(d.handle)
+            << ", uuid=" << d.uuid << "\n";
+      }
+    }
+  }
+  return tmp.str();
+}
+
+std::vector<StoredAttribute> Database::Serialize() const {
+  std::vector<StoredAttribute> nv_attr;
+
+  if (services.empty()) return std::vector<StoredAttribute>();
+
+  for (const Service& service : services) {
+    // TODO: add constructor to NV_ATTR, use emplace_back
+    nv_attr.push_back({service.handle,
+                       service.is_primary ? PRIMARY_SERVICE : SECONDARY_SERVICE,
+                       {.service = {.uuid = service.uuid,
+                                    .end_handle = service.end_handle}}});
+  }
+
+  for (const Service& service : services) {
+    for (const IncludedService& p_isvc : service.included_services) {
+      nv_attr.push_back({p_isvc.handle,
+                         INCLUDE,
+                         {.included_service = {.handle = p_isvc.start_handle,
+                                               .end_handle = p_isvc.end_handle,
+                                               .uuid = p_isvc.uuid}}});
+    }
+
+    for (const Characteristic& charac : service.characteristics) {
+      nv_attr.push_back(
+          {charac.declaration_handle,
+           CHARACTERISTIC,
+           {.characteristic = {.properties = charac.properties,
+                               .value_handle = charac.value_handle,
+                               .uuid = charac.uuid}}});
+
+      for (const Descriptor& desc : charac.descriptors) {
+        nv_attr.push_back({desc.handle, desc.uuid, {}});
+      }
+    }
+  }
+
+  return nv_attr;
+}
+
+Database Database::Deserialize(const std::vector<StoredAttribute>& nv_attr,
+                               bool* success) {
+  // clear reallocating
+  Database result;
+  auto it = nv_attr.cbegin();
+
+  for (; it != nv_attr.cend(); ++it) {
+    const auto& attr = *it;
+    if (attr.type != PRIMARY_SERVICE && attr.type != SECONDARY_SERVICE) break;
+    result.services.emplace_back(
+        Service{.handle = attr.handle,
+                .end_handle = attr.value.service.end_handle,
+                .is_primary = (attr.type == PRIMARY_SERVICE),
+                .uuid = attr.value.service.uuid});
+  }
+
+  auto current_service_it = result.services.begin();
+  for (; it != nv_attr.cend(); it++) {
+    const auto& attr = *it;
+
+    // go to the service this attribute belongs to; attributes are stored in
+    // order, so iterating just forward is enough
+    while (current_service_it != result.services.end() &&
+           current_service_it->end_handle < attr.handle) {
+      current_service_it++;
+    }
+
+    if (current_service_it == result.services.end() ||
+        !HandleInRange(*current_service_it, attr.handle)) {
+      LOG(ERROR) << "Can't find service for attribute with handle: "
+                 << loghex(attr.handle);
+      *success = false;
+      return result;
+    }
+
+    if (attr.type == INCLUDE) {
+      Service* included_service =
+          FindService(result.services, attr.value.included_service.handle);
+      if (!included_service) {
+        LOG(ERROR) << __func__ << ": Non-existing included service!";
+        *success = false;
+        return result;
+      }
+      current_service_it->included_services.push_back(IncludedService{
+          .handle = attr.handle,
+          .uuid = attr.value.included_service.uuid,
+          .start_handle = attr.value.included_service.handle,
+          .end_handle = attr.value.included_service.end_handle,
+      });
+    } else if (attr.type == CHARACTERISTIC) {
+      current_service_it->characteristics.emplace_back(
+          Characteristic{.declaration_handle = attr.handle,
+                         .value_handle = attr.value.characteristic.value_handle,
+                         .properties = attr.value.characteristic.properties,
+                         .uuid = attr.value.characteristic.uuid});
+
+    } else {
+      current_service_it->characteristics.back().descriptors.emplace_back(
+          Descriptor{.handle = attr.handle, .uuid = attr.type});
+    }
+  }
+  *success = true;
+  return result;
+}
+
+}  // namespace gatt
\ No newline at end of file
diff --git a/bta/gatt/database.h b/bta/gatt/database.h
new file mode 100644
index 0000000..1e74809
--- /dev/null
+++ b/bta/gatt/database.h
@@ -0,0 +1,126 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "types/bluetooth/uuid.h"
+
+namespace gatt {
+constexpr uint16_t HANDLE_MIN = 0x0001;
+constexpr uint16_t HANDLE_MAX = 0xffff;
+
+/* Representation of GATT attribute for storage */
+struct StoredAttribute {
+  uint16_t handle;
+  bluetooth::Uuid type;
+
+  union {
+    /* primary or secondary service definition */
+    struct {
+      bluetooth::Uuid uuid;
+      uint16_t end_handle;
+    } service;
+
+    /* included service definition */
+    struct {
+      uint16_t handle;
+      uint16_t end_handle;
+      bluetooth::Uuid uuid;
+    } included_service;
+
+    /* characteristic deifnition */
+    struct {
+      uint8_t properties;
+      uint16_t value_handle;
+      bluetooth::Uuid uuid;
+    } characteristic;
+
+    /* for descriptor definition we don't store value*/
+  } value;
+};
+
+struct IncludedService;
+struct Characteristic;
+struct Descriptor;
+
+struct Service {
+  uint16_t handle;
+  bluetooth::Uuid uuid;
+  bool is_primary;
+  uint16_t end_handle;
+  std::vector<IncludedService> included_services;
+  std::vector<Characteristic> characteristics;
+};
+
+struct IncludedService {
+  uint16_t handle; /* definition handle */
+  bluetooth::Uuid uuid;
+  uint16_t start_handle; /* start handle of included service */
+  uint16_t end_handle;   /* end handle of included service */
+};
+
+struct Characteristic {
+  uint16_t declaration_handle;
+  bluetooth::Uuid uuid;
+  uint16_t value_handle;
+  uint8_t properties;
+  std::vector<Descriptor> descriptors;
+};
+
+struct Descriptor {
+  uint16_t handle;
+  bluetooth::Uuid uuid;
+};
+
+class DatabaseBuilder;
+
+class Database {
+ public:
+  /* Return true if there are no services in this database. */
+  bool IsEmpty() const { return services.empty(); }
+
+  /* Clear the GATT database. This method forces relocation to ensure no extra
+   * space is used unnecesarly */
+  void Clear() { std::vector<Service>().swap(services); }
+
+  /* Return list of services available in this database */
+  const std::vector<Service>& Services() const { return services; }
+
+  std::string ToString() const;
+
+  std::vector<gatt::StoredAttribute> Serialize() const;
+
+  static Database Deserialize(const std::vector<gatt::StoredAttribute>& nv_attr,
+                              bool* success);
+
+  friend class DatabaseBuilder;
+
+ private:
+  std::vector<Service> services;
+};
+
+/* Find a service that should contain handle. Helper method for internal use
+ * inside gatt namespace.*/
+Service* FindService(std::vector<Service>& services, uint16_t handle);
+
+}  // namespace gatt
diff --git a/bta/gatt/database_builder.cc b/bta/gatt/database_builder.cc
new file mode 100644
index 0000000..ed065ab
--- /dev/null
+++ b/bta/gatt/database_builder.cc
@@ -0,0 +1,190 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "database_builder.h"
+
+#include "bt_trace.h"
+
+#include <base/logging.h>
+#include <algorithm>
+
+using bluetooth::Uuid;
+
+namespace gatt {
+
+void DatabaseBuilder::AddService(uint16_t handle, uint16_t end_handle,
+                                 const Uuid& uuid, bool is_primary) {
+  // general case optimization - we add services in order
+  if (database.services.empty() ||
+      database.services.back().end_handle < handle) {
+    database.services.emplace_back(Service{.handle = handle,
+                                           .end_handle = end_handle,
+                                           .is_primary = is_primary,
+                                           .uuid = uuid});
+  } else {
+    auto& vec = database.services;
+
+    // Find first service whose start handle is bigger than new service handle
+    auto it = std::lower_bound(
+        vec.begin(), vec.end(), handle,
+        [](Service s, uint16_t handle) { return s.end_handle < handle; });
+
+    // Insert new service just before it
+    vec.emplace(it, Service{.handle = handle,
+                            .end_handle = end_handle,
+                            .is_primary = is_primary,
+                            .uuid = uuid});
+  }
+
+  services_to_discover.insert({handle, end_handle});
+}
+
+void DatabaseBuilder::AddIncludedService(uint16_t handle, const Uuid& uuid,
+                                         uint16_t start_handle,
+                                         uint16_t end_handle) {
+  Service* service = FindService(database.services, handle);
+  if (!service) {
+    LOG(ERROR) << "Illegal action to add to non-existing service!";
+    return;
+  }
+
+  /* We discover all Primary Services first. If included service was not seen
+   * before, it must be a Secondary Service */
+  if (!FindService(database.services, start_handle)) {
+    AddService(start_handle, end_handle, uuid, false /* not primary */);
+  }
+
+  service->included_services.push_back(IncludedService{
+      .handle = handle,
+      .uuid = uuid,
+      .start_handle = start_handle,
+      .end_handle = end_handle,
+  });
+}
+
+void DatabaseBuilder::AddCharacteristic(uint16_t handle, uint16_t value_handle,
+                                        const Uuid& uuid, uint8_t properties) {
+  Service* service = FindService(database.services, handle);
+  if (!service) {
+    LOG(ERROR) << "Illegal action to add to non-existing service!";
+    return;
+  }
+
+  if (service->end_handle < value_handle)
+    LOG(WARNING) << "Remote device violates spec: value_handle="
+                 << loghex(value_handle) << " is after service end_handle="
+                 << loghex(service->end_handle);
+
+  service->characteristics.emplace_back(
+      Characteristic{.declaration_handle = handle,
+                     .value_handle = value_handle,
+                     .properties = properties,
+                     .uuid = uuid});
+  return;
+}
+
+void DatabaseBuilder::AddDescriptor(uint16_t handle, const Uuid& uuid) {
+  Service* service = FindService(database.services, handle);
+  if (!service) {
+    LOG(ERROR) << "Illegal action to add to non-existing service!";
+    return;
+  }
+
+  if (service->characteristics.empty()) {
+    LOG(ERROR) << __func__
+               << ": Illegal action to add to non-existing characteristic!";
+    return;
+  }
+
+  Characteristic* char_node = &service->characteristics.front();
+  for (auto it = service->characteristics.begin();
+       it != service->characteristics.end(); it++) {
+    if (it->declaration_handle > handle) break;
+    char_node = &(*it);
+  }
+
+  char_node->descriptors.emplace_back(
+      gatt::Descriptor{.handle = handle, .uuid = uuid});
+}
+
+bool DatabaseBuilder::StartNextServiceExploration() {
+  while (!services_to_discover.empty()) {
+    auto handle_range = services_to_discover.begin();
+    pending_service = *handle_range;
+    services_to_discover.erase(handle_range);
+
+    // Empty service declaration, nothing to explore, skip to next.
+    if (pending_service.first == pending_service.second) continue;
+
+    pending_characteristic = HANDLE_MIN;
+    return true;
+  }
+  return false;
+}
+
+const std::pair<uint16_t, uint16_t>&
+DatabaseBuilder::CurrentlyExploredService() {
+  return pending_service;
+}
+
+std::pair<uint16_t, uint16_t> DatabaseBuilder::NextDescriptorRangeToExplore() {
+  Service* service = FindService(database.services, pending_service.first);
+  if (!service || service->characteristics.empty()) {
+    return {HANDLE_MAX, HANDLE_MAX};
+  }
+
+  for (auto it = service->characteristics.cbegin();
+       it != service->characteristics.cend(); it++) {
+    if (it->declaration_handle > pending_characteristic) {
+      auto next = std::next(it);
+
+      /* Characteristic Declaration is followed by Characteristic Value
+       * Declaration, first descriptor is after that, see BT Spect 5.0 Vol 3,
+       * Part G 3.3.2 and 3.3.3 */
+      uint16_t start = it->declaration_handle + 2;
+      uint16_t end;
+      if (next != service->characteristics.end())
+        end = next->declaration_handle - 1;
+      else
+        end = service->end_handle;
+
+      // No place for descriptor - skip to next characteristic
+      if (start > end) continue;
+
+      pending_characteristic = start;
+      return {start, end};
+    }
+  }
+
+  pending_characteristic = HANDLE_MAX;
+  return {HANDLE_MAX, HANDLE_MAX};
+}
+
+bool DatabaseBuilder::InProgress() const { return !database.services.empty(); }
+
+Database DatabaseBuilder::Build() {
+  Database tmp = database;
+  database.Clear();
+  return tmp;
+}
+
+void DatabaseBuilder::Clear() { database.Clear(); }
+
+std::string DatabaseBuilder::ToString() const { return database.ToString(); }
+
+}  // namespace gatt
diff --git a/bta/gatt/database_builder.h b/bta/gatt/database_builder.h
new file mode 100644
index 0000000..0913100
--- /dev/null
+++ b/bta/gatt/database_builder.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#pragma once
+
+#include "gatt/database.h"
+
+#include <utility>
+#include <vector>
+
+namespace gatt {
+
+class DatabaseBuilder {
+ public:
+  constexpr static std::pair<uint16_t, uint16_t> EXPLORE_END =
+      std::make_pair(0xFFFF, 0xFFFF);
+
+  void AddService(uint16_t handle, uint16_t end_handle,
+                  const bluetooth::Uuid& uuid, bool is_primary);
+  void AddIncludedService(uint16_t handle, const bluetooth::Uuid& uuid,
+                          uint16_t start_handle, uint16_t end_handle);
+  void AddCharacteristic(uint16_t handle, uint16_t value_handle,
+                         const bluetooth::Uuid& uuid, uint8_t properties);
+  void AddDescriptor(uint16_t handle, const bluetooth::Uuid& uuid);
+
+  /* Returns true if next service exploration started, false if there are no
+   * more services to explore. */
+  bool StartNextServiceExploration();
+
+  /* Return pair with start and end handle of the currently explored service.
+   */
+  const std::pair<uint16_t, uint16_t>& CurrentlyExploredService();
+
+  /* Return pair with start and end handle of the descriptor range to discover,
+   * or DatabaseBuilder::EXPLORE_END if no more descriptors left.
+   */
+  std::pair<uint16_t, uint16_t> NextDescriptorRangeToExplore();
+
+  /* Returns true, if GATT discovery is in progress, false if discovery was not
+   * started, or is already finished.
+   */
+  // TODO(jpawlowski): in the future, we might create this object only for the
+  // time of discovery, in such case InProgress won't be needed, because object
+  // existence will mean discovery is pending
+  bool InProgress() const;
+
+  /* Call this method at end of GATT discovery, to obtain object representing
+   * the database of remote device */
+  Database Build();
+
+  void Clear();
+
+  /* Return text representation of internal state for debugging purposes */
+  std::string ToString() const;
+
+ private:
+  Database database;
+  /* Start and end handle of service that is currently being discovered on the
+   * remote device */
+  std::pair<uint16_t, uint16_t> pending_service;
+  /* Characteristic inside pending_service that is currently being explored */
+  uint16_t pending_characteristic;
+
+  /* sorted, unique set of start_handle, end_handle pair of all services that
+   * have not yet been discovered */
+  std::set<std::pair<uint16_t, uint16_t>> services_to_discover;
+};
+
+}  // namespace gatt
diff --git a/bta/hearing_aid/hearing_aid.cc b/bta/hearing_aid/hearing_aid.cc
index 8779542..b9156af 100644
--- a/bta/hearing_aid/hearing_aid.cc
+++ b/bta/hearing_aid/hearing_aid.cc
@@ -25,6 +25,7 @@
 #include "embdrv/g722/g722_enc_dec.h"
 #include "gap_api.h"
 #include "gatt_api.h"
+#include "osi/include/properties.h"
 
 #include <base/bind.h>
 #include <base/logging.h>
@@ -36,6 +37,13 @@
 using bluetooth::Uuid;
 using bluetooth::hearing_aid::ConnectionState;
 
+// The MIN_CE_LEN parameter for Connection Parameters based on the current
+// Connection Interval
+constexpr uint16_t MIN_CE_LEN_10MS_CI = 0x0006;
+constexpr uint16_t MIN_CE_LEN_20MS_CI = 0x000C;
+constexpr uint16_t CONNECTION_INTERVAL_10MS_PARAM = 0x0008;
+constexpr uint16_t CONNECTION_INTERVAL_20MS_PARAM = 0x0010;
+
 void btif_storage_add_hearing_aid(const RawAddress& address, uint16_t psm,
                                   uint8_t capabilities, uint16_t codecs,
                                   uint16_t audio_control_point_handle,
@@ -70,8 +78,6 @@
 Uuid LE_PSM_UUID               = Uuid::FromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a");
 // clang-format on
 
-constexpr uint16_t MIN_CE_LEN_1M = 0x0006;
-
 void hearingaid_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data);
 void encryption_callback(const RawAddress*, tGATT_TRANSPORT, void*,
                          tBTM_STATUS);
@@ -252,8 +258,20 @@
       : gatt_if(0),
         seq_counter(0),
         current_volume(VOLUME_UNKNOWN),
-        callbacks(callbacks) {
-    DVLOG(2) << __func__;
+        callbacks(callbacks),
+        codec_in_use(0) {
+    default_data_interval_ms = (uint16_t)osi_property_get_int32(
+        "persist.bluetooth.hearingaid.interval", (int32_t)HA_INTERVAL_20_MS);
+    if ((default_data_interval_ms != HA_INTERVAL_10_MS) &&
+        (default_data_interval_ms != HA_INTERVAL_20_MS)) {
+      LOG(ERROR) << __func__
+                 << ": invalid interval=" << default_data_interval_ms
+                 << "ms. Overwriting back to default";
+      default_data_interval_ms = HA_INTERVAL_20_MS;
+    }
+    VLOG(2) << __func__
+            << ", default_data_interval_ms=" << default_data_interval_ms;
+
     BTA_GATTC_AppRegister(
         hearingaid_gattc_callback,
         base::Bind(
@@ -269,6 +287,31 @@
             initCb));
   }
 
+  void UpdateBleConnParams(const RawAddress& address) {
+    /* List of parameters that depends on the chosen Connection Interval */
+    uint16_t min_ce_len;
+    uint16_t connection_interval;
+
+    switch (default_data_interval_ms) {
+      case HA_INTERVAL_10_MS:
+        min_ce_len = MIN_CE_LEN_10MS_CI;
+        connection_interval = CONNECTION_INTERVAL_10MS_PARAM;
+        break;
+      case HA_INTERVAL_20_MS:
+        min_ce_len = MIN_CE_LEN_20MS_CI;
+        connection_interval = CONNECTION_INTERVAL_20MS_PARAM;
+        break;
+      default:
+        LOG(ERROR) << __func__ << ":Error: invalid default_data_interval_ms="
+                   << default_data_interval_ms;
+        min_ce_len = MIN_CE_LEN_10MS_CI;
+        connection_interval = CONNECTION_INTERVAL_10MS_PARAM;
+    }
+
+    L2CA_UpdateBleConnParams(address, connection_interval, connection_interval,
+                             0x000A, 0x0064 /*1s*/, min_ce_len, min_ce_len);
+  }
+
   void Connect(const RawAddress& address) override {
     DVLOG(2) << __func__ << " " << address;
     hearingDevices.Add(HearingDevice(address, true));
@@ -339,13 +382,12 @@
     // update now, it'll be started once current device finishes.
     hearingDevice->connection_update_pending = true;
     if (!any_update_pending) {
-      L2CA_UpdateBleConnParams(address, 0x0008, 0x0008, 0x000A, 0x0064 /*1s*/,
-                               MIN_CE_LEN_1M, MIN_CE_LEN_1M);
+      UpdateBleConnParams(address);
     }
 
     // Set data length
     // TODO(jpawlowski: for 16khz only 87 is required, optimize
-    BTM_SetBleDataLength(address, 147);
+    BTM_SetBleDataLength(address, 168);
 
     tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(address);
     if (p_dev_rec) {
@@ -390,8 +432,7 @@
 
     for (auto& device : hearingDevices.devices) {
       if (device.conn_id && device.connection_update_pending) {
-        L2CA_UpdateBleConnParams(device.address, 0x0008, 0x0008, 0x000A,
-                                 0x0064 /*1s*/, MIN_CE_LEN_1M, MIN_CE_LEN_1M);
+        UpdateBleConnParams(device.address);
         return;
       }
     }
@@ -444,11 +485,10 @@
       return;
     }
 
-    const std::vector<tBTA_GATTC_SERVICE>* services =
-        BTA_GATTC_GetServices(conn_id);
+    const std::vector<gatt::Service>* services = BTA_GATTC_GetServices(conn_id);
 
-    const tBTA_GATTC_SERVICE* service = nullptr;
-    for (const tBTA_GATTC_SERVICE& tmp : *services) {
+    const gatt::Service* service = nullptr;
+    for (const gatt::Service& tmp : *services) {
       if (tmp.uuid != HEARING_AID_UUID) continue;
       LOG(INFO) << "Found Hearing Aid service, handle=" << loghex(tmp.handle);
       service = &tmp;
@@ -463,7 +503,7 @@
     }
 
     uint16_t psm_handle = 0x0000;
-    for (const tBTA_GATTC_CHARACTERISTIC& charac : service->characteristics) {
+    for (const gatt::Characteristic& charac : service->characteristics) {
       if (charac.uuid == READ_ONLY_PROPERTIES_UUID) {
         DVLOG(2) << "Reading read only properties "
                  << loghex(charac.value_handle);
@@ -557,6 +597,49 @@
     }
   }
 
+  uint16_t CalcCompressedAudioPacketSize(uint16_t codec_type,
+                                         int connection_interval) {
+    int sample_rate;
+
+    const int sample_bit_rate = 16;  /* 16 bits per sample */
+    const int compression_ratio = 4; /* G.722 has a 4:1 compression ratio */
+    if (codec_type == CODEC_G722_24KHZ) {
+      sample_rate = 24000;
+    } else {
+      sample_rate = 16000;
+    }
+
+    // compressed_data_packet_size is the size in bytes of the compressed audio
+    // data buffer that is generated for each connection interval.
+    uint32_t compressed_data_packet_size =
+        (sample_rate * connection_interval * (sample_bit_rate / 8) /
+         compression_ratio) /
+        1000;
+    return ((uint16_t)compressed_data_packet_size);
+  }
+
+  void ChooseCodec(const HearingDevice& hearingDevice) {
+    if (codec_in_use) return;
+
+    // use the best codec available for this pair of devices.
+    uint16_t codecs = hearingDevice.codecs;
+    if (hearingDevice.hi_sync_id != 0) {
+      for (const auto& device : hearingDevices.devices) {
+        if (device.hi_sync_id != hearingDevice.hi_sync_id) continue;
+
+        codecs &= device.codecs;
+      }
+    }
+
+    if ((codecs & (1 << CODEC_G722_24KHZ)) &&
+        controller_get_interface()->supports_ble_2m_phy() &&
+        default_data_interval_ms == HA_INTERVAL_10_MS) {
+      codec_in_use = CODEC_G722_24KHZ;
+    } else if (codecs & (1 << CODEC_G722_16KHZ)) {
+      codec_in_use = CODEC_G722_16KHZ;
+    }
+  }
+
   void OnAudioStatus(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
                      uint16_t len, uint8_t* value, void* data) {
     DVLOG(2) << __func__ << " " << base::HexEncode(value, len);
@@ -648,16 +731,20 @@
       hearingDevice->first_connection = false;
     }
 
+    ChooseCodec(*hearingDevice);
+
     SendStart(*hearingDevice);
 
     hearingDevice->accepting_audio = true;
     LOG(INFO) << __func__ << ": address=" << address
-              << ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id);
+              << ", hi_sync_id=" << loghex(hearingDevice->hi_sync_id)
+              << ", codec_in_use=" << loghex(codec_in_use);
+
+    StartSendingAudio(*hearingDevice);
+
     callbacks->OnDeviceAvailable(hearingDevice->capabilities,
                                  hearingDevice->hi_sync_id, address);
     callbacks->OnConnectionState(ConnectionState::CONNECTED, address);
-
-    StartSendingAudio(*hearingDevice);
   }
 
   void StartSendingAudio(const HearingDevice& hearingDevice) {
@@ -678,21 +765,15 @@
         }
       }
 
-      if ((codecs & (1 << CODEC_G722_24KHZ)) &&
-          controller_get_interface()->supports_ble_2m_phy()) {
-        codec_in_use = CODEC_G722_24KHZ;
+      CodecConfiguration codec;
+      if (codec_in_use == CODEC_G722_24KHZ) {
         codec.sample_rate = 24000;
-        codec.bit_rate = 16;
-        codec.data_interval_ms = 10;
-      } else if (codecs & (1 << CODEC_G722_16KHZ)) {
-        codec_in_use = CODEC_G722_16KHZ;
+      } else {
         codec.sample_rate = 16000;
-        codec.bit_rate = 16;
-        codec.data_interval_ms = 10;
       }
+      codec.bit_rate = 16;
+      codec.data_interval_ms = default_data_interval_ms;
 
-      // TODO: remove once we implement support for other codecs
-      codec_in_use = CODEC_G722_16KHZ;
       HearingAidAudioSource::Start(codec, audioReceiver);
     }
   }
@@ -802,7 +883,9 @@
     // TODO: this should basically fit the encoded data, tune the size later
     std::vector<uint8_t> encoded_data_left;
     if (left) {
-      encoded_data_left.resize(2000);
+      // TODO: instead of a magic number, we need to figure out the correct
+      // buffer size
+      encoded_data_left.resize(4000);
       int encoded_size =
           g722_encode(encoder_state_left, encoded_data_left.data(),
                       (const int16_t*)chan_left.data(), chan_left.size());
@@ -822,7 +905,9 @@
 
     std::vector<uint8_t> encoded_data_right;
     if (right) {
-      encoded_data_right.resize(2000);
+      // TODO: instead of a magic number, we need to figure out the correct
+      // buffer size
+      encoded_data_right.resize(4000);
       int encoded_size =
           g722_encode(encoder_state_right, encoded_data_right.data(),
                       (const int16_t*)chan_right.data(), chan_right.size());
@@ -843,15 +928,8 @@
     size_t encoded_data_size =
         std::max(encoded_data_left.size(), encoded_data_right.size());
 
-    // TODO: make it also dependent on the interval, when we support intervals
-    // different than 10ms
-    uint16_t packet_size;
-
-    if (codec_in_use == CODEC_G722_24KHZ) {
-      packet_size = 120;
-    } else /* if (codec_in_use == CODEC_G722_16KHZ) */ {
-      packet_size = 80;
-    }
+    uint16_t packet_size =
+        CalcCompressedAudioPacketSize(codec_in_use, default_data_interval_ms);
 
     for (size_t i = 0; i < encoded_data_size; i += packet_size) {
       if (left) {
@@ -1080,7 +1158,8 @@
 
   /* currently used codec */
   uint8_t codec_in_use;
-  CodecConfiguration codec;
+
+  uint16_t default_data_interval_ms;
 
   HearingDevices hearingDevices;
 };
diff --git a/bta/hearing_aid/hearing_aid_audio_source.cc b/bta/hearing_aid/hearing_aid_audio_source.cc
index 947caaf..e03b040 100644
--- a/bta/hearing_aid/hearing_aid_audio_source.cc
+++ b/bta/hearing_aid/hearing_aid_audio_source.cc
@@ -28,9 +28,9 @@
 extern const char* audio_ha_hw_dump_ctrl_event(tHEARING_AID_CTRL_CMD event);
 
 namespace {
-int bit_rate = 16;
-int sample_rate = 16000;
-int data_interval_ms = 10 /* msec */;
+int bit_rate = -1;
+int sample_rate = -1;
+int data_interval_ms = -1;
 int num_channels = 2;
 alarm_t* audio_timer = nullptr;
 
@@ -94,6 +94,11 @@
       UIPC_Ioctl(*uipc_hearing_aid, UIPC_CH_ID_AV_AUDIO, UIPC_SET_READ_POLL_TMO,
                  reinterpret_cast<void*>(0));
 
+      if (data_interval_ms != HA_INTERVAL_10_MS &&
+          data_interval_ms != HA_INTERVAL_20_MS) {
+        LOG(FATAL) << " Unsupported data interval: " << data_interval_ms;
+      }
+
       audio_timer = alarm_new_periodic("hearing_aid_data_timer");
       alarm_set_on_mloop(audio_timer, data_interval_ms, send_audio_data,
                          nullptr);
@@ -266,6 +271,11 @@
                                   HearingAidAudioReceiver* audioReceiver) {
   localAudioReceiver = audioReceiver;
   VLOG(2) << "Hearing Aid UIPC Open";
+
+  bit_rate = codecConfiguration.bit_rate;
+  sample_rate = codecConfiguration.sample_rate;
+  data_interval_ms = codecConfiguration.data_interval_ms;
+
   stats.Reset();
 }
 
diff --git a/bta/hf_client/bta_hf_client_main.cc b/bta/hf_client/bta_hf_client_main.cc
index 4f717ae..9bb8ab5 100644
--- a/bta/hf_client/bta_hf_client_main.cc
+++ b/bta/hf_client/bta_hf_client_main.cc
@@ -386,7 +386,7 @@
 
     /* reopen registered server */
     /* Collision may be detected before or after we close servers. */
-    // bta_hf_client_start_server();
+    bta_hf_client_start_server();
 
     /* Start timer to handle connection opening restart */
     alarm_set_on_mloop(client_cb->collision_timer,
diff --git a/bta/hh/bta_hh_le.cc b/bta/hh/bta_hh_le.cc
index adeb957..759d6a1 100644
--- a/bta/hh/bta_hh_le.cc
+++ b/bta/hh/bta_hh_le.cc
@@ -457,9 +457,9 @@
   return NULL;
 }
 
-static const tBTA_GATTC_DESCRIPTOR* find_descriptor_by_short_uuid(
+static const gatt::Descriptor* find_descriptor_by_short_uuid(
     uint16_t conn_id, uint16_t char_handle, uint16_t short_uuid) {
-  const tBTA_GATTC_CHARACTERISTIC* p_char =
+  const gatt::Characteristic* p_char =
       BTA_GATTC_GetCharacteristic(conn_id, char_handle);
 
   if (!p_char) {
@@ -467,7 +467,7 @@
     return NULL;
   }
 
-  for (const tBTA_GATTC_DESCRIPTOR& desc : p_char->descriptors) {
+  for (const gatt::Descriptor& desc : p_char->descriptors) {
     if (desc.uuid == Uuid::From16Bit(short_uuid)) return &desc;
   }
 
@@ -486,7 +486,7 @@
                                                      uint16_t short_uuid,
                                                      GATT_READ_OP_CB cb,
                                                      void* cb_data) {
-  const tBTA_GATTC_DESCRIPTOR* p_desc =
+  const gatt::Descriptor* p_desc =
       find_descriptor_by_short_uuid(p_cb->conn_id, char_handle, short_uuid);
   if (!p_desc) return BTA_HH_ERR;
 
@@ -671,7 +671,7 @@
 bool bta_hh_le_write_ccc(tBTA_HH_DEV_CB* p_cb, uint8_t char_handle,
                          uint16_t clt_cfg_value, GATT_WRITE_OP_CB cb,
                          void* cb_data) {
-  const tBTA_GATTC_DESCRIPTOR* p_desc = find_descriptor_by_short_uuid(
+  const gatt::Descriptor* p_desc = find_descriptor_by_short_uuid(
       p_cb->conn_id, char_handle, GATT_UUID_CHAR_CLIENT_CONFIG);
   if (!p_desc) return false;
 
@@ -691,7 +691,7 @@
   uint8_t srvc_inst_id, hid_inst_id;
 
   tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data;
-  const tBTA_GATTC_CHARACTERISTIC* characteristic =
+  const gatt::Characteristic* characteristic =
       BTA_GATTC_GetOwningCharacteristic(conn_id, handle);
   uint16_t char_uuid = characteristic->uuid.As16Bit();
 
@@ -1310,17 +1310,16 @@
   }
 
   tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data;
-  const tBTA_GATTC_DESCRIPTOR* p_desc =
-      BTA_GATTC_GetDescriptor(conn_id, handle);
+  const gatt::Descriptor* p_desc = BTA_GATTC_GetDescriptor(conn_id, handle);
 
   if (!p_desc) {
     APPL_TRACE_ERROR("%s: error: descriptor is null!", __func__);
     return;
   }
 
-  const tBTA_GATTC_CHARACTERISTIC* characteristic =
+  const gatt::Characteristic* characteristic =
       BTA_GATTC_GetOwningCharacteristic(conn_id, handle);
-  const tBTA_GATTC_SERVICE* service =
+  const gatt::Service* service =
       BTA_GATTC_GetOwningService(conn_id, characteristic->value_handle);
 
   tBTA_HH_LE_RPT* p_rpt;
@@ -1398,10 +1397,10 @@
  *
  ******************************************************************************/
 static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB* p_dev_cb,
-                                       const tBTA_GATTC_SERVICE* service) {
+                                       const gatt::Service* service) {
   tBTA_HH_LE_RPT* p_rpt;
 
-  for (const tBTA_GATTC_CHARACTERISTIC& charac : service->characteristics) {
+  for (const gatt::Characteristic& charac : service->characteristics) {
     if (!charac.uuid.Is16Bit()) continue;
 
     uint16_t uuid16 = charac.uuid.As16Bit();
@@ -1460,7 +1459,7 @@
   }
 
   /* Make sure PROTO_MODE is processed as last */
-  for (const tBTA_GATTC_CHARACTERISTIC& charac : service->characteristics) {
+  for (const gatt::Characteristic& charac : service->characteristics) {
     if (charac.uuid == Uuid::From16Bit(GATT_UUID_HID_PROTO_MODE)) {
       p_dev_cb->hid_srvc.proto_mode_handle = charac.value_handle;
       bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode);
@@ -1491,11 +1490,11 @@
     return;
   }
 
-  const std::vector<tBTA_GATTC_SERVICE>* services =
+  const std::vector<gatt::Service>* services =
       BTA_GATTC_GetServices(p_data->conn_id);
 
   bool have_hid = false;
-  for (const tBTA_GATTC_SERVICE& service : *services) {
+  for (const gatt::Service& service : *services) {
     if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_LE_HID) &&
         service.is_primary && !have_hid) {
       have_hid = true;
@@ -1513,7 +1512,7 @@
     } else if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_SCAN_PARAM)) {
       p_dev_cb->scan_refresh_char_handle = 0;
 
-      for (const tBTA_GATTC_CHARACTERISTIC& charac : service.characteristics) {
+      for (const gatt::Characteristic& charac : service.characteristics) {
         if (charac.uuid == Uuid::From16Bit(GATT_UUID_SCAN_REFRESH)) {
           p_dev_cb->scan_refresh_char_handle = charac.value_handle;
 
@@ -1528,7 +1527,7 @@
     } else if (service.uuid == Uuid::From16Bit(UUID_SERVCLASS_GAP_SERVER)) {
       // TODO(jpawlowski): this should be done by GAP profile, remove when GAP
       // is fixed.
-      for (const tBTA_GATTC_CHARACTERISTIC& charac : service.characteristics) {
+      for (const gatt::Characteristic& charac : service.characteristics) {
         if (charac.uuid == Uuid::From16Bit(GATT_UUID_GAP_PREF_CONN_PARAM)) {
           /* read the char value */
           BtaGattQueue::ReadCharacteristic(p_dev_cb->conn_id,
@@ -1565,7 +1564,7 @@
     return;
   }
 
-  const tBTA_GATTC_CHARACTERISTIC* p_char =
+  const gatt::Characteristic* p_char =
       BTA_GATTC_GetCharacteristic(p_dev_cb->conn_id, p_data->handle);
   if (p_char == NULL) {
     APPL_TRACE_ERROR(
@@ -1719,7 +1718,7 @@
 static void read_report_cb(uint16_t conn_id, tGATT_STATUS status,
                            uint16_t handle, uint16_t len, uint8_t* value,
                            void* data) {
-  const tBTA_GATTC_CHARACTERISTIC* p_char =
+  const gatt::Characteristic* p_char =
       BTA_GATTC_GetCharacteristic(conn_id, handle);
 
   if (p_char == NULL) return;
@@ -1752,7 +1751,7 @@
   hs_data.handle = p_dev_cb->hid_handle;
 
   if (status == GATT_SUCCESS) {
-    const tBTA_GATTC_SERVICE* p_svc =
+    const gatt::Service* p_svc =
         BTA_GATTC_GetOwningService(conn_id, p_char->value_handle);
 
     p_rpt = bta_hh_le_find_report_entry(p_dev_cb, p_svc->handle, char_uuid,
@@ -1818,7 +1817,7 @@
   APPL_TRACE_DEBUG("bta_hh_le_write_cmpl w4_evt: %d", p_dev_cb->w4_evt);
 #endif
 
-  const tBTA_GATTC_CHARACTERISTIC* p_char =
+  const gatt::Characteristic* p_char =
       BTA_GATTC_GetCharacteristic(conn_id, handle);
   uint16_t uuid = p_char->uuid.As16Bit();
   if (uuid != GATT_UUID_HID_REPORT && uuid != GATT_UUID_HID_BT_KB_INPUT &&
@@ -1867,7 +1866,7 @@
 
   p_cb->w4_evt = w4_evt;
 
-  const tBTA_GATTC_CHARACTERISTIC* p_char =
+  const gatt::Characteristic* p_char =
       BTA_GATTC_GetCharacteristic(p_cb->conn_id, p_rpt->char_inst_id);
 
   tGATT_WRITE_TYPE write_type = GATT_WRITE;
diff --git a/bta/include/bta_api.h b/bta/include/bta_api.h
index 9f2ee28..85b50b6 100644
--- a/bta/include/bta_api.h
+++ b/bta/include/bta_api.h
@@ -485,9 +485,9 @@
 typedef uint8_t tBTA_DM_BLE_LOCAL_KEY_MASK;
 
 typedef struct {
-  BT_OCTET16 ir;
-  BT_OCTET16 irk;
-  BT_OCTET16 dhk;
+  Octet16 ir;
+  Octet16 irk;
+  Octet16 dhk;
 } tBTA_BLE_LOCAL_ID_KEYS;
 
 #define BTA_DM_SEC_GRANTED BTA_SUCCESS
@@ -512,7 +512,7 @@
   RawAddress bd_addr;  /* BD address peer device. */
   BD_NAME bd_name;     /* Name of peer device. */
   bool key_present;    /* Valid link key value in key element */
-  LINK_KEY key;        /* Link key associated with peer device. */
+  LinkKey key;         /* Link key associated with peer device. */
   uint8_t key_type;    /* The type of Link Key */
   bool success;        /* true of authentication succeeded, false if failed. */
   uint8_t fail_reason; /* The HCI reason/error code for when success=false */
@@ -689,7 +689,7 @@
   tBTA_DM_BLE_SEC_REQ ble_req;        /* BLE SMP related request */
   tBTA_DM_BLE_KEY ble_key;            /* BLE SMP keys used when pairing */
   tBTA_BLE_LOCAL_ID_KEYS ble_id_keys; /* IR event */
-  BT_OCTET16 ble_er;                  /* ER event data */
+  Octet16 ble_er;                     /* ER event data */
 } tBTA_DM_SEC;
 
 /* Security callback */
@@ -1289,9 +1289,10 @@
  *
  ******************************************************************************/
 extern void BTA_DmAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
-                            LINK_KEY link_key, tBTA_SERVICE_MASK trusted_mask,
-                            bool is_trusted, uint8_t key_type,
-                            tBTA_IO_CAP io_cap, uint8_t pin_length);
+                            const LinkKey& link_key,
+                            tBTA_SERVICE_MASK trusted_mask, bool is_trusted,
+                            uint8_t key_type, tBTA_IO_CAP io_cap,
+                            uint8_t pin_length);
 
 /*******************************************************************************
  *
diff --git a/bta/include/bta_dm_ci.h b/bta/include/bta_dm_ci.h
index dbbace6..a89854a 100644
--- a/bta/include/bta_dm_ci.h
+++ b/bta/include/bta_dm_ci.h
@@ -55,7 +55,7 @@
  *
  ******************************************************************************/
 extern void bta_dm_ci_rmt_oob(bool accept, const RawAddress& bd_addr,
-                              BT_OCTET16 c, BT_OCTET16 r);
+                              const Octet16& c, const Octet16& r);
 /*******************************************************************************
  *
  * Function         bta_dm_sco_ci_data_ready
diff --git a/bta/include/bta_dm_co.h b/bta/include/bta_dm_co.h
index ddb9d22..4561cbf 100644
--- a/bta/include/bta_dm_co.h
+++ b/bta/include/bta_dm_co.h
@@ -105,7 +105,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-extern void bta_dm_co_loc_oob(bool valid, BT_OCTET16 c, BT_OCTET16 r);
+extern void bta_dm_co_loc_oob(bool valid, const Octet16& c, const Octet16& r);
 
 /*******************************************************************************
  *
@@ -207,7 +207,7 @@
  *
  ******************************************************************************/
 extern void bta_dm_co_ble_load_local_keys(
-    tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask, BT_OCTET16 er,
+    tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask, Octet16* p_er,
     tBTA_BLE_LOCAL_ID_KEYS* p_id_keys);
 
 #endif /* BTA_DM_CO_H */
diff --git a/bta/include/bta_gatt_api.h b/bta/include/bta_gatt_api.h
index 2dfe26d..618cb04 100644
--- a/bta/include/bta_gatt_api.h
+++ b/bta/include/bta_gatt_api.h
@@ -25,6 +25,7 @@
 #ifndef BTA_GATT_API_H
 #define BTA_GATT_API_H
 
+#include "bta/gatt/database.h"
 #include "bta_api.h"
 #include "gatt_api.h"
 
@@ -97,34 +98,6 @@
   uint16_t handles[BTA_GATTC_MULTI_MAX];
 } tBTA_GATTC_MULTI;
 
-/* Representation of GATT attribute for storage */
-struct tBTA_GATTC_NV_ATTR {
-  uint16_t handle;
-  bluetooth::Uuid type;
-
-  union {
-    /* primary or secondary service */
-    struct {
-      bluetooth::Uuid uuid;
-      uint16_t e_handle;
-    } service;
-
-    struct {
-      uint16_t s_handle;
-      uint16_t e_handle;
-      bluetooth::Uuid uuid;
-    } included_service;
-
-    struct {
-      uint8_t properties;
-      uint16_t value_handle;
-      bluetooth::Uuid uuid;
-    } characteristic;
-
-    /* for descriptor we don't store value*/
-  } value;
-};
-
 /* callback data structure */
 typedef struct {
   tGATT_STATUS status;
@@ -383,41 +356,6 @@
 /* Server callback function */
 typedef void(tBTA_GATTS_CBACK)(tBTA_GATTS_EVT event, tBTA_GATTS* p_data);
 
-struct tBTA_GATTC_CHARACTERISTIC;
-struct tBTA_GATTC_DESCRIPTOR;
-struct tBTA_GATTC_INCLUDED_SVC;
-
-struct tBTA_GATTC_SERVICE {
-  bluetooth::Uuid uuid;
-  bool is_primary;
-  uint16_t handle;
-  uint16_t s_handle;
-  uint16_t e_handle;
-  std::vector<tBTA_GATTC_CHARACTERISTIC> characteristics;
-  std::vector<tBTA_GATTC_INCLUDED_SVC> included_svc;
-};
-
-struct tBTA_GATTC_CHARACTERISTIC {
-  bluetooth::Uuid uuid;
-  // this is used only during discovery, and not persisted in cache
-  uint16_t declaration_handle;
-  uint16_t value_handle;
-  tGATT_CHAR_PROP properties;
-  std::vector<tBTA_GATTC_DESCRIPTOR> descriptors;
-};
-
-struct tBTA_GATTC_DESCRIPTOR {
-  bluetooth::Uuid uuid;
-  uint16_t handle;
-};
-
-struct tBTA_GATTC_INCLUDED_SVC {
-  bluetooth::Uuid uuid;
-  uint16_t handle;
-  tBTA_GATTC_SERVICE* owning_service; /* owning service*/
-  tBTA_GATTC_SERVICE* included_service;
-};
-
 /*****************************************************************************
  *  External Function Declarations
  ****************************************************************************/
@@ -538,7 +476,7 @@
  * PTS tests.
  */
 extern void BTA_GATTC_DiscoverServiceByUuid(uint16_t conn_id,
-                                            const bluetooth::Uuid& p_srvc_uuid);
+                                            const bluetooth::Uuid& srvc_uuid);
 
 /*******************************************************************************
  *
@@ -549,10 +487,10 @@
  *
  * Parameters       conn_id: connection ID which identify the server.
  *
- * Returns          returns list of tBTA_GATTC_SERVICE or NULL.
+ * Returns          returns list of gatt::Service or NULL.
  *
  ******************************************************************************/
-extern const std::vector<tBTA_GATTC_SERVICE>* BTA_GATTC_GetServices(
+extern const std::vector<gatt::Service>* BTA_GATTC_GetServices(
     uint16_t conn_id);
 
 /*******************************************************************************
@@ -565,11 +503,11 @@
  * Parameters       conn_id: connection ID which identify the server.
  *                  handle: characteristic handle
  *
- * Returns          returns pointer to tBTA_GATTC_CHARACTERISTIC or NULL.
+ * Returns          returns pointer to gatt::Characteristic or NULL.
  *
  ******************************************************************************/
-extern const tBTA_GATTC_CHARACTERISTIC* BTA_GATTC_GetCharacteristic(
-    uint16_t conn_id, uint16_t handle);
+extern const gatt::Characteristic* BTA_GATTC_GetCharacteristic(uint16_t conn_id,
+                                                               uint16_t handle);
 
 /*******************************************************************************
  *
@@ -581,21 +519,21 @@
  * Parameters       conn_id: connection ID which identify the server.
  *                  handle: descriptor handle
  *
- * Returns          returns pointer to tBTA_GATTC_DESCRIPTOR or NULL.
+ * Returns          returns pointer to gatt::Descriptor or NULL.
  *
  ******************************************************************************/
-extern const tBTA_GATTC_DESCRIPTOR* BTA_GATTC_GetDescriptor(uint16_t conn_id,
-                                                            uint16_t handle);
+extern const gatt::Descriptor* BTA_GATTC_GetDescriptor(uint16_t conn_id,
+                                                       uint16_t handle);
 
 /* Return characteristic that owns descriptor with handle equal to |handle|, or
  * NULL */
-extern const tBTA_GATTC_CHARACTERISTIC* BTA_GATTC_GetOwningCharacteristic(
+extern const gatt::Characteristic* BTA_GATTC_GetOwningCharacteristic(
     uint16_t conn_id, uint16_t handle);
 
 /* Return service that owns descriptor or characteristic with handle equal to
  * |handle|, or NULL */
-extern const tBTA_GATTC_SERVICE* BTA_GATTC_GetOwningService(uint16_t conn_id,
-                                                            uint16_t handle);
+extern const gatt::Service* BTA_GATTC_GetOwningService(uint16_t conn_id,
+                                                       uint16_t handle);
 
 /*******************************************************************************
  *
diff --git a/bta/include/bta_hearing_aid_api.h b/bta/include/bta_hearing_aid_api.h
index 96ad7e6..e0a3bf5 100644
--- a/bta/include/bta_hearing_aid_api.h
+++ b/bta/include/bta_hearing_aid_api.h
@@ -21,14 +21,16 @@
 #include <base/callback_forward.h>
 #include <hardware/bt_hearing_aid.h>
 
+constexpr uint16_t HA_INTERVAL_10_MS = 10;
+constexpr uint16_t HA_INTERVAL_20_MS = 20;
 
 /** Implementations of HearingAid will also implement this interface */
 class HearingAidAudioReceiver {
  public:
   virtual ~HearingAidAudioReceiver() = default;
   virtual void OnAudioDataReady(const std::vector<uint8_t>& data) = 0;
-  virtual void OnAudioSuspend();
-  virtual void OnAudioResume();
+  virtual void OnAudioSuspend() = 0;
+  virtual void OnAudioResume() = 0;
 };
 
 class HearingAid {
diff --git a/bta/test/gatt/database_builder_sample_device_test.cc b/bta/test/gatt/database_builder_sample_device_test.cc
new file mode 100644
index 0000000..bd4dabc
--- /dev/null
+++ b/bta/test/gatt/database_builder_sample_device_test.cc
@@ -0,0 +1,286 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include <base/logging.h>
+#include <utility>
+
+#include "gatt/database_builder.h"
+
+using bluetooth::Uuid;
+
+namespace gatt {
+
+namespace {
+/* EXPECT_EQ doesn't work well with static constexpr fields, need a variable
+ * with address */
+constexpr std::pair<uint16_t, uint16_t> EXPLORE_END =
+    DatabaseBuilder::EXPLORE_END;
+
+/* make_pair doesn't work well with EXPECT_EQ, have own helper instead */
+inline std::pair<uint16_t, uint16_t> make_pair_u16(uint16_t first,
+                                                   uint16_t second) {
+  return std::make_pair(first, second);
+}
+
+// clang-format off
+Uuid SERVICE_1_UUID = Uuid::FromString("00001800-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_2_UUID = Uuid::FromString("00001801-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_3_UUID = Uuid::FromString("0000180f-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_4_UUID = Uuid::FromString("0000fef5-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_UUID = Uuid::FromString("0000180a-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_6_UUID = Uuid::FromString("0000fe55-0000-1000-8000-00805f9b34fb");
+
+Uuid SERVICE_1_CHAR_1_UUID = Uuid::FromString("00002a00-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_1_CHAR_2_UUID = Uuid::FromString("00002a01-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_1_CHAR_3_UUID = Uuid::FromString("00002a04-0000-1000-8000-00805f9b34fb");
+
+Uuid SERVICE_3_CHAR_1_UUID = Uuid::FromString("00002a19-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_3_CHAR_1_DESC_1_UUID = Uuid::FromString("00002902-0000-1000-8000-00805f9b34fb");
+
+Uuid SERVICE_4_CHAR_1_UUID = Uuid::FromString("8082caa8-41a6-4021-91c6-56f9b954cc34");
+Uuid SERVICE_4_CHAR_2_UUID = Uuid::FromString("724249f0-5ec3-4b5f-8804-42345af08651");
+Uuid SERVICE_4_CHAR_3_UUID = Uuid::FromString("6c53db25-47a1-45fe-a022-7c92fb334fd4");
+Uuid SERVICE_4_CHAR_4_UUID = Uuid::FromString("9d84b9a3-000c-49d8-9183-855b673fda31");
+Uuid SERVICE_4_CHAR_5_UUID = Uuid::FromString("457871e8-d516-4ca1-9116-57d0b17b9cb2");
+Uuid SERVICE_4_CHAR_6_UUID = Uuid::FromString("5f78df94-798c-46f5-990a-b3eb6a065c88");
+Uuid SERVICE_4_CHAR_6_DESC_1_UUID = Uuid::FromString("00002902-0000-1000-8000-00805f9b34fb");
+
+Uuid SERVICE_5_CHAR_1_UUID = Uuid::FromString("00002a29-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_CHAR_2_UUID = Uuid::FromString("00002a24-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_CHAR_3_UUID = Uuid::FromString("00002a25-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_CHAR_4_UUID = Uuid::FromString("00002a27-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_CHAR_5_UUID = Uuid::FromString("00002a26-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_CHAR_6_UUID = Uuid::FromString("00002a28-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_CHAR_7_UUID = Uuid::FromString("00002a50-0000-1000-8000-00805f9b34fb");
+
+Uuid SERVICE_6_CHAR_1_UUID = Uuid::FromString("00000001-1000-1000-8000-00805f9b34fb");
+Uuid SERVICE_6_CHAR_1_DESC_1_UUID = Uuid::FromString("00002902-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_6_CHAR_2_UUID = Uuid::FromString("00000002-1000-1000-8000-00805f9b34fb");
+Uuid SERVICE_6_CHAR_3_UUID = Uuid::FromString("00000003-1000-1000-8000-00805f9b34fb");
+// clang-format on
+
+}  // namespace
+
+// clang-format off
+/* Content of sample database, comes from Daydream controller:
+Service: handle=0x0001, end_handle=0x0007, uuid=00001800-0000-1000-8000-00805f9b34fb
+	 Characteristic: declaration_handle=0x0002, value_handle=0x0003, uuid=00002a00-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x0004, value_handle=0x0005, uuid=00002a01-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x0006, value_handle=0x0007, uuid=00002a04-0000-1000-8000-00805f9b34fb, prop=0x02
+Service: handle=0x0008, end_handle=0x0008, uuid=00001801-0000-1000-8000-00805f9b34fb
+Service: handle=0x0009, end_handle=0x000c, uuid=0000180f-0000-1000-8000-00805f9b34fb
+	 Characteristic: declaration_handle=0x000a, value_handle=0x000b, uuid=00002a19-0000-1000-8000-00805f9b34fb, prop=0x12
+		 Descriptor: handle=0x000c, uuid=00002902-0000-1000-8000-00805f9b34fb
+Service: handle=0x000d, end_handle=0x001a, uuid=0000fef5-0000-1000-8000-00805f9b34fb
+	 Characteristic: declaration_handle=0x000e, value_handle=0x000f, uuid=8082caa8-41a6-4021-91c6-56f9b954cc34, prop=0x0a
+	 Characteristic: declaration_handle=0x0010, value_handle=0x0011, uuid=724249f0-5ec3-4b5f-8804-42345af08651, prop=0x0a
+	 Characteristic: declaration_handle=0x0012, value_handle=0x0013, uuid=6c53db25-47a1-45fe-a022-7c92fb334fd4, prop=0x02
+	 Characteristic: declaration_handle=0x0014, value_handle=0x0015, uuid=9d84b9a3-000c-49d8-9183-855b673fda31, prop=0x0a
+	 Characteristic: declaration_handle=0x0016, value_handle=0x0017, uuid=457871e8-d516-4ca1-9116-57d0b17b9cb2, prop=0x0e
+	 Characteristic: declaration_handle=0x0018, value_handle=0x0019, uuid=5f78df94-798c-46f5-990a-b3eb6a065c88, prop=0x12
+		 Descriptor: handle=0x001a, uuid=00002902-0000-1000-8000-00805f9b34fb
+Service: handle=0x001b, end_handle=0x0029, uuid=0000180a-0000-1000-8000-00805f9b34fb
+	 Characteristic: declaration_handle=0x001c, value_handle=0x001d, uuid=00002a29-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x001e, value_handle=0x001f, uuid=00002a24-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x0020, value_handle=0x0021, uuid=00002a25-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x0022, value_handle=0x0023, uuid=00002a27-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x0024, value_handle=0x0025, uuid=00002a26-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x0026, value_handle=0x0027, uuid=00002a28-0000-1000-8000-00805f9b34fb, prop=0x02
+	 Characteristic: declaration_handle=0x0028, value_handle=0x0029, uuid=00002a50-0000-1000-8000-00805f9b34fb, prop=0x02
+Service: handle=0x002a, end_handle=0x0031, uuid=0000fe55-0000-1000-8000-00805f9b34fb
+	 Characteristic: declaration_handle=0x002b, value_handle=0x002c, uuid=00000001-1000-1000-8000-00805f9b34fb, prop=0x10
+		 Descriptor: handle=0x002d, uuid=00002902-0000-1000-8000-00805f9b34fb
+	 Characteristic: declaration_handle=0x002e, value_handle=0x002f, uuid=00000002-1000-1000-8000-00805f9b34fb, prop=0x08
+	 Characteristic: declaration_handle=0x0030, value_handle=0x0031, uuid=00000003-1000-1000-8000-00805f9b34fb, prop=0x02
+*/
+// clang-format on
+
+/* This test verifies that DatabaseBuilder will properly discover database
+ * content from a remote device. It also verify that after the discovery is
+ * done, returned database is equal to the discovered one */
+TEST(DatabaseBuilderSampleDeviceTest, DoDiscovery) {
+  DatabaseBuilder builder;
+
+  EXPECT_FALSE(builder.InProgress());
+
+  // At start of discovery, builder will receive All services in order from
+  // lower layers.
+  builder.AddService(0x0001, 0x0007, SERVICE_1_UUID, true);
+
+  // The moment we receive first service, we are in progress
+  // TODO: we should be able to set InProgress state once we sent GATT request,
+  // not when it's back and parsed
+  EXPECT_TRUE(builder.InProgress());
+
+  builder.AddService(0x0008, 0x0008, SERVICE_2_UUID, true);
+  builder.AddService(0x0009, 0x000c, SERVICE_3_UUID, true);
+  builder.AddService(0x000d, 0x001a, SERVICE_4_UUID, true);
+  builder.AddService(0x001b, 0x0029, SERVICE_5_UUID, true);
+  builder.AddService(0x002a, 0x0031, SERVICE_6_UUID, true);
+
+  // At this moment, all services are received, stack will grab them one and one
+  // to discover their content.
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+
+  // Grabbing first service, to start Included Service and Characteristic
+  // discovery
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x0001, 0x0007));
+
+  builder.AddCharacteristic(0x0002, 0x0003, SERVICE_1_CHAR_1_UUID, 0x02);
+  builder.AddCharacteristic(0x0004, 0x0005, SERVICE_1_CHAR_2_UUID, 0x02);
+  builder.AddCharacteristic(0x0006, 0x0007, SERVICE_1_CHAR_3_UUID, 0x02);
+
+  // All characteristics were discovered, stack will try to look for
+  // descriptors. Since there is no space for descriptors, builder should return
+  // nothing more to discover
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(), EXPLORE_END);
+
+  // Service with handles 0x0008, 0x0008 is skipped for exploration - we know
+  // it's empty.
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x0009, 0x000c));
+
+  builder.AddCharacteristic(0x000a, 0x000b, SERVICE_3_CHAR_1_UUID, 0x12);
+
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(),
+            make_pair_u16(0x000c, 0x000c));
+
+  builder.AddDescriptor(0x000c, SERVICE_3_CHAR_1_DESC_1_UUID);
+
+  // All descriptors were explored
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(), EXPLORE_END);
+
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x000d, 0x001a));
+
+  builder.AddCharacteristic(0x000e, 0x000f, SERVICE_4_CHAR_1_UUID, 0x0a);
+  builder.AddCharacteristic(0x0010, 0x0011, SERVICE_4_CHAR_2_UUID, 0x0a);
+  builder.AddCharacteristic(0x0012, 0x0013, SERVICE_4_CHAR_3_UUID, 0x02);
+  builder.AddCharacteristic(0x0014, 0x0015, SERVICE_4_CHAR_4_UUID, 0x0a);
+  builder.AddCharacteristic(0x0016, 0x0017, SERVICE_4_CHAR_5_UUID, 0x0e);
+  builder.AddCharacteristic(0x0018, 0x0019, SERVICE_4_CHAR_6_UUID, 0x12);
+
+  // Just last Characteristic have space for descriptor
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(),
+            make_pair_u16(0x001a, 0x001a));
+
+  builder.AddDescriptor(0x001a, SERVICE_4_CHAR_6_DESC_1_UUID);
+
+  // All descriptors were explored
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(), EXPLORE_END);
+
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x001b, 0x0029));
+
+  builder.AddCharacteristic(0x001c, 0x001d, SERVICE_5_CHAR_1_UUID, 0x02);
+  builder.AddCharacteristic(0x001e, 0x001f, SERVICE_5_CHAR_2_UUID, 0x02);
+  builder.AddCharacteristic(0x0020, 0x0021, SERVICE_5_CHAR_3_UUID, 0x02);
+  builder.AddCharacteristic(0x0022, 0x0023, SERVICE_5_CHAR_4_UUID, 0x02);
+  builder.AddCharacteristic(0x0024, 0x0025, SERVICE_5_CHAR_5_UUID, 0x02);
+  builder.AddCharacteristic(0x0026, 0x0027, SERVICE_5_CHAR_6_UUID, 0x02);
+  builder.AddCharacteristic(0x0028, 0x0029, SERVICE_5_CHAR_7_UUID, 0x02);
+
+  // No space for descriptors
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(), EXPLORE_END);
+
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x002a, 0x0031));
+
+  builder.AddCharacteristic(0x002b, 0x002c, SERVICE_6_CHAR_1_UUID, 0x10);
+  builder.AddCharacteristic(0x002e, 0x002f, SERVICE_6_CHAR_2_UUID, 0x08);
+  builder.AddCharacteristic(0x0030, 0x0031, SERVICE_6_CHAR_3_UUID, 0x02);
+
+  // Just one Characteristic have space for descriptor
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(),
+            make_pair_u16(0x002d, 0x002d));
+
+  builder.AddDescriptor(0x002d, SERVICE_6_CHAR_1_DESC_1_UUID);
+
+  // All descriptors were explored
+  EXPECT_EQ(builder.NextDescriptorRangeToExplore(), EXPLORE_END);
+
+  EXPECT_FALSE(builder.StartNextServiceExploration());
+
+  EXPECT_TRUE(builder.InProgress());
+  Database result = builder.Build();
+  EXPECT_FALSE(builder.InProgress());
+
+  // verify that the returned database matches what was discovered
+  EXPECT_EQ(result.Services()[0].handle, 0x0001);
+  EXPECT_EQ(result.Services()[0].uuid, SERVICE_1_UUID);
+  EXPECT_EQ(result.Services()[1].uuid, SERVICE_2_UUID);
+  EXPECT_EQ(result.Services()[2].uuid, SERVICE_3_UUID);
+  EXPECT_EQ(result.Services()[3].uuid, SERVICE_4_UUID);
+  EXPECT_EQ(result.Services()[4].uuid, SERVICE_5_UUID);
+  EXPECT_EQ(result.Services()[5].uuid, SERVICE_6_UUID);
+
+  EXPECT_EQ(result.Services()[0].characteristics[0].uuid,
+            SERVICE_1_CHAR_1_UUID);
+  EXPECT_EQ(result.Services()[0].characteristics[1].uuid,
+            SERVICE_1_CHAR_2_UUID);
+  EXPECT_EQ(result.Services()[0].characteristics[2].uuid,
+            SERVICE_1_CHAR_3_UUID);
+
+  EXPECT_EQ(result.Services()[2].characteristics[0].uuid,
+            SERVICE_3_CHAR_1_UUID);
+  EXPECT_EQ(result.Services()[2].characteristics[0].descriptors[0].uuid,
+            SERVICE_3_CHAR_1_DESC_1_UUID);
+
+  EXPECT_EQ(result.Services()[3].characteristics[0].uuid,
+            SERVICE_4_CHAR_1_UUID);
+  EXPECT_EQ(result.Services()[3].characteristics[1].uuid,
+            SERVICE_4_CHAR_2_UUID);
+  EXPECT_EQ(result.Services()[3].characteristics[2].uuid,
+            SERVICE_4_CHAR_3_UUID);
+  EXPECT_EQ(result.Services()[3].characteristics[3].uuid,
+            SERVICE_4_CHAR_4_UUID);
+  EXPECT_EQ(result.Services()[3].characteristics[4].uuid,
+            SERVICE_4_CHAR_5_UUID);
+  EXPECT_EQ(result.Services()[3].characteristics[5].uuid,
+            SERVICE_4_CHAR_6_UUID);
+  EXPECT_EQ(result.Services()[3].characteristics[5].descriptors[0].uuid,
+            SERVICE_4_CHAR_6_DESC_1_UUID);
+
+  EXPECT_EQ(result.Services()[4].characteristics[0].uuid,
+            SERVICE_5_CHAR_1_UUID);
+  EXPECT_EQ(result.Services()[4].characteristics[1].uuid,
+            SERVICE_5_CHAR_2_UUID);
+  EXPECT_EQ(result.Services()[4].characteristics[2].uuid,
+            SERVICE_5_CHAR_3_UUID);
+  EXPECT_EQ(result.Services()[4].characteristics[3].uuid,
+            SERVICE_5_CHAR_4_UUID);
+  EXPECT_EQ(result.Services()[4].characteristics[4].uuid,
+            SERVICE_5_CHAR_5_UUID);
+  EXPECT_EQ(result.Services()[4].characteristics[5].uuid,
+            SERVICE_5_CHAR_6_UUID);
+  EXPECT_EQ(result.Services()[4].characteristics[6].uuid,
+            SERVICE_5_CHAR_7_UUID);
+
+  EXPECT_EQ(result.Services()[5].characteristics[0].uuid,
+            SERVICE_6_CHAR_1_UUID);
+  EXPECT_EQ(result.Services()[5].characteristics[0].descriptors[0].uuid,
+            SERVICE_6_CHAR_1_DESC_1_UUID);
+  EXPECT_EQ(result.Services()[5].characteristics[1].uuid,
+            SERVICE_6_CHAR_2_UUID);
+  EXPECT_EQ(result.Services()[5].characteristics[2].uuid,
+            SERVICE_6_CHAR_3_UUID);
+}
+
+}  // namespace gatt
\ No newline at end of file
diff --git a/bta/test/gatt/database_builder_test.cc b/bta/test/gatt/database_builder_test.cc
new file mode 100644
index 0000000..fcafb49
--- /dev/null
+++ b/bta/test/gatt/database_builder_test.cc
@@ -0,0 +1,165 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include <base/logging.h>
+#include <utility>
+
+#include "gatt/database_builder.h"
+
+using bluetooth::Uuid;
+
+namespace gatt {
+
+namespace {
+/* make_pair doesn't work well with EXPECT_EQ, have own helper instead */
+inline std::pair<uint16_t, uint16_t> make_pair_u16(uint16_t first,
+                                                   uint16_t second) {
+  return std::make_pair(first, second);
+}
+
+Uuid SERVICE_1_UUID = Uuid::FromString("00001800-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_2_UUID = Uuid::FromString("00001801-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_3_UUID = Uuid::FromString("0000180f-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_4_UUID = Uuid::FromString("0000fef5-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_5_UUID = Uuid::FromString("0000180a-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_1_CHAR_1_UUID =
+    Uuid::FromString("00002a00-0000-1000-8000-00805f9b34fb");
+Uuid SERVICE_1_CHAR_1_DESC_1_UUID =
+    Uuid::FromString("00002902-0000-1000-8000-00805f9b34fb");
+
+}  // namespace
+
+/* Verify adding empty service works ok */
+TEST(DatabaseBuilderTest, EmptyServiceAddTest) {
+  DatabaseBuilder builder;
+
+  EXPECT_FALSE(builder.InProgress());
+
+  // Simple database, just one empty
+  builder.AddService(0x0001, 0x0001, SERVICE_1_UUID, true);
+  EXPECT_FALSE(builder.StartNextServiceExploration());
+
+  Database result = builder.Build();
+
+  // verify that the returned database matches what was discovered
+  EXPECT_EQ(result.Services()[0].handle, 0x0001);
+  EXPECT_EQ(result.Services()[0].end_handle, 0x0001);
+  EXPECT_EQ(result.Services()[0].is_primary, true);
+  EXPECT_EQ(result.Services()[0].uuid, SERVICE_1_UUID);
+}
+
+/* Verify adding service, characteristic and descriptor work */
+TEST(DatabaseBuilderTest, DescriptorAddTest) {
+  DatabaseBuilder builder;
+
+  EXPECT_FALSE(builder.InProgress());
+
+  // Simple database, just one empty
+  builder.AddService(0x0001, 0x000f, SERVICE_1_UUID, true);
+  builder.AddCharacteristic(0x0002, 0x0003, SERVICE_1_CHAR_1_UUID, 0x02);
+  builder.AddDescriptor(0x0004, SERVICE_1_CHAR_1_DESC_1_UUID);
+
+  Database result = builder.Build();
+
+  // verify that the returned database matches what was discovered
+  EXPECT_EQ(result.Services()[0].handle, 0x0001);
+  EXPECT_EQ(result.Services()[0].end_handle, 0x000f);
+  EXPECT_EQ(result.Services()[0].is_primary, true);
+  EXPECT_EQ(result.Services()[0].uuid, SERVICE_1_UUID);
+
+  EXPECT_EQ(result.Services()[0].characteristics[0].uuid,
+            SERVICE_1_CHAR_1_UUID);
+  EXPECT_EQ(result.Services()[0].characteristics[0].declaration_handle, 0x0002);
+  EXPECT_EQ(result.Services()[0].characteristics[0].value_handle, 0x0003);
+  EXPECT_EQ(result.Services()[0].characteristics[0].properties, 0x02);
+
+  EXPECT_EQ(result.Services()[0].characteristics[0].descriptors[0].uuid,
+            SERVICE_1_CHAR_1_DESC_1_UUID);
+  EXPECT_EQ(result.Services()[0].characteristics[0].descriptors[0].handle,
+            0x0004);
+}
+
+/* This test verifies that DatabaseBuilder properly handle discovery of
+ * secondary service, that is added to the discovery queue from included service
+ * definition. Such service might come out of order.  */
+TEST(DatabaseBuilderTest, SecondaryServiceOutOfOrderTest) {
+  DatabaseBuilder builder;
+
+  EXPECT_FALSE(builder.InProgress());
+
+  // At start of discovery, builder will receive All services in order from
+  // lower layers.
+  builder.AddService(0x0001, 0x000f, SERVICE_1_UUID, true);
+  builder.AddService(0x0030, 0x003f, SERVICE_3_UUID, true);
+  builder.AddService(0x0050, 0x005f, SERVICE_5_UUID, true);
+
+  // First service skipped, no place for handles
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x0001, 0x000f));
+
+  // For this test, content of first service is irrevelant
+
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  // Grabbing first service, to start Included Service and Characteristic
+  // discovery
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x0030, 0x003f));
+
+  builder.AddIncludedService(0x0031, SERVICE_4_UUID, 0x0040, 0x004f);
+  builder.AddIncludedService(0x0032, SERVICE_2_UUID, 0x0020, 0x002f);
+
+  /* Secondary service exploration */
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x0020, 0x002f));
+
+  /* Secondary service exploration */
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x0040, 0x004f));
+
+  /* Back to primary service exploration */
+  EXPECT_TRUE(builder.StartNextServiceExploration());
+  EXPECT_EQ(builder.CurrentlyExploredService(), make_pair_u16(0x0050, 0x005f));
+
+  Database result = builder.Build();
+
+  // verify that the returned database matches what was discovered
+  EXPECT_EQ(result.Services()[0].handle, 0x0001);
+  EXPECT_EQ(result.Services()[0].is_primary, true);
+  EXPECT_EQ(result.Services()[0].uuid, SERVICE_1_UUID);
+
+  EXPECT_EQ(result.Services()[1].handle, 0x0020);
+  EXPECT_EQ(result.Services()[1].end_handle, 0x002f);
+  EXPECT_EQ(result.Services()[1].uuid, SERVICE_2_UUID);
+  EXPECT_EQ(result.Services()[1].is_primary, false);
+
+  EXPECT_EQ(result.Services()[2].handle, 0x0030);
+  EXPECT_EQ(result.Services()[2].end_handle, 0x003f);
+  EXPECT_EQ(result.Services()[2].uuid, SERVICE_3_UUID);
+  EXPECT_EQ(result.Services()[2].is_primary, true);
+
+  EXPECT_EQ(result.Services()[3].handle, 0x0040);
+  EXPECT_EQ(result.Services()[3].uuid, SERVICE_4_UUID);
+  EXPECT_EQ(result.Services()[3].is_primary, false);
+
+  EXPECT_EQ(result.Services()[4].handle, 0x0050);
+  EXPECT_EQ(result.Services()[4].uuid, SERVICE_5_UUID);
+  EXPECT_EQ(result.Services()[4].is_primary, true);
+}
+
+}  // namespace gatt
\ No newline at end of file
diff --git a/bta/test/gatt/database_test.cc b/bta/test/gatt/database_test.cc
new file mode 100644
index 0000000..78250ec
--- /dev/null
+++ b/bta/test/gatt/database_test.cc
@@ -0,0 +1,207 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gtest/gtest.h>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include "gatt/database.h"
+#include "gatt/database_builder.h"
+#include "stack/include/gattdefs.h"
+
+using bluetooth::Uuid;
+
+namespace gatt {
+
+namespace {
+const Uuid PRIMARY_SERVICE = Uuid::From16Bit(GATT_UUID_PRI_SERVICE);
+const Uuid SECONDARY_SERVICE = Uuid::From16Bit(GATT_UUID_SEC_SERVICE);
+const Uuid INCLUDE = Uuid::From16Bit(GATT_UUID_INCLUDE_SERVICE);
+const Uuid CHARACTERISTIC = Uuid::From16Bit(GATT_UUID_CHAR_DECLARE);
+
+Uuid SERVICE_1_UUID = Uuid::FromString("1800");
+Uuid SERVICE_2_UUID = Uuid::FromString("1801");
+Uuid SERVICE_1_CHAR_1_UUID = Uuid::FromString("2a00");
+Uuid SERVICE_1_CHAR_1_DESC_1_UUID = Uuid::FromString("2902");
+}  // namespace
+
+/* This test makes sure that each possible GATT cache element is properly
+ * serialized into StoredAttribute */
+TEST(GattDatabaseTest, serialize_deserialize_binary_test) {
+  DatabaseBuilder builder;
+  builder.AddService(0x0001, 0x000f, SERVICE_1_UUID, true);
+  builder.AddService(0x0010, 0x001f, SERVICE_2_UUID, false);
+  builder.AddIncludedService(0x0002, SERVICE_2_UUID, 0x0010, 0x001f);
+  builder.AddCharacteristic(0x0003, 0x0004, SERVICE_1_CHAR_1_UUID, 0x02);
+  builder.AddDescriptor(0x0005, SERVICE_1_CHAR_1_DESC_1_UUID);
+
+  Database db = builder.Build();
+  std::vector<StoredAttribute> serialized = db.Serialize();
+
+  // Primary Service
+  EXPECT_EQ(serialized[0].handle, 0x0001);
+  EXPECT_EQ(serialized[0].type, PRIMARY_SERVICE);
+  EXPECT_EQ(serialized[0].value.service.uuid, SERVICE_1_UUID);
+  EXPECT_EQ(serialized[0].value.service.end_handle, 0x000f);
+
+  // Secondary Service
+  EXPECT_EQ(serialized[1].handle, 0x0010);
+  EXPECT_EQ(serialized[1].type, SECONDARY_SERVICE);
+  EXPECT_EQ(serialized[1].value.service.uuid, SERVICE_2_UUID);
+  EXPECT_EQ(serialized[1].value.service.end_handle, 0x001f);
+
+  // Included Service
+  EXPECT_EQ(serialized[2].handle, 0x0002);
+  EXPECT_EQ(serialized[2].type, INCLUDE);
+  EXPECT_EQ(serialized[2].value.included_service.handle, 0x0010);
+  EXPECT_EQ(serialized[2].value.included_service.end_handle, 0x001f);
+  EXPECT_EQ(serialized[2].value.included_service.uuid, SERVICE_2_UUID);
+
+  // Characteristic
+  EXPECT_EQ(serialized[3].handle, 0x0003);
+  EXPECT_EQ(serialized[3].type, CHARACTERISTIC);
+  EXPECT_EQ(serialized[3].value.characteristic.properties, 0x02);
+  EXPECT_EQ(serialized[3].value.characteristic.value_handle, 0x0004);
+  EXPECT_EQ(serialized[3].value.characteristic.uuid, SERVICE_1_CHAR_1_UUID);
+
+  // Descriptor
+  EXPECT_EQ(serialized[4].handle, 0x0005);
+  EXPECT_EQ(serialized[4].type, SERVICE_1_CHAR_1_DESC_1_UUID);
+}
+
+/* This test makes sure that Service represented in StoredAttribute have proper
+ * binary format. */
+TEST(GattCacheTest, stored_attribute_to_binary_service_test) {
+  StoredAttribute attr;
+
+  /* make sure padding at end of union is cleared */
+  memset(&attr, 0, sizeof(attr));
+
+  attr = {
+      .handle = 0x0001,
+      .type = PRIMARY_SERVICE,
+      .value = {.service = {.uuid = Uuid::FromString("1800"),
+                            .end_handle = 0x001c}},
+  };
+
+  constexpr size_t len = sizeof(StoredAttribute);
+  // clang-format off
+  uint8_t binary_form[len] = {
+      /*handle */ 0x01, 0x00,
+      /* type*/ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+      /* service uuid */ 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+      /* end handle */ 0x1C, 0x00,
+      /* cleared padding at end of union*/ 0x00, 0x00};
+  // clang-format on
+
+  // useful for debugging:
+  // LOG(ERROR) << " " << base::HexEncode(&attr, len);
+  EXPECT_EQ(memcmp(binary_form, &attr, len), 0);
+}
+
+/* This test makes sure that Service represented in StoredAttribute have proper
+ * binary format. */
+TEST(GattCacheTest, stored_attribute_to_binary_included_service_test) {
+  StoredAttribute attr;
+
+  /* make sure padding at end of union is cleared */
+  memset(&attr, 0, sizeof(attr));
+
+  attr = {
+      .handle = 0x0001,
+      .type = INCLUDE,
+      .value = {.included_service =
+                    {
+                        .handle = 0x0010,
+                        .end_handle = 0x001f,
+                        .uuid = Uuid::FromString("1801"),
+                    }},
+  };
+
+  constexpr size_t len = sizeof(StoredAttribute);
+  // clang-format off
+  uint8_t binary_form[len] = {
+      /*handle */ 0x01, 0x00,
+      /* type*/ 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+      /* handle */ 0x10, 0x00,
+      /* end handle */ 0x1f, 0x00,
+      /* service uuid */ 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+  // clang-format on
+
+  // useful for debugging:
+  // LOG(ERROR) << " " << base::HexEncode(&attr, len);
+  EXPECT_EQ(memcmp(binary_form, &attr, len), 0);
+}
+
+/* This test makes sure that Characteristic represented in StoredAttribute have
+ * proper binary format. */
+TEST(GattCacheTest, stored_attribute_to_binary_characteristic_test) {
+  StoredAttribute attr;
+
+  /* make sure padding at end of union is cleared */
+  memset(&attr, 0, sizeof(attr));
+
+  attr = {
+      .handle = 0x0002,
+      .type = CHARACTERISTIC,
+      .value = {.characteristic = {.properties = 0x02,
+                                   .value_handle = 0x0003,
+                                   .uuid = Uuid::FromString("2a00")}},
+  };
+
+  constexpr size_t len = sizeof(StoredAttribute);
+  // clang-format off
+  uint8_t binary_form[len] = {
+      /*handle */ 0x02, 0x00,
+      /* type */ 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+      /* properties */ 0x02,
+      /* after properties there is one byte padding. This might cause troube
+         on other platforms, investigate if it's ever a problem */ 0x00,
+      /* value handle */ 0x03, 0x00,
+      /* uuid */ 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB};
+  // clang-format on
+
+  // useful for debugging:
+  // LOG(ERROR) << " " << base::HexEncode(&attr, len);
+  EXPECT_EQ(memcmp(binary_form, &attr, len), 0);
+}
+
+/* This test makes sure that Descriptor represented in StoredAttribute have
+ * proper binary format. */
+TEST(GattCacheTest, stored_attribute_to_binary_descriptor_test) {
+  StoredAttribute attr;
+
+  /* make sure padding at end of union is cleared */
+  memset(&attr, 0, sizeof(attr));
+
+  attr = {.handle = 0x0003, .type = Uuid::FromString("2902"), .value = {}};
+
+  constexpr size_t len = sizeof(StoredAttribute);
+  // clang-format off
+  uint8_t binary_form[len] = {
+      /*handle */ 0x03, 0x00,
+      /* type */ 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
+      /* clear padding    */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                             0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+  // clang-format on
+
+  // useful for debugging:
+  // LOG(ERROR) << " " << base::HexEncode(&attr, len);
+  EXPECT_EQ(memcmp(binary_form, &attr, len), 0);
+}
+}  // namespace gatt
\ No newline at end of file
diff --git a/bta/test/gatt_cache_file_test.cc b/bta/test/gatt_cache_file_test.cc
deleted file mode 100644
index 66d1caa..0000000
--- a/bta/test/gatt_cache_file_test.cc
+++ /dev/null
@@ -1,54 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2018 The Android Open Source Project
- *
- *  Licensed under the Apache License, Version 2.0 (the "License");
- *  you may not use this file except in compliance with the License.
- *  You may obtain a copy of the License at:
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing, software
- *  distributed under the License is distributed on an "AS IS" BASIS,
- *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *  See the License for the specific language governing permissions and
- *  limitations under the License.
- *
- ******************************************************************************/
-
-#include <gtest/gtest.h>
-
-#include <base/logging.h>
-#include <base/strings/string_number_conversions.h>
-#include "bta/include/bta_gatt_api.h"
-
-using bluetooth::Uuid;
-
-/* This test makes sure that cache element is properly encoded into file*/
-TEST(GattCacheTest, nv_attr_service_to_binary_test) {
-  tBTA_GATTC_NV_ATTR attr;
-
-  /* make sure padding at end of union is cleared */
-  memset(&attr, 0, sizeof(attr));
-
-  attr = {
-      .handle = 0x0001,
-      .type = Uuid::FromString("2800"),
-      .value = {.service = {.uuid = Uuid::FromString("1800"),
-                            .e_handle = 0x001c}},
-  };
-
-  constexpr size_t len = sizeof(tBTA_GATTC_NV_ATTR);
-  // clang-format off
-  uint8_t binary_form[len] = {
-      /*handle */ 0x01, 0x00,
-      /* type*/ 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
-      /* service uuid */ 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB,
-      /* end handle */ 0x1C, 0x00,
-      /* cleared padding at end of union*/ 0x00, 0x00};
-  // clang-format on
-
-  // useful for debugging:
-  // LOG(ERROR) << " " << base::HexEncode(&attr, len);
-  EXPECT_EQ(memcmp(binary_form, &attr, len), 0);
-}
diff --git a/btif/Android.bp b/btif/Android.bp
index f9177a7..4b2dc30 100644
--- a/btif/Android.bp
+++ b/btif/Android.bp
@@ -121,6 +121,7 @@
     header_libs: ["libbluetooth_headers"],
     shared_libs: [
         "libaudioclient",
+        "android.hardware.bluetooth.a2dp@1.0",
         "libhidlbase",
         "liblog",
         "libprotobuf-cpp-lite",
@@ -132,7 +133,9 @@
         "libbtcore",
         "libbt-stack",
         "libbt-sbc-encoder",
+        "libbt-utils",
         "libFraunhoferAAC",
+        "libg722codec",
         "libbtdevice",
         "libbt-hci",
         "libudrv-uipc",
diff --git a/btif/BUILD.gn b/btif/BUILD.gn
index c5f820a..56a8d33 100644
--- a/btif/BUILD.gn
+++ b/btif/BUILD.gn
@@ -17,7 +17,9 @@
 static_library("btif") {
   sources = [
     "//audio_a2dp_hw/src/audio_a2dp_hw_utils.cc",
+    "//audio_hearing_aid_hw/src/audio_hearing_aid_hw_utils.cc",
     "src/btif_a2dp.cc",
+    "src/btif_a2dp_audio_interface_linux.cc",
     "src/btif_a2dp_control.cc",
     "src/btif_a2dp_sink.cc",
     "src/btif_a2dp_source.cc",
@@ -78,7 +80,9 @@
   include_dirs = [
     "include",
     "//",
+    "//linux_include",
     "//audio_a2dp_hw/include",
+    "//audio_hearing_aid_hw/include",
     "//bta/include",
     "//bta/sys",
     "//btcore/include",
diff --git a/btif/avrcp/avrcp_service.cc b/btif/avrcp/avrcp_service.cc
index 16b5fab..186866b 100644
--- a/btif/avrcp/avrcp_service.cc
+++ b/btif/avrcp/avrcp_service.cc
@@ -18,6 +18,9 @@
 
 #include <base/bind.h>
 #include <base/logging.h>
+#include <base/task/cancelable_task_tracker.h>
+#include <base/threading/thread.h>
+#include <mutex>
 #include <sstream>
 
 #include "bta_closure_api.h"
@@ -31,6 +34,22 @@
 AvrcpService* AvrcpService::instance_ = nullptr;
 AvrcpService::ServiceInterfaceImpl* AvrcpService::service_interface_ = nullptr;
 
+std::mutex jni_mutex_;
+base::MessageLoop* jni_message_loop_ = nullptr;
+base::CancelableTaskTracker task_tracker_;
+
+void do_in_avrcp_jni(const base::Closure& task) {
+  std::lock_guard<std::mutex> lock(jni_mutex_);
+
+  if (jni_message_loop_ == nullptr) {
+    LOG(WARNING) << __func__ << ": jni_message_loop_ is null";
+    return;
+  }
+
+  task_tracker_.PostTask(jni_message_loop_->task_runner().get(), FROM_HERE,
+                         task);
+}
+
 class A2dpInterfaceImpl : public A2dpInterface {
   RawAddress active_peer() override { return btif_av_source_active_peer(); }
 } a2dp_interface_;
@@ -116,8 +135,8 @@
   MediaInterfaceWrapper(MediaInterface* cb) : wrapped_(cb){};
 
   void SendKeyEvent(uint8_t key, KeyState state) override {
-    do_in_jni_thread(base::Bind(&MediaInterface::SendKeyEvent,
-                                base::Unretained(wrapped_), key, state));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::SendKeyEvent,
+                               base::Unretained(wrapped_), key, state));
   }
 
   void GetSongInfo(SongInfoCallback info_cb) override {
@@ -127,8 +146,8 @@
 
     auto bound_cb = base::Bind(cb_lambda, info_cb);
 
-    do_in_jni_thread(base::Bind(&MediaInterface::GetSongInfo,
-                                base::Unretained(wrapped_), bound_cb));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::GetSongInfo,
+                               base::Unretained(wrapped_), bound_cb));
   }
 
   void GetPlayStatus(PlayStatusCallback status_cb) override {
@@ -138,8 +157,8 @@
 
     auto bound_cb = base::Bind(cb_lambda, status_cb);
 
-    do_in_jni_thread(base::Bind(&MediaInterface::GetPlayStatus,
-                                base::Unretained(wrapped_), bound_cb));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::GetPlayStatus,
+                               base::Unretained(wrapped_), bound_cb));
   }
 
   void GetNowPlayingList(NowPlayingCallback now_playing_cb) override {
@@ -151,8 +170,8 @@
 
     auto bound_cb = base::Bind(cb_lambda, now_playing_cb);
 
-    do_in_jni_thread(base::Bind(&MediaInterface::GetNowPlayingList,
-                                base::Unretained(wrapped_), bound_cb));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::GetNowPlayingList,
+                               base::Unretained(wrapped_), bound_cb));
   }
 
   void GetMediaPlayerList(MediaListCallback list_cb) override {
@@ -164,8 +183,8 @@
 
     auto bound_cb = base::Bind(cb_lambda, list_cb);
 
-    do_in_jni_thread(base::Bind(&MediaInterface::GetMediaPlayerList,
-                                base::Unretained(wrapped_), bound_cb));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::GetMediaPlayerList,
+                               base::Unretained(wrapped_), bound_cb));
   }
 
   void GetFolderItems(uint16_t player_id, std::string media_id,
@@ -177,9 +196,9 @@
 
     auto bound_cb = base::Bind(cb_lambda, folder_cb);
 
-    do_in_jni_thread(base::Bind(&MediaInterface::GetFolderItems,
-                                base::Unretained(wrapped_), player_id, media_id,
-                                bound_cb));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::GetFolderItems,
+                               base::Unretained(wrapped_), player_id, media_id,
+                               bound_cb));
   }
 
   void SetBrowsedPlayer(uint16_t player_id,
@@ -191,21 +210,21 @@
 
     auto bound_cb = base::Bind(cb_lambda, browse_cb);
 
-    do_in_jni_thread(base::Bind(&MediaInterface::SetBrowsedPlayer,
-                                base::Unretained(wrapped_), player_id,
-                                bound_cb));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::SetBrowsedPlayer,
+                               base::Unretained(wrapped_), player_id,
+                               bound_cb));
   }
 
   void PlayItem(uint16_t player_id, bool now_playing,
                 std::string media_id) override {
-    do_in_jni_thread(base::Bind(&MediaInterface::PlayItem,
-                                base::Unretained(wrapped_), player_id,
-                                now_playing, media_id));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::PlayItem,
+                               base::Unretained(wrapped_), player_id,
+                               now_playing, media_id));
   }
 
   void SetActiveDevice(const RawAddress& address) override {
-    do_in_jni_thread(base::Bind(&MediaInterface::SetActiveDevice,
-                                base::Unretained(wrapped_), address));
+    do_in_avrcp_jni(base::Bind(&MediaInterface::SetActiveDevice,
+                               base::Unretained(wrapped_), address));
   }
 
   void RegisterUpdateCallback(MediaCallbacks* callback) override {
@@ -227,7 +246,7 @@
   VolumeInterfaceWrapper(VolumeInterface* interface) : wrapped_(interface){};
 
   void DeviceConnected(const RawAddress& bdaddr) override {
-    do_in_jni_thread(
+    do_in_avrcp_jni(
         base::Bind(static_cast<void (VolumeInterface::*)(const RawAddress&)>(
                        &VolumeInterface::DeviceConnected),
                    base::Unretained(wrapped_), bdaddr));
@@ -240,20 +259,20 @@
 
     auto bound_cb = base::Bind(cb_lambda, cb);
 
-    do_in_jni_thread(base::Bind(static_cast<void (VolumeInterface::*)(
-                                    const RawAddress&, VolumeChangedCb)>(
-                                    &VolumeInterface::DeviceConnected),
-                                base::Unretained(wrapped_), bdaddr, bound_cb));
+    do_in_avrcp_jni(base::Bind(static_cast<void (VolumeInterface::*)(
+                                   const RawAddress&, VolumeChangedCb)>(
+                                   &VolumeInterface::DeviceConnected),
+                               base::Unretained(wrapped_), bdaddr, bound_cb));
   }
 
   void DeviceDisconnected(const RawAddress& bdaddr) override {
-    do_in_jni_thread(base::Bind(&VolumeInterface::DeviceDisconnected,
-                                base::Unretained(wrapped_), bdaddr));
+    do_in_avrcp_jni(base::Bind(&VolumeInterface::DeviceDisconnected,
+                               base::Unretained(wrapped_), bdaddr));
   }
 
   void SetVolume(int8_t volume) override {
-    do_in_jni_thread(base::Bind(&VolumeInterface::SetVolume,
-                                base::Unretained(wrapped_), volume));
+    do_in_avrcp_jni(base::Bind(&VolumeInterface::SetVolume,
+                               base::Unretained(wrapped_), volume));
   }
 
  private:
@@ -263,14 +282,11 @@
 void AvrcpService::Init(MediaInterface* media_interface,
                         VolumeInterface* volume_interface) {
   LOG(INFO) << "AVRCP Target Service started";
-  if (instance_ == nullptr) {
-    instance_ = new AvrcpService();
-  }
 
   // TODO (apanicke): Add a function that sets up the SDP records once we
   // remove the AVRCP SDP setup in AVDTP (bta_av_main.cc)
 
-  instance_->media_interface_ = new MediaInterfaceWrapper(media_interface);
+  media_interface_ = new MediaInterfaceWrapper(media_interface);
   media_interface->RegisterUpdateCallback(instance_);
 
   VolumeInterfaceWrapper* wrapped_volume_interface = nullptr;
@@ -278,23 +294,23 @@
     wrapped_volume_interface = new VolumeInterfaceWrapper(volume_interface);
   }
 
-  instance_->volume_interface_ = wrapped_volume_interface;
+  volume_interface_ = wrapped_volume_interface;
 
   ConnectionHandler::Initialize(
       base::Bind(&AvrcpService::DeviceCallback, base::Unretained(instance_)),
       &avrcp_interface_, &sdp_interface_, wrapped_volume_interface);
-  instance_->connection_handler_ = ConnectionHandler::Get();
+  connection_handler_ = ConnectionHandler::Get();
 }
 
 void AvrcpService::Cleanup() {
   LOG(INFO) << "AVRCP Target Service stopped";
 
-  instance_->connection_handler_->CleanUp();
-  instance_->connection_handler_ = nullptr;
-  if (instance_->volume_interface_ != nullptr) {
-    delete instance_->volume_interface_;
+  connection_handler_->CleanUp();
+  connection_handler_ = nullptr;
+  if (volume_interface_ != nullptr) {
+    delete volume_interface_;
   }
-  delete instance_->media_interface_;
+  delete media_interface_;
 }
 
 AvrcpService* AvrcpService::Get() {
@@ -310,15 +326,15 @@
   return service_interface_;
 }
 
-bool AvrcpService::ConnectDevice(const RawAddress& bdaddr) {
+void AvrcpService::ConnectDevice(const RawAddress& bdaddr) {
   LOG(INFO) << __PRETTY_FUNCTION__ << ": address=" << bdaddr.ToString();
 
-  return connection_handler_->ConnectDevice(bdaddr);
+  connection_handler_->ConnectDevice(bdaddr);
 }
 
-bool AvrcpService::DisconnectDevice(const RawAddress& bdaddr) {
+void AvrcpService::DisconnectDevice(const RawAddress& bdaddr) {
   LOG(INFO) << __PRETTY_FUNCTION__ << ": address=" << bdaddr.ToString();
-  return connection_handler_->DisconnectDevice(bdaddr);
+  connection_handler_->DisconnectDevice(bdaddr);
 }
 
 void AvrcpService::SendMediaUpdate(bool track_changed, bool play_state,
@@ -369,37 +385,62 @@
 
 // Service Interface
 void AvrcpService::ServiceInterfaceImpl::Init(
-    MediaInterface* mediaInterface, VolumeInterface* volume_interface) {
-  // TODO (apanicke): Run this in a message loop. This currently works though
-  // since the underlying AVRC_API will correctly run this in a message loop so
-  // no race conditions can occur, but if that changes it could change the
-  // behaviour here.
-  CHECK(instance_ == nullptr);
+    MediaInterface* media_interface, VolumeInterface* volume_interface) {
+  std::lock_guard<std::mutex> lock(service_interface_lock_);
 
-  AvrcpService::Init(mediaInterface, volume_interface);
+  // TODO: This function should block until the service is completely up so
+  // that its possible to call Get() on the service immediately after calling
+  // init without issues.
+
+  CHECK(instance_ == nullptr);
+  instance_ = new AvrcpService();
+
+  {
+    std::lock_guard<std::mutex> jni_lock(jni_mutex_);
+    jni_message_loop_ = get_jni_message_loop();
+  }
+
+  do_in_bta_thread(FROM_HERE,
+                   base::Bind(&AvrcpService::Init, base::Unretained(instance_),
+                              media_interface, volume_interface));
 }
 
 bool AvrcpService::ServiceInterfaceImpl::ConnectDevice(
     const RawAddress& bdaddr) {
-  // TODO (apanicke): Same as above
+  std::lock_guard<std::mutex> lock(service_interface_lock_);
   CHECK(instance_ != nullptr);
-  return instance_->ConnectDevice(bdaddr);
+  do_in_bta_thread(FROM_HERE, base::Bind(&AvrcpService::ConnectDevice,
+                                         base::Unretained(instance_), bdaddr));
+  return true;
 }
 
 bool AvrcpService::ServiceInterfaceImpl::DisconnectDevice(
     const RawAddress& bdaddr) {
-  // TODO (apanicke): Same as above
+  std::lock_guard<std::mutex> lock(service_interface_lock_);
   CHECK(instance_ != nullptr);
-  return instance_->DisconnectDevice(bdaddr);
+  do_in_bta_thread(FROM_HERE, base::Bind(&AvrcpService::DisconnectDevice,
+                                         base::Unretained(instance_), bdaddr));
+  return true;
 }
 
 bool AvrcpService::ServiceInterfaceImpl::Cleanup() {
-  // TODO (apanicke): Same as above
+  std::lock_guard<std::mutex> lock(service_interface_lock_);
+
   if (instance_ == nullptr) return false;
 
-  instance_->Cleanup();
-  delete instance_;
+  {
+    std::lock_guard<std::mutex> jni_lock(jni_mutex_);
+    task_tracker_.TryCancelAll();
+    jni_message_loop_ = nullptr;
+  }
+
+  do_in_bta_thread(FROM_HERE,
+                   base::Bind(&AvrcpService::Cleanup, base::Owned(instance_)));
+
+  // Setting instance to nullptr here is fine since it will be deleted on the
+  // other thread.
   instance_ = nullptr;
+
   return true;
 }
 
diff --git a/btif/avrcp/avrcp_service.h b/btif/avrcp/avrcp_service.h
index 5f8fa2d..e19899d 100644
--- a/btif/avrcp/avrcp_service.h
+++ b/btif/avrcp/avrcp_service.h
@@ -37,9 +37,6 @@
  */
 class AvrcpService : public MediaCallbacks {
  public:
-  static void Init(MediaInterface* media_interface,
-                   VolumeInterface* volume_interface);
-
   /**
    * Gets a handle to the AvrcpService
    *
@@ -56,10 +53,11 @@
    */
   static ServiceInterface* GetServiceInterface();
 
+  void Init(MediaInterface* media_interface, VolumeInterface* volume_interface);
   void Cleanup();
 
-  bool ConnectDevice(const RawAddress& bdaddr);
-  bool DisconnectDevice(const RawAddress& bdaddr);
+  void ConnectDevice(const RawAddress& bdaddr);
+  void DisconnectDevice(const RawAddress& bdaddr);
 
   // Functions inherited from MediaCallbacks in order to receive updates
   void SendMediaUpdate(bool track_changed, bool play_state,
@@ -75,6 +73,9 @@
     bool ConnectDevice(const RawAddress& bdaddr) override;
     bool DisconnectDevice(const RawAddress& bdaddr) override;
     bool Cleanup() override;
+
+   private:
+    std::mutex service_interface_lock_;
   };
 
   static void DebugDump(int fd);
diff --git a/btif/co/bta_dm_co.cc b/btif/co/bta_dm_co.cc
index ab69bfb..200ce55 100644
--- a/btif/co/bta_dm_co.cc
+++ b/btif/co/bta_dm_co.cc
@@ -137,7 +137,7 @@
  * Returns          void.
  *
  ******************************************************************************/
-void bta_dm_co_loc_oob(bool valid, BT_OCTET16 c, BT_OCTET16 r) {
+void bta_dm_co_loc_oob(bool valid, const Octet16& c, const Octet16& r) {
   BTIF_TRACE_DEBUG("bta_dm_co_loc_oob, valid = %d", valid);
 #ifdef BTIF_DM_OOB_TEST
   btif_dm_proc_loc_oob(valid, c, r);
@@ -158,16 +158,16 @@
  *
  ******************************************************************************/
 void bta_dm_co_rmt_oob(const RawAddress& bd_addr) {
-  BT_OCTET16 p_c;
-  BT_OCTET16 p_r;
+  Octet16 c;
+  Octet16 r;
   bool result = false;
 
 #ifdef BTIF_DM_OOB_TEST
-  result = btif_dm_proc_rmt_oob(bd_addr, p_c, p_r);
+  result = btif_dm_proc_rmt_oob(bd_addr, &c, &r);
 #endif
 
   BTIF_TRACE_DEBUG("bta_dm_co_rmt_oob: result=%d", result);
-  bta_dm_ci_rmt_oob(result, bd_addr, p_c, p_r);
+  bta_dm_ci_rmt_oob(result, bd_addr, c, r);
 }
 
 /*******************************************************************************
@@ -212,13 +212,13 @@
  *
  ******************************************************************************/
 void bta_dm_co_ble_load_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask,
-                                   BT_OCTET16 er,
+                                   Octet16* p_er,
                                    tBTA_BLE_LOCAL_ID_KEYS* p_id_keys) {
   BTIF_TRACE_DEBUG("##################################");
   BTIF_TRACE_DEBUG(
       "bta_dm_co_ble_load_local_keys:  Load local keys if any are persisted");
   BTIF_TRACE_DEBUG("##################################");
-  btif_dm_get_ble_local_keys(p_key_mask, er, p_id_keys);
+  btif_dm_get_ble_local_keys(p_key_mask, p_er, p_id_keys);
 }
 
 /*******************************************************************************
diff --git a/btif/include/btif_av.h b/btif/include/btif_av.h
index 53e1ed0..486c2af 100644
--- a/btif/include/btif_av.h
+++ b/btif/include/btif_av.h
@@ -48,8 +48,10 @@
 
 /**
  * Stop streaming.
+ *
+ * @param peer_address the peer address or RawAddress::kEmpty to stop all peers
  */
-void btif_av_stream_stop(void);
+void btif_av_stream_stop(const RawAddress& peer_address);
 
 /**
  * Suspend streaming.
diff --git a/btif/include/btif_common.h b/btif/include/btif_common.h
index 94fadee..af0beb0 100644
--- a/btif/include/btif_common.h
+++ b/btif/include/btif_common.h
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 
 #include <base/bind.h>
+#include <base/message_loop/message_loop.h>
 #include <base/tracked_objects.h>
 #include <hardware/bluetooth.h>
 
@@ -177,6 +178,7 @@
 extern bt_status_t do_in_jni_thread(const tracked_objects::Location& from_here,
                                     const base::Closure& task);
 extern bool is_on_jni_thread();
+extern base::MessageLoop* get_jni_message_loop();
 /**
  * This template wraps callback into callback that will be executed on jni
  * thread
diff --git a/btif/include/btif_dm.h b/btif/include/btif_dm.h
index 6105ab5..a9cb228 100644
--- a/btif/include/btif_dm.h
+++ b/btif/include/btif_dm.h
@@ -61,9 +61,9 @@
                                    tBTA_LE_AUTH_REQ* p_auth_req);
 #ifdef BTIF_DM_OOB_TEST
 void btif_dm_load_local_oob(void);
-void btif_dm_proc_loc_oob(bool valid, BT_OCTET16 c, BT_OCTET16 r);
-bool btif_dm_proc_rmt_oob(const RawAddress& bd_addr, BT_OCTET16 p_c,
-                          BT_OCTET16 p_r);
+void btif_dm_proc_loc_oob(bool valid, const Octet16& c, const Octet16& r);
+bool btif_dm_proc_rmt_oob(const RawAddress& bd_addr, Octet16* p_c,
+                          Octet16* p_r);
 #endif /* BTIF_DM_OOB_TEST */
 
 /*callout for reading SMP properties from Text file*/
@@ -98,7 +98,7 @@
 
 void btif_dm_load_ble_local_keys(void);
 void btif_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask,
-                                BT_OCTET16 er,
+                                Octet16* p_er,
                                 tBTA_BLE_LOCAL_ID_KEYS* p_id_keys);
 void btif_dm_save_ble_bonding_keys(void);
 void btif_dm_remove_ble_bonding_keys(void);
diff --git a/btif/include/btif_storage.h b/btif/include/btif_storage.h
index 79a94e3..30615ce 100644
--- a/btif/include/btif_storage.h
+++ b/btif/include/btif_storage.h
@@ -151,7 +151,7 @@
  *
  ******************************************************************************/
 bt_status_t btif_storage_add_bonded_device(RawAddress* remote_bd_addr,
-                                           LINK_KEY link_key, uint8_t key_type,
+                                           LinkKey link_key, uint8_t key_type,
                                            uint8_t pin_length);
 
 /*******************************************************************************
@@ -243,6 +243,8 @@
  ******************************************************************************/
 bool btif_storage_is_restricted_device(const RawAddress* remote_bd_addr);
 
+int btif_storage_get_num_bonded_devices(void);
+
 bt_status_t btif_storage_add_ble_bonding_key(RawAddress* remote_bd_addr,
                                              const uint8_t* key,
                                              uint8_t key_type,
@@ -252,13 +254,13 @@
                                              uint8_t* key_value,
                                              int key_length);
 
-bt_status_t btif_storage_add_ble_local_key(char* key, uint8_t key_type,
-                                           uint8_t key_length);
+bt_status_t btif_storage_add_ble_local_key(const Octet16& key,
+                                           uint8_t key_type);
 bt_status_t btif_storage_remove_ble_bonding_keys(
     const RawAddress* remote_bd_addr);
 bt_status_t btif_storage_remove_ble_local_keys(void);
-bt_status_t btif_storage_get_ble_local_key(uint8_t key_type, char* key_value,
-                                           int key_len);
+bt_status_t btif_storage_get_ble_local_key(uint8_t key_type,
+                                           Octet16* key_value);
 
 bt_status_t btif_storage_get_remote_addr_type(const RawAddress* remote_bd_addr,
                                               int* addr_type);
diff --git a/btif/src/bluetooth.cc b/btif/src/bluetooth.cc
index 94dc108..4fb8f64 100644
--- a/btif/src/bluetooth.cc
+++ b/btif/src/bluetooth.cc
@@ -326,8 +326,6 @@
 #if (BTSNOOP_MEM == TRUE)
   btif_debug_btsnoop_dump(fd);
 #endif
-
-  close(fd);
 }
 
 static void dumpMetrics(std::string* output) {
diff --git a/btif/src/btif_a2dp.cc b/btif/src/btif_a2dp.cc
index d7235eb..86485d4 100644
--- a/btif/src/btif_a2dp.cc
+++ b/btif/src/btif_a2dp.cc
@@ -36,8 +36,8 @@
 #include "osi/include/log.h"
 
 void btif_a2dp_on_idle(void) {
-  APPL_TRACE_WARNING("## ON A2DP IDLE ## peer_sep = %d",
-                     btif_av_get_peer_sep());
+  LOG_INFO(LOG_TAG, "%s: ## ON A2DP IDLE ## peer_sep = %d", __func__,
+           btif_av_get_peer_sep());
   if (btif_av_get_peer_sep() == AVDT_TSEP_SNK) {
     btif_a2dp_source_on_idle();
   } else if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) {
@@ -49,7 +49,10 @@
                           tBTA_AV_START* p_av_start, bool pending_start) {
   bool ack = false;
 
-  APPL_TRACE_WARNING("## ON A2DP STARTED ##");
+  LOG_INFO(LOG_TAG,
+           "%s: ## ON A2DP STARTED ## peer %s pending_start:%s p_av_start:%p",
+           __func__, peer_addr.ToString().c_str(),
+           logbool(pending_start).c_str(), p_av_start);
 
   if (p_av_start == NULL) {
     /* ack back a local start request */
@@ -60,16 +63,19 @@
     } else if (bluetooth::headset::IsCallIdle()) {
       btif_av_stream_start_offload();
     } else {
-      APPL_TRACE_ERROR("%s: call in progress, do not start offload", __func__);
+      LOG_ERROR(LOG_TAG, "%s: peer %s call in progress, do not start offload",
+                __func__, peer_addr.ToString().c_str());
       btif_a2dp_audio_on_started(A2DP_CTRL_ACK_INCALL_FAILURE);
     }
     return true;
   }
 
-  APPL_TRACE_WARNING(
-      "%s: pending_start = %d status = %d suspending = %d initiator = %d",
-      __func__, pending_start, p_av_start->status, p_av_start->suspending,
-      p_av_start->initiator);
+  LOG_INFO(LOG_TAG,
+           "%s: peer %s pending_start:%s status:%d suspending:%s initiator:%s",
+           __func__, peer_addr.ToString().c_str(),
+           logbool(pending_start).c_str(), p_av_start->status,
+           logbool(p_av_start->suspending).c_str(),
+           logbool(p_av_start->initiator).c_str());
 
   if (p_av_start->status == BTA_AV_SUCCESS) {
     if (!p_av_start->suspending) {
@@ -82,21 +88,31 @@
           }
           ack = true;
         }
+      } else {
+        // We were started remotely
+        if (btif_av_is_a2dp_offload_enabled()) {
+          btif_av_stream_start_offload();
+        }
       }
 
       /* media task is autostarted upon a2dp audiopath connection */
     }
   } else if (pending_start) {
-    APPL_TRACE_WARNING("%s: A2DP start request failed: status = %d", __func__,
-                       p_av_start->status);
-    btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+    LOG_ERROR(LOG_TAG, "%s: peer %s A2DP start request failed: status = %d",
+              __func__, peer_addr.ToString().c_str(), p_av_start->status);
+    if (btif_av_is_a2dp_offload_enabled()) {
+      btif_a2dp_audio_on_started(p_av_start->status);
+    } else {
+      btif_a2dp_command_ack(A2DP_CTRL_ACK_FAILURE);
+    }
     ack = true;
   }
   return ack;
 }
 
 void btif_a2dp_on_stopped(tBTA_AV_SUSPEND* p_av_suspend) {
-  APPL_TRACE_WARNING("## ON A2DP STOPPED ##");
+  LOG_INFO(LOG_TAG, "%s: ## ON A2DP STOPPED ## p_av_suspend=%p", __func__,
+           p_av_suspend);
 
   if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) {
     btif_a2dp_sink_on_stopped(p_av_suspend);
@@ -110,7 +126,8 @@
 }
 
 void btif_a2dp_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
-  APPL_TRACE_EVENT("## ON A2DP SUSPENDED ##");
+  LOG_INFO(LOG_TAG, "%s: ## ON A2DP SUSPENDED ## p_av_suspend=%p", __func__,
+           p_av_suspend);
   if (!btif_av_is_a2dp_offload_enabled()) {
     if (btif_av_get_peer_sep() == AVDT_TSEP_SRC) {
       btif_a2dp_sink_on_suspended(p_av_suspend);
@@ -125,18 +142,21 @@
 void btif_a2dp_on_offload_started(const RawAddress& peer_addr,
                                   tBTA_AV_STATUS status) {
   tA2DP_CTRL_ACK ack;
-  APPL_TRACE_EVENT("%s status %d", __func__, status);
+  LOG_INFO(LOG_TAG, "%s: peer %s status %d", __func__,
+           peer_addr.ToString().c_str(), status);
 
   switch (status) {
     case BTA_AV_SUCCESS:
       ack = A2DP_CTRL_ACK_SUCCESS;
       break;
     case BTA_AV_FAIL_RESOURCES:
-      APPL_TRACE_ERROR("%s FAILED UNSUPPORTED", __func__);
+      LOG_ERROR(LOG_TAG, "%s: peer %s FAILED UNSUPPORTED", __func__,
+                peer_addr.ToString().c_str());
       ack = A2DP_CTRL_ACK_UNSUPPORTED;
       break;
     default:
-      APPL_TRACE_ERROR("%s FAILED: status = %d", __func__, status);
+      LOG_ERROR(LOG_TAG, "%s: peer %s FAILED: status = %d", __func__,
+                peer_addr.ToString().c_str(), status);
       ack = A2DP_CTRL_ACK_FAILURE;
       break;
   }
@@ -146,7 +166,8 @@
       // Offload request will return with failure from btif_av sm if
       // suspend is triggered for remote start. Disconnect only if SoC
       // returned failure for offload VSC
-      APPL_TRACE_ERROR("%s: offload start failed", __func__);
+      LOG_ERROR(LOG_TAG, "%s: peer %s offload start failed", __func__,
+                peer_addr.ToString().c_str());
       btif_av_src_disconnect_sink(peer_addr);
     }
   } else {
diff --git a/btif/src/btif_a2dp_audio_interface.cc b/btif/src/btif_a2dp_audio_interface.cc
index 3608acd..944318e 100644
--- a/btif/src/btif_a2dp_audio_interface.cc
+++ b/btif/src/btif_a2dp_audio_interface.cc
@@ -19,6 +19,9 @@
 #define LOG_TAG "btif_a2dp_audio_interface"
 
 #include "btif_a2dp_audio_interface.h"
+
+#include <mutex>
+
 #include <a2dp_vendor.h>
 #include <a2dp_vendor_ldac_constants.h>
 #include <android/hardware/bluetooth/a2dp/1.0/IBluetoothAudioHost.h>
@@ -37,8 +40,12 @@
 #include "btif_av.h"
 #include "btif_av_co.h"
 #include "btif_hf.h"
+#include "osi/include/metrics.h"
 #include "osi/include/osi.h"
 
+using system_bt_osi::A2dpSessionMetrics;
+using system_bt_osi::BluetoothMetricsLogger;
+
 using android::hardware::bluetooth::a2dp::V1_0::IBluetoothAudioOffload;
 using android::hardware::bluetooth::a2dp::V1_0::IBluetoothAudioHost;
 using android::hardware::bluetooth::a2dp::V1_0::Status;
@@ -50,8 +57,10 @@
 using android::hardware::ProcessState;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+using ::android::hardware::hidl_death_recipient;
 using ::android::hardware::hidl_vec;
 using ::android::sp;
+using ::android::wp;
 android::sp<IBluetoothAudioOffload> btAudio;
 
 #define CASE_RETURN_STR(const) \
@@ -63,11 +72,67 @@
 
 static void btif_a2dp_audio_send_start_req();
 static void btif_a2dp_audio_send_suspend_req();
+static void btif_a2dp_audio_send_stop_req();
 static void btif_a2dp_audio_interface_init();
 static void btif_a2dp_audio_interface_deinit();
+static void btif_a2dp_audio_interface_restart_session();
 // Delay reporting
 // static void btif_a2dp_audio_send_sink_latency();
 
+class A2dpOffloadAudioStats {
+ public:
+  A2dpOffloadAudioStats() { Reset(); }
+  void Reset() {
+    std::lock_guard<std::recursive_mutex> lock(lock_);
+    ResetPreserveSession();
+    codec_index_ = -1;
+  }
+  void ResetPreserveSession() {
+    std::lock_guard<std::recursive_mutex> lock(lock_);
+    audio_start_time_ms_ = -1;
+    audio_stop_time_ms_ = -1;
+  }
+  void StoreMetrics() {
+    std::lock_guard<std::recursive_mutex> lock(lock_);
+    if (audio_start_time_ms_ < 0 || audio_stop_time_ms_ < 0) {
+      return;
+    }
+    A2dpSessionMetrics metrics;
+    metrics.codec_index = codec_index_;
+    metrics.is_a2dp_offload = true;
+    if (audio_stop_time_ms_ > audio_start_time_ms_) {
+      metrics.audio_duration_ms = audio_stop_time_ms_ - audio_start_time_ms_;
+    }
+    BluetoothMetricsLogger::GetInstance()->LogA2dpSession(metrics);
+  }
+  void LogAudioStart() {
+    std::lock_guard<std::recursive_mutex> lock(lock_);
+    audio_start_time_ms_ = time_get_os_boottime_ms();
+  }
+  void LogAudioStop() {
+    std::lock_guard<std::recursive_mutex> lock(lock_);
+    audio_stop_time_ms_ = time_get_os_boottime_ms();
+  }
+  void LogAudioStopMetricsAndReset() {
+    std::lock_guard<std::recursive_mutex> lock(lock_);
+    LogAudioStop();
+    StoreMetrics();
+    ResetPreserveSession();
+  }
+  void SetCodecIndex(int64_t codec_index) {
+    std::lock_guard<std::recursive_mutex> lock(lock_);
+    codec_index_ = codec_index;
+  }
+
+ private:
+  std::recursive_mutex lock_;
+  int64_t audio_start_time_ms_ = -1;
+  int64_t audio_stop_time_ms_ = -1;
+  int64_t codec_index_ = -1;
+};
+
+static A2dpOffloadAudioStats a2dp_offload_audio_stats;
+
 class BluetoothAudioHost : public IBluetoothAudioHost {
  public:
   Return<void> startStream() {
@@ -79,7 +144,7 @@
     return Void();
   }
   Return<void> stopStream() {
-    btif_a2dp_audio_process_request(A2DP_CTRL_CMD_STOP);
+    btif_a2dp_audio_send_stop_req();
     return Void();
   }
 
@@ -91,6 +156,20 @@
       }*/
 };
 
+class BluetoothAudioDeathRecipient : public hidl_death_recipient {
+ public:
+  virtual void serviceDied(
+      uint64_t /*cookie*/,
+      const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
+    LOG_ERROR(LOG_TAG, "%s", __func__);
+    // Restart the session on the correct thread
+    do_in_bta_thread(FROM_HERE,
+                     base::Bind(&btif_a2dp_audio_interface_restart_session));
+  }
+};
+sp<BluetoothAudioDeathRecipient> bluetoothAudioDeathRecipient =
+    new BluetoothAudioDeathRecipient();
+
 static Status mapToStatus(uint8_t resp) {
   switch (resp) {
     case A2DP_CTRL_ACK_SUCCESS:
@@ -118,6 +197,7 @@
   a2dpCodecConfig->getCodecSpecificConfig(&a2dp_offload);
   btav_a2dp_codec_config_t codec_config;
   codec_config = a2dpCodecConfig->getCodecConfig();
+  a2dp_offload_audio_stats.SetCodecIndex(a2dpCodecConfig->codecIndex());
   switch (codec_config.codec_type) {
     case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
       p_codec_info->codecType =
@@ -190,6 +270,12 @@
   btAudio = IBluetoothAudioOffload::getService();
   CHECK(btAudio != nullptr);
 
+  auto death_link = btAudio->linkToDeath(bluetoothAudioDeathRecipient, 0);
+  if (!death_link.isOk()) {
+    LOG_ERROR(LOG_TAG, "%s: Cannot observe the Bluetooth Audio HAL's death",
+              __func__);
+  }
+
   LOG_DEBUG(
       LOG_TAG, "%s: IBluetoothAudioOffload::getService() returned %p (%s)",
       __func__, btAudio.get(), (btAudio->isRemote() ? "remote" : "local"));
@@ -199,11 +285,22 @@
 
 static void btif_a2dp_audio_interface_deinit() {
   LOG_INFO(LOG_TAG, "%s: start", __func__);
+  if (btAudio != nullptr) {
+    auto death_unlink = btAudio->unlinkToDeath(bluetoothAudioDeathRecipient);
+    if (!death_unlink.isOk()) {
+      LOG_ERROR(LOG_TAG,
+                "%s: Error unlinking death observer from Bluetooth Audio HAL",
+                __func__);
+    }
+  }
   btAudio = nullptr;
 }
 
 void btif_a2dp_audio_interface_start_session() {
   LOG_INFO(LOG_TAG, "%s", __func__);
+  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
+      system_bt_osi::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
+  a2dp_offload_audio_stats.Reset();
   btif_a2dp_audio_interface_init();
   CHECK(btAudio != nullptr);
   CodecConfiguration codec_info;
@@ -214,6 +311,10 @@
 
 void btif_a2dp_audio_interface_end_session() {
   LOG_INFO(LOG_TAG, "%s", __func__);
+  a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
+  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
+      system_bt_osi::DISCONNECT_REASON_UNKNOWN, 0);
+  a2dp_offload_audio_stats.Reset();
   if (btAudio == nullptr) return;
   auto ret = btAudio->endSession();
   if (!ret.isOk()) {
@@ -222,12 +323,31 @@
   btif_a2dp_audio_interface_deinit();
 }
 
+// Conditionally restart the session only if it was started before
+static void btif_a2dp_audio_interface_restart_session() {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  if (btAudio == nullptr) {
+    LOG_INFO(LOG_TAG, "%s: nothing to restart - session was not started",
+             __func__);
+    return;
+  }
+  btAudio = nullptr;
+  btif_a2dp_audio_interface_start_session();
+}
+
 void btif_a2dp_audio_on_started(tBTA_AV_STATUS status) {
   LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status);
   if (btAudio != nullptr) {
     if (a2dp_cmd_pending == A2DP_CTRL_CMD_START) {
+      if (status != A2DP_CTRL_ACK_PENDING) {
+        a2dp_cmd_pending = A2DP_CTRL_CMD_NONE;
+      }
       LOG_INFO(LOG_TAG, "%s: calling method onStarted", __func__);
-      btAudio->streamStarted(mapToStatus(status));
+      auto hal_status = mapToStatus(status);
+      btAudio->streamStarted(hal_status);
+      if (hal_status == Status::SUCCESS) {
+        a2dp_offload_audio_stats.LogAudioStart();
+      }
     }
   }
 }
@@ -236,8 +356,15 @@
   LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status);
   if (btAudio != nullptr) {
     if (a2dp_cmd_pending == A2DP_CTRL_CMD_SUSPEND) {
+      if (status != A2DP_CTRL_ACK_PENDING) {
+        a2dp_cmd_pending = A2DP_CTRL_CMD_NONE;
+      }
       LOG_INFO(LOG_TAG, "calling method onSuspended");
-      btAudio->streamSuspended(mapToStatus(status));
+      auto hal_status = mapToStatus(status);
+      btAudio->streamSuspended(hal_status);
+      if (hal_status == Status::SUCCESS) {
+        a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
+      }
     }
   }
 }
@@ -245,9 +372,11 @@
 void btif_a2dp_audio_on_stopped(tBTA_AV_STATUS status) {
   LOG_INFO(LOG_TAG, "%s: status = %d", __func__, status);
   if (btAudio != nullptr && a2dp_cmd_pending == A2DP_CTRL_CMD_START) {
+    a2dp_cmd_pending = A2DP_CTRL_CMD_NONE;
     LOG_INFO(LOG_TAG, "%s: Remote disconnected when start under progress",
              __func__);
     btAudio->streamStarted(mapToStatus(A2DP_CTRL_ACK_DISCONNECT_IN_PROGRESS));
+    a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
   }
 }
 void btif_a2dp_audio_send_start_req() {
@@ -255,7 +384,11 @@
   uint8_t resp;
   resp = btif_a2dp_audio_process_request(A2DP_CTRL_CMD_START);
   if (btAudio != nullptr) {
-    auto ret = btAudio->streamStarted(mapToStatus(resp));
+    auto status = mapToStatus(resp);
+    auto ret = btAudio->streamStarted(status);
+    if (status == Status::SUCCESS) {
+      a2dp_offload_audio_stats.LogAudioStart();
+    }
     if (!ret.isOk()) LOG_ERROR(LOG_TAG, "HAL server died");
   }
 }
@@ -264,10 +397,21 @@
   uint8_t resp;
   resp = btif_a2dp_audio_process_request(A2DP_CTRL_CMD_SUSPEND);
   if (btAudio != nullptr) {
-    auto ret = btAudio->streamSuspended(mapToStatus(resp));
+    auto status = mapToStatus(resp);
+    auto ret = btAudio->streamSuspended(status);
+    if (status == Status::SUCCESS) {
+      a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
+    }
     if (!ret.isOk()) LOG_ERROR(LOG_TAG, "HAL server died");
   }
 }
+
+void btif_a2dp_audio_send_stop_req() {
+  LOG_INFO(LOG_TAG, "%s", __func__);
+  btif_a2dp_audio_process_request(A2DP_CTRL_CMD_STOP);
+  a2dp_offload_audio_stats.LogAudioStopMetricsAndReset();
+}
+
 /*void btif_a2dp_audio_send_sink_latency()
 {
   LOG_INFO(LOG_TAG, "%s", __func__);
@@ -281,7 +425,6 @@
 uint8_t btif_a2dp_audio_process_request(uint8_t cmd) {
   LOG_INFO(LOG_TAG, "%s: cmd: %s", __func__,
            audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd));
-  a2dp_cmd_pending = cmd;
   uint8_t status;
   switch (cmd) {
     case A2DP_CTRL_CMD_START:
@@ -324,7 +467,7 @@
       APPL_TRACE_WARNING("%s: A2DP command %s while AV stream is not ready",
                          __func__,
                          audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd));
-      return A2DP_CTRL_ACK_FAILURE;
+      status = A2DP_CTRL_ACK_FAILURE;
       break;
 
     case A2DP_CTRL_CMD_STOP:
@@ -334,8 +477,8 @@
         status = A2DP_CTRL_ACK_SUCCESS;
         break;
       }
-      btif_av_stream_stop();
-      return A2DP_CTRL_ACK_SUCCESS;
+      btif_av_stream_stop(RawAddress::kEmpty);
+      status = A2DP_CTRL_ACK_SUCCESS;
       break;
 
     case A2DP_CTRL_CMD_SUSPEND:
@@ -365,5 +508,10 @@
   }
   LOG_INFO(LOG_TAG, "a2dp-ctrl-cmd : %s DONE returning status %d",
            audio_a2dp_hw_dump_ctrl_event((tA2DP_CTRL_CMD)cmd), status);
+  if (status == A2DP_CTRL_ACK_PENDING) {
+    a2dp_cmd_pending = cmd;
+  } else {
+    a2dp_cmd_pending = A2DP_CTRL_CMD_NONE;
+  }
   return status;
 }
diff --git a/btif/src/btif_a2dp_audio_interface_linux.cc b/btif/src/btif_a2dp_audio_interface_linux.cc
new file mode 100644
index 0000000..b5a0e19
--- /dev/null
+++ b/btif/src/btif_a2dp_audio_interface_linux.cc
@@ -0,0 +1,37 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2018 Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include "btif_a2dp_audio_interface.h"
+
+#include <base/logging.h>
+
+void btif_a2dp_audio_on_started(tBTA_AV_STATUS status) {
+  LOG(FATAL) << "Unimplemented yet";
+}
+void btif_a2dp_audio_on_stopped(tBTA_AV_STATUS status) {
+  LOG(FATAL) << "Unimplemented yet";
+}
+void btif_a2dp_audio_on_suspended(tBTA_AV_STATUS status) {
+  LOG(FATAL) << "Unimplemented yet";
+}
+void btif_a2dp_audio_interface_start_session(void) {
+  LOG(FATAL) << "Unimplemented yet";
+}
+void btif_a2dp_audio_interface_end_session(void) {
+  LOG(FATAL) << "Unimplemented yet";
+}
diff --git a/btif/src/btif_a2dp_control.cc b/btif/src/btif_a2dp_control.cc
index bae6185..fb62480 100644
--- a/btif/src/btif_a2dp_control.cc
+++ b/btif/src/btif_a2dp_control.cc
@@ -163,7 +163,7 @@
         btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
         break;
       }
-      btif_av_stream_stop();
+      btif_av_stream_stop(RawAddress::kEmpty);
       btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
       break;
 
@@ -385,7 +385,7 @@
        */
       if (btif_a2dp_source_is_streaming()) {
         /* Post stop event and wait for audio path to stop */
-        btif_av_stream_stop();
+        btif_av_stream_stop(RawAddress::kEmpty);
       }
       break;
 
diff --git a/btif/src/btif_a2dp_source.cc b/btif/src/btif_a2dp_source.cc
index 9d8fe34..efd842b 100644
--- a/btif/src/btif_a2dp_source.cc
+++ b/btif/src/btif_a2dp_source.cc
@@ -132,6 +132,7 @@
     media_read_total_underflow_bytes = 0;
     media_read_total_underflow_count = 0;
     media_read_last_underflow_us = 0;
+    codec_index = -1;
   }
 
   uint64_t session_start_us;
@@ -160,6 +161,8 @@
   size_t media_read_total_underflow_bytes;
   size_t media_read_total_underflow_count;
   uint64_t media_read_last_underflow_us;
+
+  int codec_index = -1;
 };
 
 class BtWorkerThread {
@@ -282,6 +285,19 @@
   }
 
   BtifA2dpSource::RunState State() const { return state_; }
+  std::string StateStr() const {
+    switch (state_) {
+      case kStateOff:
+        return "STATE_OFF";
+      case kStateStartingUp:
+        return "STATE_STARTING_UP";
+      case kStateRunning:
+        return "STATE_RUNNING";
+      case kStateShuttingDown:
+        return "STATE_SHUTTING_DOWN";
+    }
+  }
+
   void SetState(BtifA2dpSource::RunState state) { state_ = state; }
 
   fixed_queue_t* tx_audio_queue;
@@ -380,6 +396,7 @@
   dst->media_read_total_underflow_count +=
       src->media_read_total_underflow_count;
   dst->media_read_last_underflow_us = src->media_read_last_underflow_us;
+  if (dst->codec_index < 0) dst->codec_index = src->codec_index;
   btif_a2dp_source_accumulate_scheduling_stats(&src->tx_queue_enqueue_stats,
                                                &dst->tx_queue_enqueue_stats);
   btif_a2dp_source_accumulate_scheduling_stats(&src->tx_queue_dequeue_stats,
@@ -403,7 +420,8 @@
 }
 
 bool btif_a2dp_source_startup(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   if (btif_a2dp_source_cb.State() != BtifA2dpSource::kStateOff) {
     LOG_ERROR(LOG_TAG, "%s: A2DP Source media task already running", __func__);
@@ -422,18 +440,18 @@
 }
 
 static void btif_a2dp_source_startup_delayed(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   raise_priority_a2dp(TASK_HIGH_MEDIA);
   btif_a2dp_control_init();
   btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateRunning);
-  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
-      system_bt_osi::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
 }
 
 bool btif_a2dp_source_start_session(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_setup_codec(peer_address);
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE,
@@ -443,14 +461,18 @@
 
 static void btif_a2dp_source_start_session_delayed(
     const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
   if (btif_a2dp_source_cb.State() != BtifA2dpSource::kStateRunning) {
     LOG_ERROR(LOG_TAG, "%s: A2DP Source media task is not running", __func__);
     return;
   }
   if (btif_av_is_a2dp_offload_enabled()) {
     btif_a2dp_audio_interface_start_session();
+  } else {
+    BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
+        system_bt_osi::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
   }
 }
 
@@ -458,9 +480,11 @@
                                       const RawAddress& new_peer_address) {
   bool is_streaming = alarm_is_scheduled(btif_a2dp_source_cb.media_alarm);
   LOG_INFO(LOG_TAG,
-           "%s: old_peer_address=%s new_peer_address=%s is_streaming=%s",
+           "%s: old_peer_address=%s new_peer_address=%s is_streaming=%s "
+           "state=%s",
            __func__, old_peer_address.ToString().c_str(),
-           new_peer_address.ToString().c_str(), logbool(is_streaming).c_str());
+           new_peer_address.ToString().c_str(), logbool(is_streaming).c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
 
   CHECK(!new_peer_address.IsEmpty());
 
@@ -487,8 +511,9 @@
 }
 
 bool btif_a2dp_source_end_session(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE,
       base::Bind(&btif_a2dp_source_end_session_delayed, peer_address));
@@ -497,20 +522,26 @@
 
 static void btif_a2dp_source_end_session_delayed(
     const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
-  if (btif_a2dp_source_cb.State() != BtifA2dpSource::kStateRunning) {
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
+  if (!btif_av_is_a2dp_offload_enabled()) {
+    BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
+        system_bt_osi::DISCONNECT_REASON_UNKNOWN, 0);
+  }
+  if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateRunning) {
+    btif_av_stream_stop(peer_address);
+  } else {
     LOG_ERROR(LOG_TAG, "%s: A2DP Source media task is not running", __func__);
-    return;
   }
   if (btif_av_is_a2dp_offload_enabled()) {
-    btif_av_stream_stop();
     btif_a2dp_audio_interface_end_session();
   }
 }
 
 void btif_a2dp_source_shutdown(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   if ((btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) ||
       (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateShuttingDown)) {
@@ -525,7 +556,8 @@
 }
 
 static void btif_a2dp_source_shutdown_delayed(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   // Stop the timer
   alarm_free(btif_a2dp_source_cb.media_alarm);
@@ -538,12 +570,11 @@
   btif_a2dp_source_cb.tx_audio_queue = nullptr;
 
   btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateOff);
-  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
-      system_bt_osi::DISCONNECT_REASON_UNKNOWN, 0);
 }
 
 void btif_a2dp_source_cleanup(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   // Make sure the source is shutdown
   btif_a2dp_source_shutdown();
@@ -556,7 +587,8 @@
 }
 
 static void btif_a2dp_source_cleanup_delayed(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
   // Nothing to do
 }
 
@@ -573,8 +605,9 @@
 }
 
 static void btif_a2dp_source_setup_codec(const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
 
   // Check to make sure the platform has 8 bits/byte since
   // we're using that in frame size calculations now.
@@ -588,8 +621,9 @@
 
 static void btif_a2dp_source_setup_codec_delayed(
     const RawAddress& peer_address) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
 
   tA2DP_ENCODER_INIT_PEER_PARAMS peer_params;
   bta_av_co_get_peer_params(peer_address, &peer_params);
@@ -623,37 +657,27 @@
 }
 
 void btif_a2dp_source_start_audio_req(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_start_event));
-  btif_a2dp_source_cb.stats.Reset();
-  // Assign session_start_us to 1 when time_get_os_boottime_us() is 0 to
-  // indicate btif_a2dp_source_start_audio_req() has been called
-  btif_a2dp_source_cb.stats.session_start_us = time_get_os_boottime_us();
-  if (btif_a2dp_source_cb.stats.session_start_us == 0) {
-    btif_a2dp_source_cb.stats.session_start_us = 1;
-  }
-  btif_a2dp_source_cb.stats.session_end_us = 0;
 }
 
 void btif_a2dp_source_stop_audio_req(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_stop_event));
-
-  btif_a2dp_source_cb.stats.session_end_us = time_get_os_boottime_us();
-  btif_a2dp_source_update_metrics();
-  btif_a2dp_source_accumulate_stats(&btif_a2dp_source_cb.stats,
-                                    &btif_a2dp_source_cb.accumulated_stats);
 }
 
 void btif_a2dp_source_encoder_user_config_update_req(
     const RawAddress& peer_address,
     const btav_a2dp_codec_config_t& codec_user_config) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_encoder_user_config_update_event,
                             peer_address, codec_user_config));
@@ -662,8 +686,9 @@
 static void btif_a2dp_source_encoder_user_config_update_event(
     const RawAddress& peer_address,
     const btav_a2dp_codec_config_t& codec_user_config) {
-  LOG_INFO(LOG_TAG, "%s: peer_address=%s", __func__,
-           peer_address.ToString().c_str());
+  LOG_INFO(LOG_TAG, "%s: peer_address=%s state=%s", __func__,
+           peer_address.ToString().c_str(),
+           btif_a2dp_source_cb.StateStr().c_str());
   if (!bta_av_co_set_codec_user_config(peer_address, codec_user_config)) {
     LOG_ERROR(LOG_TAG, "%s: cannot update codec user configuration", __func__);
   }
@@ -671,7 +696,8 @@
 
 void btif_a2dp_source_feeding_update_req(
     const btav_a2dp_codec_config_t& codec_audio_config) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_feeding_update_event,
                             codec_audio_config));
@@ -679,7 +705,8 @@
 
 static void btif_a2dp_source_audio_feeding_update_event(
     const btav_a2dp_codec_config_t& codec_audio_config) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
   if (!bta_av_co_set_codec_audio_config(codec_audio_config)) {
     LOG_ERROR(LOG_TAG, "%s: cannot update codec audio feeding parameters",
               __func__);
@@ -687,7 +714,8 @@
 }
 
 void btif_a2dp_source_on_idle(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
   if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
   /* Make sure media task is stopped */
@@ -695,7 +723,8 @@
 }
 
 void btif_a2dp_source_on_stopped(tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
@@ -724,7 +753,8 @@
 }
 
 void btif_a2dp_source_on_suspended(tBTA_AV_SUSPEND* p_av_suspend) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   if (btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) return;
 
@@ -748,14 +778,17 @@
 
 /* when true media task discards any tx frames */
 void btif_a2dp_source_set_tx_flush(bool enable) {
-  LOG_INFO(LOG_TAG, "%s: enable=%s", __func__, (enable) ? "true" : "false");
+  LOG_INFO(LOG_TAG, "%s: enable=%s state=%s", __func__,
+           (enable) ? "true" : "false", btif_a2dp_source_cb.StateStr().c_str());
   btif_a2dp_source_cb.tx_flush = enable;
 }
 
 static void btif_a2dp_source_audio_tx_start_event(void) {
-  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s", __func__,
+  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s state=%s",
+           __func__,
            alarm_is_scheduled(btif_a2dp_source_cb.media_alarm) ? "" : "not ",
-           btif_a2dp_source_is_streaming() ? "true" : "false");
+           btif_a2dp_source_is_streaming() ? "true" : "false",
+           btif_a2dp_source_cb.StateStr().c_str());
 
   if (btif_av_is_a2dp_offload_enabled()) return;
 
@@ -777,16 +810,34 @@
   alarm_set(btif_a2dp_source_cb.media_alarm,
             btif_a2dp_source_cb.encoder_interface->get_encoder_interval_ms(),
             btif_a2dp_source_alarm_cb, nullptr);
+
+  btif_a2dp_source_cb.stats.Reset();
+  // Assign session_start_us to 1 when time_get_os_boottime_us() is 0 to
+  // indicate btif_a2dp_source_start_audio_req() has been called
+  btif_a2dp_source_cb.stats.session_start_us = time_get_os_boottime_us();
+  if (btif_a2dp_source_cb.stats.session_start_us == 0) {
+    btif_a2dp_source_cb.stats.session_start_us = 1;
+  }
+  btif_a2dp_source_cb.stats.session_end_us = 0;
+  A2dpCodecConfig* codec_config = bta_av_get_a2dp_current_codec();
+  if (codec_config != nullptr) {
+    btif_a2dp_source_cb.stats.codec_index = codec_config->codecIndex();
+  }
 }
 
 static void btif_a2dp_source_audio_tx_stop_event(void) {
-  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s", __func__,
+  LOG_INFO(LOG_TAG, "%s: media_alarm is %srunning, streaming %s state=%s",
+           __func__,
            alarm_is_scheduled(btif_a2dp_source_cb.media_alarm) ? "" : "not ",
-           btif_a2dp_source_is_streaming() ? "true" : "false");
+           btif_a2dp_source_is_streaming() ? "true" : "false",
+           btif_a2dp_source_cb.StateStr().c_str());
 
   if (btif_av_is_a2dp_offload_enabled()) return;
 
-  const bool send_ack = btif_a2dp_source_is_streaming();
+  btif_a2dp_source_cb.stats.session_end_us = time_get_os_boottime_us();
+  btif_a2dp_source_update_metrics();
+  btif_a2dp_source_accumulate_stats(&btif_a2dp_source_cb.stats,
+                                    &btif_a2dp_source_cb.accumulated_stats);
 
   uint8_t p_buf[AUDIO_STREAM_OUTPUT_BUFFER_SZ * 2];
   uint16_t event;
@@ -814,7 +865,7 @@
    * to get the ACK for any pending command in such cases.
    */
 
-  if (send_ack) btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
+  btif_a2dp_command_ack(A2DP_CTRL_ACK_SUCCESS);
 
   /* audio engine stopped, reset tx suspended flag */
   btif_a2dp_source_cb.tx_flush = false;
@@ -960,7 +1011,8 @@
 
 static void btif_a2dp_source_audio_tx_flush_event(void) {
   /* Flush all enqueued audio buffers (encoded) */
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
   if (btif_av_is_a2dp_offload_enabled()) return;
 
   if (btif_a2dp_source_cb.encoder_interface != nullptr)
@@ -976,7 +1028,8 @@
 }
 
 static bool btif_a2dp_source_audio_tx_flush_req(void) {
-  LOG_INFO(LOG_TAG, "%s", __func__);
+  LOG_INFO(LOG_TAG, "%s: state=%s", __func__,
+           btif_a2dp_source_cb.StateStr().c_str());
 
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_audio_tx_flush_event));
@@ -1214,17 +1267,21 @@
 }
 
 static void btif_a2dp_source_update_metrics(void) {
-  const BtifMediaStats& stats = btif_a2dp_source_cb.stats;
-  const SchedulingStats& enqueue_stats = stats.tx_queue_enqueue_stats;
+  BtifMediaStats stats = btif_a2dp_source_cb.stats;
+  SchedulingStats enqueue_stats = stats.tx_queue_enqueue_stats;
   A2dpSessionMetrics metrics;
+  metrics.codec_index = stats.codec_index;
+  metrics.is_a2dp_offload = btif_av_is_a2dp_offload_enabled();
   // session_start_us is 0 when btif_a2dp_source_start_audio_req() is not called
   // mark the metric duration as invalid (-1) in this case
   if (stats.session_start_us != 0) {
     int64_t session_end_us = stats.session_end_us == 0
                                  ? time_get_os_boottime_us()
                                  : stats.session_end_us;
-    metrics.audio_duration_ms =
-        (session_end_us - stats.session_start_us) / 1000;
+    if (static_cast<uint64_t>(session_end_us) > stats.session_start_us) {
+      metrics.audio_duration_ms =
+          (session_end_us - stats.session_start_us) / 1000;
+    }
   }
 
   if (enqueue_stats.total_updates > 1) {
diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc
index db9c53a..8d416c1 100644
--- a/btif/src/btif_av.cc
+++ b/btif/src/btif_av.cc
@@ -404,7 +404,6 @@
         BTIF_TRACE_WARNING("%s: unable to set active peer to empty in BtaAvCo",
                            __func__);
       }
-      btif_av_stream_stop();
       btif_a2dp_source_end_session(active_peer_);
       btif_a2dp_source_shutdown();
       active_peer_ = peer_address;
@@ -1685,7 +1684,8 @@
   BTIF_TRACE_DEBUG("%s: Peer %s", __PRETTY_FUNCTION__,
                    peer_.PeerAddress().ToString().c_str());
 
-  peer_.ClearFlags(BtifAvPeer::kFlagPendingStart |
+  peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending |
+                   BtifAvPeer::kFlagPendingStart |
                    BtifAvPeer::kFlagPendingStop);
 
   // Set the active peer if the first connected device.
@@ -1756,12 +1756,13 @@
 
       // If remote tries to start A2DP when DUT is A2DP Source, then Suspend.
       // If A2DP is Sink and call is active, then disconnect the AVDTP channel.
-      if (peer_.IsSink() && !peer_.CheckFlags(BtifAvPeer::kFlagPendingStart)) {
+      bool should_suspend = false;
+      if (peer_.IsSink() && !peer_.CheckFlags(BtifAvPeer::kFlagPendingStart |
+                                              BtifAvPeer::kFlagRemoteSuspend)) {
         BTIF_TRACE_WARNING("%s: Peer %s : trigger Suspend as remote initiated",
                            __PRETTY_FUNCTION__,
                            peer_.PeerAddress().ToString().c_str());
-        btif_av_source_dispatch_sm_event(peer_.PeerAddress(),
-                                         BTIF_AV_SUSPEND_STREAM_REQ_EVT);
+        should_suspend = true;
       }
 
       // If peer is A2DP Source, we do not want to ACK commands on UIPC
@@ -1786,6 +1787,11 @@
         btif_a2dp_on_started(peer_.PeerAddress(), nullptr, true);
         // Pending start flag will be cleared when exit current state
       }
+
+      if (should_suspend) {
+        btif_av_source_dispatch_sm_event(peer_.PeerAddress(),
+                                         BTIF_AV_SUSPEND_STREAM_REQ_EVT);
+      }
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateStarted);
 
     } break;
@@ -2018,6 +2024,8 @@
                peer_.FlagsToString().c_str());
 
       peer_.SetFlags(BtifAvPeer::kFlagPendingStop);
+      peer_.ClearFlags(BtifAvPeer::kFlagLocalSuspendPending);
+
       btif_a2dp_on_stopped(&p_av->suspend);
 
       btif_report_audio_state(peer_.PeerAddress(), BTAV_AUDIO_STATE_STOPPED);
@@ -2729,11 +2737,19 @@
 bool btif_av_is_sink_enabled(void) { return btif_av_sink.Enabled(); }
 
 void btif_av_stream_start(void) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
   btif_av_source_dispatch_sm_event(btif_av_source_active_peer(),
                                    BTIF_AV_START_STREAM_REQ_EVT);
 }
 
-void btif_av_stream_stop(void) {
+void btif_av_stream_stop(const RawAddress& peer_address) {
+  LOG_INFO(LOG_TAG, "%s peer %s", __func__, peer_address.ToString().c_str());
+
+  if (!peer_address.IsEmpty()) {
+    btif_av_source_dispatch_sm_event(peer_address, BTIF_AV_STOP_STREAM_REQ_EVT);
+    return;
+  }
+
   // The active peer might have changed and we might be in the process
   // of reconfiguring the stream. We need to stop the appopriate peer(s).
   for (auto it : btif_av_source.Peers()) {
@@ -2744,6 +2760,7 @@
 }
 
 void btif_av_stream_suspend(void) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
   // The active peer might have changed and we might be in the process
   // of reconfiguring the stream. We need to suspend the appropriate peer(s).
   for (auto it : btif_av_source.Peers()) {
@@ -2754,11 +2771,13 @@
 }
 
 void btif_av_stream_start_offload(void) {
+  LOG_INFO(LOG_TAG, "%s", __func__);
   btif_av_source_dispatch_sm_event(btif_av_source_active_peer(),
                                    BTIF_AV_OFFLOAD_START_REQ_EVT);
 }
 
 void btif_av_src_disconnect_sink(const RawAddress& peer_address) {
+  LOG_INFO(LOG_TAG, "%s: peer %s", __func__, peer_address.ToString().c_str());
   src_disconnect_sink(peer_address);
 }
 
diff --git a/btif/src/btif_core.cc b/btif/src/btif_core.cc
index c2b662a..9fa4590 100644
--- a/btif/src/btif_core.cc
+++ b/btif/src/btif_core.cc
@@ -252,6 +252,8 @@
   return btif_thread_id_ == PlatformThread::CurrentId();
 }
 
+base::MessageLoop* get_jni_message_loop() { return message_loop_; }
+
 /*******************************************************************************
  *
  * Function         btif_is_dut_mode
diff --git a/btif/src/btif_dm.cc b/btif/src/btif_dm.cc
index d8ebbdf..91afabf 100644
--- a/btif/src/btif_dm.cc
+++ b/btif/src/btif_dm.cc
@@ -97,8 +97,9 @@
 #define BTIF_DM_MAX_SDP_ATTEMPTS_AFTER_PAIRING 2
 
 #define NUM_TIMEOUT_RETRIES 5
-
+#ifndef PROPERTY_PRODUCT_MODEL
 #define PROPERTY_PRODUCT_MODEL "ro.product.model"
+#endif
 #define DEFAULT_LOCAL_NAME_MAX 31
 #if (DEFAULT_LOCAL_NAME_MAX > BTM_MAX_LOC_BD_NAME_LEN)
 #error "default btif local name size exceeds stack supported length"
@@ -125,15 +126,17 @@
   btif_dm_ble_cb_t ble;
 } btif_dm_pairing_cb_t;
 
+// TODO(jpawlowski): unify ?
+// btif_dm_local_key_id_t == tBTM_BLE_LOCAL_ID_KEYS == tBTA_BLE_LOCAL_ID_KEYS
 typedef struct {
-  uint8_t ir[BT_OCTET16_LEN];
-  uint8_t irk[BT_OCTET16_LEN];
-  uint8_t dhk[BT_OCTET16_LEN];
+  Octet16 ir;
+  Octet16 irk;
+  Octet16 dhk;
 } btif_dm_local_key_id_t;
 
 typedef struct {
   bool is_er_rcvd;
-  uint8_t er[BT_OCTET16_LEN];
+  Octet16 er;
   bool is_id_keys_rcvd;
   btif_dm_local_key_id_t id_keys; /* ID kyes */
 
@@ -1395,10 +1398,15 @@
       if ((p_data->disc_res.result != BTA_SUCCESS) &&
           (pairing_cb.state == BT_BOND_STATE_BONDING) &&
           (pairing_cb.sdp_attempts < BTIF_DM_MAX_SDP_ATTEMPTS_AFTER_PAIRING)) {
-        BTIF_TRACE_WARNING("%s:SDP failed after bonding re-attempting",
-                           __func__);
-        pairing_cb.sdp_attempts++;
-        btif_dm_get_remote_services(bd_addr);
+        if (pairing_cb.sdp_attempts) {
+          BTIF_TRACE_WARNING("%s: SDP failed after bonding re-attempting",
+                             __func__);
+          pairing_cb.sdp_attempts++;
+          btif_dm_get_remote_services(bd_addr);
+        } else {
+          BTIF_TRACE_WARNING("%s: SDP triggered by someone failed when bonding",
+                             __func__);
+        }
         return;
       }
       prop.type = BT_PROPERTY_UUIDS;
@@ -1791,25 +1799,22 @@
     case BTA_DM_BLE_LOCAL_IR_EVT:
       BTIF_TRACE_DEBUG("BTA_DM_BLE_LOCAL_IR_EVT. ");
       ble_local_key_cb.is_id_keys_rcvd = true;
-      memcpy(&ble_local_key_cb.id_keys.irk[0], &p_data->ble_id_keys.irk[0],
-             sizeof(BT_OCTET16));
-      memcpy(&ble_local_key_cb.id_keys.ir[0], &p_data->ble_id_keys.ir[0],
-             sizeof(BT_OCTET16));
-      memcpy(&ble_local_key_cb.id_keys.dhk[0], &p_data->ble_id_keys.dhk[0],
-             sizeof(BT_OCTET16));
-      btif_storage_add_ble_local_key((char*)&ble_local_key_cb.id_keys.irk[0],
-                                     BTIF_DM_LE_LOCAL_KEY_IRK, BT_OCTET16_LEN);
-      btif_storage_add_ble_local_key((char*)&ble_local_key_cb.id_keys.ir[0],
-                                     BTIF_DM_LE_LOCAL_KEY_IR, BT_OCTET16_LEN);
-      btif_storage_add_ble_local_key((char*)&ble_local_key_cb.id_keys.dhk[0],
-                                     BTIF_DM_LE_LOCAL_KEY_DHK, BT_OCTET16_LEN);
+      ble_local_key_cb.id_keys.irk = p_data->ble_id_keys.irk;
+      ble_local_key_cb.id_keys.ir = p_data->ble_id_keys.ir;
+      ble_local_key_cb.id_keys.dhk = p_data->ble_id_keys.dhk;
+      btif_storage_add_ble_local_key(ble_local_key_cb.id_keys.irk,
+                                     BTIF_DM_LE_LOCAL_KEY_IRK);
+      btif_storage_add_ble_local_key(ble_local_key_cb.id_keys.ir,
+                                     BTIF_DM_LE_LOCAL_KEY_IR);
+      btif_storage_add_ble_local_key(ble_local_key_cb.id_keys.dhk,
+                                     BTIF_DM_LE_LOCAL_KEY_DHK);
       break;
     case BTA_DM_BLE_LOCAL_ER_EVT:
       BTIF_TRACE_DEBUG("BTA_DM_BLE_LOCAL_ER_EVT. ");
       ble_local_key_cb.is_er_rcvd = true;
-      memcpy(&ble_local_key_cb.er[0], &p_data->ble_er[0], sizeof(BT_OCTET16));
-      btif_storage_add_ble_local_key((char*)&ble_local_key_cb.er[0],
-                                     BTIF_DM_LE_LOCAL_KEY_ER, BT_OCTET16_LEN);
+      ble_local_key_cb.er = p_data->ble_er;
+      btif_storage_add_ble_local_key(ble_local_key_cb.er,
+                                     BTIF_DM_LE_LOCAL_KEY_ER);
       break;
 
     case BTA_DM_BLE_AUTH_CMPL_EVT:
@@ -2649,7 +2654,7 @@
   }
 }
 
-void btif_dm_proc_loc_oob(bool valid, BT_OCTET16 c, BT_OCTET16 r) {
+void btif_dm_proc_loc_oob(bool valid, const Octet16& c, const Octet16& r) {
   FILE* fp;
   const char* path_a = "/data/misc/bluedroid/LOCAL/a.key";
   const char* path_b = "/data/misc/bluedroid/LOCAL/b.key";
@@ -2658,8 +2663,8 @@
   BTIF_TRACE_DEBUG("%s: valid=%d", __func__, valid);
   if (is_empty_128bit(oob_cb.oob_data.c192) && valid) {
     BTIF_TRACE_DEBUG("save local OOB data in memory");
-    memcpy(oob_cb.oob_data.c192, c, BT_OCTET16_LEN);
-    memcpy(oob_cb.oob_data.r192, r, BT_OCTET16_LEN);
+    memcpy(oob_cb.oob_data.c192, c.data(), OCTET16_LEN);
+    memcpy(oob_cb.oob_data.r192, r.data(), OCTET16_LEN);
     osi_property_get("service.brcm.bt.oob", prop_oob, "3");
     BTIF_TRACE_DEBUG("%s: prop_oob = %s", __func__, prop_oob);
     if (prop_oob[0] == '1')
@@ -2674,8 +2679,8 @@
       } else {
         BTIF_TRACE_DEBUG("%s: save local OOB data into file %s", __func__,
                          path);
-        fwrite(c, 1, BT_OCTET16_LEN, fp);
-        fwrite(r, 1, BT_OCTET16_LEN, fp);
+        fwrite(c.data(), 1, OCTET16_LEN, fp);
+        fwrite(r.data(), 1, OCTET16_LEN, fp);
         fclose(fp);
       }
     }
@@ -2745,8 +2750,8 @@
   return true;
 }
 
-bool btif_dm_proc_rmt_oob(const RawAddress& bd_addr, BT_OCTET16 p_c,
-                          BT_OCTET16 p_r) {
+bool btif_dm_proc_rmt_oob(const RawAddress& bd_addr, Octet16* p_c,
+                          Octet16* p_r) {
   const char* path_a = "/data/misc/bluedroid/LOCAL/a.key";
   const char* path_b = "/data/misc/bluedroid/LOCAL/b.key";
   const char* path = NULL;
@@ -2769,8 +2774,8 @@
   }
 
   BTIF_TRACE_DEBUG("%s: read OOB data from %s", __func__, path);
-  fread(p_c, 1, BT_OCTET16_LEN, fp);
-  fread(p_r, 1, BT_OCTET16_LEN, fp);
+  fread(p_c->data(), 1, OCTET16_LEN, fp);
+  fread(p_r->data(), 1, OCTET16_LEN, fp);
   fclose(fp);
 
   RawAddress bt_bd_addr = bd_addr;
@@ -2872,41 +2877,37 @@
 void btif_dm_load_ble_local_keys(void) {
   memset(&ble_local_key_cb, 0, sizeof(btif_dm_local_key_cb_t));
 
-  if (btif_storage_get_ble_local_key(BTIF_DM_LE_LOCAL_KEY_ER,
-                                     (char*)&ble_local_key_cb.er[0],
-                                     BT_OCTET16_LEN) == BT_STATUS_SUCCESS) {
+  if (btif_storage_get_ble_local_key(
+          BTIF_DM_LE_LOCAL_KEY_ER, &ble_local_key_cb.er) == BT_STATUS_SUCCESS) {
     ble_local_key_cb.is_er_rcvd = true;
     BTIF_TRACE_DEBUG("%s BLE ER key loaded", __func__);
   }
 
   if ((btif_storage_get_ble_local_key(BTIF_DM_LE_LOCAL_KEY_IR,
-                                      (char*)&ble_local_key_cb.id_keys.ir[0],
-                                      BT_OCTET16_LEN) == BT_STATUS_SUCCESS) &&
+                                      &ble_local_key_cb.id_keys.ir) ==
+       BT_STATUS_SUCCESS) &&
       (btif_storage_get_ble_local_key(BTIF_DM_LE_LOCAL_KEY_IRK,
-                                      (char*)&ble_local_key_cb.id_keys.irk[0],
-                                      BT_OCTET16_LEN) == BT_STATUS_SUCCESS) &&
+                                      &ble_local_key_cb.id_keys.irk) ==
+       BT_STATUS_SUCCESS) &&
       (btif_storage_get_ble_local_key(BTIF_DM_LE_LOCAL_KEY_DHK,
-                                      (char*)&ble_local_key_cb.id_keys.dhk[0],
-                                      BT_OCTET16_LEN) == BT_STATUS_SUCCESS)) {
+                                      &ble_local_key_cb.id_keys.dhk) ==
+       BT_STATUS_SUCCESS)) {
     ble_local_key_cb.is_id_keys_rcvd = true;
     BTIF_TRACE_DEBUG("%s BLE ID keys loaded", __func__);
   }
 }
 void btif_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask,
-                                BT_OCTET16 er,
+                                Octet16* p_er,
                                 tBTA_BLE_LOCAL_ID_KEYS* p_id_keys) {
   if (ble_local_key_cb.is_er_rcvd) {
-    memcpy(&er[0], &ble_local_key_cb.er[0], sizeof(BT_OCTET16));
+    *p_er = ble_local_key_cb.er;
     *p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ER;
   }
 
   if (ble_local_key_cb.is_id_keys_rcvd) {
-    memcpy(&p_id_keys->ir[0], &ble_local_key_cb.id_keys.ir[0],
-           sizeof(BT_OCTET16));
-    memcpy(&p_id_keys->irk[0], &ble_local_key_cb.id_keys.irk[0],
-           sizeof(BT_OCTET16));
-    memcpy(&p_id_keys->dhk[0], &ble_local_key_cb.id_keys.dhk[0],
-           sizeof(BT_OCTET16));
+    p_id_keys->ir = ble_local_key_cb.id_keys.ir;
+    p_id_keys->irk = ble_local_key_cb.id_keys.irk;
+    p_id_keys->dhk = ble_local_key_cb.id_keys.dhk;
     *p_key_mask |= BTA_BLE_LOCAL_KEY_TYPE_ID;
   }
   BTIF_TRACE_DEBUG("%s  *p_key_mask=0x%02x", __func__, *p_key_mask);
diff --git a/btif/src/btif_gatt_test.cc b/btif/src/btif_gatt_test.cc
index 1297d50..1c0af35 100644
--- a/btif/src/btif_gatt_test.cc
+++ b/btif/src/btif_gatt_test.cc
@@ -219,24 +219,18 @@
 
     case 0x04: /* Discover */
     {
-      tGATT_DISC_PARAM param;
-      memset(&param, 0, sizeof(tGATT_DISC_PARAM));
-
       if (params->u1 >= GATT_DISC_MAX) {
         LOG_ERROR(LOG_TAG, "%s: DISCOVER - Invalid type (%d)!", __func__,
                   params->u1);
         return (bt_status_t)0;
       }
 
-      param.s_handle = params->u2;
-      param.e_handle = params->u3;
-      param.service = *params->uuid1;
-
       LOG_DEBUG(LOG_TAG,
                 "%s: DISCOVER (%s), conn_id=%d, uuid=%s, handles=0x%04x-0x%04x",
                 __func__, disc_name[params->u1], test_cb.conn_id,
-                param.service.ToString().c_str(), params->u2, params->u3);
-      GATTC_Discover(test_cb.conn_id, params->u1, &param);
+                params->uuid1->ToString().c_str(), params->u2, params->u3);
+      GATTC_Discover(test_cb.conn_id, params->u1, params->u2, params->u3,
+                     *params->uuid1);
       break;
     }
 
diff --git a/btif/src/btif_hf.cc b/btif/src/btif_hf.cc
index 5388986..038af2e 100644
--- a/btif/src/btif_hf.cc
+++ b/btif/src/btif_hf.cc
@@ -118,16 +118,6 @@
 
 static btif_hf_cb_t btif_hf_cb[BTA_AG_MAX_NUM_CLIENTS];
 
-/* By default, even though codec negotiation is enabled, we will not use WBS as
- * the default
- * codec unless this variable is set to true.
- */
-#ifndef BTIF_HF_WBS_PREFERRED
-#define BTIF_HF_WBS_PREFERRED false
-#endif
-
-static bool btif_conf_hf_force_wbs = BTIF_HF_WBS_PREFERRED;
-
 static const char* dump_hf_call_state(bthf_call_state_t call_state) {
   switch (call_state) {
     CASE_RETURN_STR(BTHF_CALL_STATE_IDLE)
@@ -511,7 +501,7 @@
       we should set the BTA AG Codec to mSBC. This would trigger a +BCS to mSBC
       at the time
       of SCO connection establishment */
-      if ((btif_conf_hf_force_wbs) && (p_data->val.num & BTA_AG_CODEC_MSBC)) {
+      if (p_data->val.num & BTA_AG_CODEC_MSBC) {
         BTIF_TRACE_EVENT("%s: btif_hf override-Preferred Codec to MSBC",
                          __func__);
         BTA_AgSetCodec(btif_hf_cb[idx].handle, BTA_AG_CODEC_MSBC);
@@ -783,14 +773,12 @@
     LOG(ERROR) << ": SLC not connected for " << *bd_addr;
     return BT_STATUS_NOT_READY;
   }
-  BTA_AgAudioOpen(btif_hf_cb[idx].handle);
-  // Inform the application that the audio connection has been initiated
-  // successfully
   do_in_jni_thread(base::Bind(&Callbacks::AudioStateCallback,
                               // Manual pointer management for now
                               base::Unretained(bt_hf_callbacks),
                               BTHF_AUDIO_STATE_CONNECTING,
                               &btif_hf_cb[idx].connected_bda));
+  BTA_AgAudioOpen(btif_hf_cb[idx].handle);
   return BT_STATUS_SUCCESS;
 }
 
diff --git a/btif/src/btif_rc.cc b/btif/src/btif_rc.cc
index 0113c85..3dafe65 100644
--- a/btif/src/btif_rc.cc
+++ b/btif/src/btif_rc.cc
@@ -3246,8 +3246,9 @@
 
       case AVRC_EVT_ADDR_PLAYER_CHANGE:
         do_in_jni_thread(
-            FROM_HERE, base::Bind(bt_rc_ctrl_callbacks->set_addressed_player_cb,
-                                  p_dev->rc_addr, BTRC_STS_ADDR_PLAY_CHGD));
+            FROM_HERE,
+            base::Bind(bt_rc_ctrl_callbacks->addressed_player_changed_cb,
+                       p_dev->rc_addr, p_rsp->param.addr_player.player_id));
         break;
 
       case AVRC_EVT_UIDS_CHANGE:
diff --git a/btif/src/btif_sdp_server.cc b/btif/src/btif_sdp_server.cc
index 4738da4..d3a8808 100644
--- a/btif/src/btif_sdp_server.cc
+++ b/btif/src/btif_sdp_server.cc
@@ -28,6 +28,7 @@
 
 #define LOG_TAG "bt_btif_sdp_server"
 
+#include <log/log.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/btif/src/btif_storage.cc b/btif/src/btif_storage.cc
index 766dc7f..3b10eee 100644
--- a/btif/src/btif_storage.cc
+++ b/btif/src/btif_storage.cc
@@ -413,9 +413,9 @@
 static bt_status_t btif_in_fetch_bonded_device(const std::string& bdstr) {
   bool bt_linkkey_file_found = false;
 
-  LINK_KEY link_key;
-  size_t size = sizeof(link_key);
-  if (btif_config_get_bin(bdstr, "LinkKey", (uint8_t*)link_key, &size)) {
+  LinkKey link_key;
+  size_t size = link_key.size();
+  if (btif_config_get_bin(bdstr, "LinkKey", link_key.data(), &size)) {
     int linkkey_type;
     if (btif_config_get_int(bdstr, "LinkKeyType", &linkkey_type)) {
       bt_linkkey_file_found = true;
@@ -457,9 +457,9 @@
     if (!RawAddress::IsValidAddress(name)) continue;
 
     BTIF_TRACE_DEBUG("Remote device:%s", name.c_str());
-    LINK_KEY link_key;
+    LinkKey link_key;
     size_t size = sizeof(link_key);
-    if (btif_config_get_bin(name, "LinkKey", link_key, &size)) {
+    if (btif_config_get_bin(name, "LinkKey", link_key.data(), &size)) {
       int linkkey_type;
       if (btif_config_get_int(name, "LinkKeyType", &linkkey_type)) {
         RawAddress bd_addr;
@@ -815,12 +815,13 @@
  ******************************************************************************/
 
 bt_status_t btif_storage_add_bonded_device(RawAddress* remote_bd_addr,
-                                           LINK_KEY link_key, uint8_t key_type,
+                                           LinkKey link_key, uint8_t key_type,
                                            uint8_t pin_length) {
   std::string bdstr = remote_bd_addr->ToString();
   int ret = btif_config_set_int(bdstr, "LinkKeyType", (int)key_type);
   ret &= btif_config_set_int(bdstr, "PinLength", (int)pin_length);
-  ret &= btif_config_set_bin(bdstr, "LinkKey", link_key, sizeof(LINK_KEY));
+  ret &=
+      btif_config_set_bin(bdstr, "LinkKey", link_key.data(), link_key.size());
 
   if (is_restricted_mode()) {
     BTIF_TRACE_WARNING("%s: '%s' pairing will be removed if unrestricted",
@@ -1125,8 +1126,8 @@
  *                  BT_STATUS_FAIL otherwise
  *
  ******************************************************************************/
-bt_status_t btif_storage_add_ble_local_key(char* key, uint8_t key_type,
-                                           uint8_t key_length) {
+bt_status_t btif_storage_add_ble_local_key(const Octet16& key,
+                                           uint8_t key_type) {
   const char* name;
   switch (key_type) {
     case BTIF_DM_LE_LOCAL_KEY_IR:
@@ -1144,24 +1145,17 @@
     default:
       return BT_STATUS_FAIL;
   }
-  int ret =
-      btif_config_set_bin("Adapter", name, (const uint8_t*)key, key_length);
+  int ret = btif_config_set_bin("Adapter", name, key.data(), key.size());
   btif_config_save();
   return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
 }
 
-/*******************************************************************************
- *
- * Function         btif_storage_get_ble_local_key
- *
- * Description
- *
- * Returns          BT_STATUS_SUCCESS if the fetch was successful,
- *                  BT_STATUS_FAIL otherwise
- *
- ******************************************************************************/
-bt_status_t btif_storage_get_ble_local_key(uint8_t key_type, char* key_value,
-                                           int key_length) {
+/** Stores local key of |key_type| into |key_value|
+ * Returns BT_STATUS_SUCCESS if the fetch was successful, BT_STATUS_FAIL
+ * otherwise
+ */
+bt_status_t btif_storage_get_ble_local_key(uint8_t key_type,
+                                           Octet16* key_value) {
   const char* name;
   switch (key_type) {
     case BTIF_DM_LE_LOCAL_KEY_IR:
@@ -1179,8 +1173,8 @@
     default:
       return BT_STATUS_FAIL;
   }
-  size_t length = key_length;
-  int ret = btif_config_get_bin("Adapter", name, (uint8_t*)key_value, &length);
+  size_t length = key_value->size();
+  int ret = btif_config_get_bin("Adapter", name, key_value->data(), &length);
   return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
 }
 
@@ -1572,6 +1566,12 @@
   return btif_config_exist(remote_bd_addr->ToString(), "Restricted");
 }
 
+int btif_storage_get_num_bonded_devices(void) {
+  btif_bonded_devices_t bonded_devices;
+  btif_in_fetch_bonded_devices(&bonded_devices, 0);
+  return bonded_devices.num_devices;
+}
+
 /*******************************************************************************
  * Function         btif_storage_load_hidd
  *
diff --git a/build/secondary/third_party/libchrome/BUILD.gn b/build/secondary/third_party/libchrome/BUILD.gn
index 9ffdaa7..df6a886 100644
--- a/build/secondary/third_party/libchrome/BUILD.gn
+++ b/build/secondary/third_party/libchrome/BUILD.gn
@@ -133,6 +133,7 @@
     "base/strings/sys_string_conversions_posix.cc",
     "base/strings/utf_string_conversions.cc",
     "base/strings/utf_string_conversion_utils.cc",
+    "base/synchronization/atomic_flag.cc",
     "base/synchronization/condition_variable_posix.cc",
     "base/synchronization/lock.cc",
     "base/synchronization/lock_impl_posix.cc",
diff --git a/conf/bt_stack.conf b/conf/bt_stack.conf
index f1557b1..d1e7756 100644
--- a/conf/bt_stack.conf
+++ b/conf/bt_stack.conf
@@ -50,6 +50,9 @@
 # SMP Pair options (formatted as hex bytes) auth, io, ikey, rkey, ksize
 #PTS_SmpOptions=0xD,0x4,0xf,0xf,0x10
 
+# PTS AVRCP Test mode
+#PTS_AvrcpTest=true
+
 # SMP Certification Failure Cases
 # Set any of the following SMP error values (from smp_api_types.h)
 # to induce pairing failues for various PTS SMP test cases.
diff --git a/doc/pts_guide.md b/doc/pts_guide.md
new file mode 100644
index 0000000..1e13b9d
--- /dev/null
+++ b/doc/pts_guide.md
@@ -0,0 +1,43 @@
+# Fluoride Bluetooth Profile Tuning Suite (PTS) Test Mode
+
+This document provides commands to enable PTS test mode for Fluoride stack. We
+need special handling for some test cases as they are not applicable for the
+Fluoride stack.
+
+## PTS Test Mode system property
+
+Profile services in packages/apps/Bluetooth uses system property
+`persist.bluetooth.pts` to check if the PTS test mode is enabled. To enable it:
+
+```sh
+adb shell setprop persist.bluetooth.pts true
+```
+
+To disable it:
+
+```sh
+adb shell setprop persist.bluetooth.pts false
+```
+
+### Current use case
+
+- In `newavrcp`, we send active player update to remote device only in PTS test
+  mode (AVRCP/TG/MPS/BV-05-C AVRCP/TG/MPS/BV-07-C).
+
+## PTS Helpers in stack config
+
+Native stack also requires some special handling, and the config is stored in
+`conf/bt_stack.conf`. To enable a flag, uncomment the corresponding line and
+push the config file to `/etc/bluetooth/` in IUT.
+
+### Current use case
+
+- `PTS_SecurePairOnly` enables secure connections only mode.
+- `PTS_DisableConnUpdates` disables LE Connection updates.
+- `PTS_DisableSDPOnLEPair` disables BR/EDR discovery after LE pairing to avoid
+  cross key derivation errors.
+- `PTS_SmpOptions` sets SMP Pair options (formatted as hex bytes) `auth, io,
+  ikey, rkey, ksize`.
+- `PTS_AvrcpTest` enables AVRCP test mode. The UID is set to 0xffffffffffffffff
+  in `TrackChangedNotificationResponse` (AVRCP/TG/NFY/BV-04-C).
+- `PTS_SmpFailureCase` enables handling for various SMP failure cases.
diff --git a/hci/src/hci_layer.cc b/hci/src/hci_layer.cc
index 5111fc5..f1e0e7a 100644
--- a/hci/src/hci_layer.cc
+++ b/hci/src/hci_layer.cc
@@ -78,7 +78,9 @@
 
 // Abort if there is no response to an HCI command.
 static const uint32_t COMMAND_PENDING_TIMEOUT_MS = 2000;
+static const uint32_t COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS = 500;
 static const uint32_t COMMAND_TIMEOUT_RESTART_MS = 5000;
+static const int HCI_UNKNOWN_COMMAND_TIMED_OUT = 0x00ffffff;
 
 // Our interface
 static bool interface_created;
@@ -105,7 +107,7 @@
 // Inbound-related
 static alarm_t* command_response_timer;
 static list_t* commands_pending_response;
-static std::recursive_mutex commands_pending_response_mutex;
+static std::recursive_timed_mutex commands_pending_response_mutex;
 static alarm_t* hci_timeout_abort_timer;
 
 // The hand-off point for data going to a higher layer, set by the higher layer
@@ -255,7 +257,8 @@
 
   // Free the timers
   {
-    std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+    std::lock_guard<std::recursive_timed_mutex> lock(
+        commands_pending_response_mutex);
     alarm_free(command_response_timer);
     command_response_timer = NULL;
     alarm_free(startup_timer);
@@ -277,7 +280,8 @@
   hci_close();
 
   {
-    std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+    std::lock_guard<std::recursive_timed_mutex> lock(
+        commands_pending_response_mutex);
     list_free(commands_pending_response);
     commands_pending_response = NULL;
   }
@@ -371,7 +375,8 @@
 
 static void event_finish_startup(UNUSED_ATTR void* context) {
   LOG_INFO(LOG_TAG, "%s", __func__);
-  std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+  std::lock_guard<std::recursive_timed_mutex> lock(
+      commands_pending_response_mutex);
   alarm_cancel(startup_timer);
   future_ready(startup_future, FUTURE_SUCCESS);
   startup_future = NULL;
@@ -380,9 +385,18 @@
 static void startup_timer_expired(UNUSED_ATTR void* context) {
   LOG_ERROR(LOG_TAG, "%s", __func__);
 
-  std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+  std::unique_lock<std::recursive_timed_mutex> lock(
+      commands_pending_response_mutex, std::defer_lock);
+  if (!lock.try_lock_for(std::chrono::milliseconds(
+          COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS))) {
+    LOG_ERROR(LOG_TAG, "%s: Cannot obtain the mutex", __func__);
+    // We cannot recover if the startup timer expired and we are deadlock,
+    // hence abort.
+    abort();
+  }
   future_ready(startup_future, FUTURE_FAIL);
   startup_future = NULL;
+  lock.unlock();
 }
 
 // Command/packet transmitting functions
@@ -408,7 +422,8 @@
 static void event_command_ready(waiting_command_t* wait_entry) {
   {
     /// Move it to the list of commands awaiting response
-    std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+    std::lock_guard<std::recursive_timed_mutex> lock(
+        commands_pending_response_mutex);
     wait_entry->timestamp = std::chrono::steady_clock::now();
     list_append(commands_pending_response, wait_entry);
   }
@@ -475,10 +490,7 @@
   abort();
 }
 
-// Print debugging information and quit. Don't dereference original_wait_entry.
-static void command_timed_out(void* original_wait_entry) {
-  std::unique_lock<std::recursive_mutex> lock(commands_pending_response_mutex);
-
+static void command_timed_out_log_info(void* original_wait_entry) {
   LOG_ERROR(LOG_TAG, "%s: %d commands pending response", __func__,
             get_num_waiting_commands());
 
@@ -508,7 +520,21 @@
 
     LOG_EVENT_INT(BT_HCI_TIMEOUT_TAG_NUM, wait_entry->opcode);
   }
-  lock.unlock();
+}
+
+// Print debugging information and quit. Don't dereference original_wait_entry.
+static void command_timed_out(void* original_wait_entry) {
+  LOG_ERROR(LOG_TAG, "%s", __func__);
+  std::unique_lock<std::recursive_timed_mutex> lock(
+      commands_pending_response_mutex, std::defer_lock);
+  if (!lock.try_lock_for(std::chrono::milliseconds(
+          COMMAND_PENDING_MUTEX_ACQUIRE_TIMEOUT_MS))) {
+    LOG_ERROR(LOG_TAG, "%s: Cannot obtain the mutex", __func__);
+    LOG_EVENT_INT(BT_HCI_TIMEOUT_TAG_NUM, HCI_UNKNOWN_COMMAND_TIMED_OUT);
+  } else {
+    command_timed_out_log_info(original_wait_entry);
+    lock.unlock();
+  }
 
   // Don't request a firmware dump for multiple hci timeouts
   if (hci_timeout_abort_timer != NULL || hci_firmware_log_fd != INVALID_FD) {
@@ -675,7 +701,8 @@
 // Misc internal functions
 
 static waiting_command_t* get_waiting_command(command_opcode_t opcode) {
-  std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+  std::lock_guard<std::recursive_timed_mutex> lock(
+      commands_pending_response_mutex);
 
   for (const list_node_t* node = list_begin(commands_pending_response);
        node != list_end(commands_pending_response); node = list_next(node)) {
@@ -693,12 +720,14 @@
 }
 
 static int get_num_waiting_commands() {
-  std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+  std::lock_guard<std::recursive_timed_mutex> lock(
+      commands_pending_response_mutex);
   return list_length(commands_pending_response);
 }
 
 static void update_command_response_timer(void) {
-  std::lock_guard<std::recursive_mutex> lock(commands_pending_response_mutex);
+  std::lock_guard<std::recursive_timed_mutex> lock(
+      commands_pending_response_mutex);
 
   if (command_response_timer == NULL) return;
   if (list_is_empty(commands_pending_response)) {
diff --git a/include/hardware/avrcp/avrcp.h b/include/hardware/avrcp/avrcp.h
index dba43fc..577acb9 100644
--- a/include/hardware/avrcp/avrcp.h
+++ b/include/hardware/avrcp/avrcp.h
@@ -72,10 +72,11 @@
 
 class MediaCallbacks {
  public:
-  virtual void SendMediaUpdate(bool track_changed, bool play_state, bool queue);
+  virtual void SendMediaUpdate(bool track_changed, bool play_state,
+                               bool queue) = 0;
   virtual void SendFolderUpdate(bool available_players, bool addressed_players,
-                                bool uids_changed);
-  virtual void SendActiveDeviceChanged(const RawAddress& address);
+                                bool uids_changed) = 0;
+  virtual void SendActiveDeviceChanged(const RawAddress& address) = 0;
   virtual ~MediaCallbacks() = default;
 };
 
diff --git a/include/hardware/bt_rc.h b/include/hardware/bt_rc.h
index 9349eed..382500d 100644
--- a/include/hardware/bt_rc.h
+++ b/include/hardware/bt_rc.h
@@ -613,6 +613,9 @@
                                                       uint8_t depth);
 typedef void (*btrc_ctrl_set_addressed_player_callback)(
     const RawAddress& bd_addr, uint8_t status);
+typedef void (*btrc_ctrl_addressed_player_changed_callback)(
+    const RawAddress& bd_addr, uint16_t id);
+
 /** BT-RC Controller callback structure. */
 typedef struct {
   /** set to sizeof(BtRcCallbacks) */
@@ -635,6 +638,7 @@
   btrc_ctrl_change_path_callback change_folder_path_cb;
   btrc_ctrl_set_browsed_player_callback set_browsed_player_cb;
   btrc_ctrl_set_addressed_player_callback set_addressed_player_cb;
+  btrc_ctrl_addressed_player_changed_callback addressed_player_changed_cb;
 } btrc_ctrl_callbacks_t;
 
 /** Represents the standard BT-RC AVRCP Controller interface. */
diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h
index 10a4b11..e2f5203 100644
--- a/internal_include/bt_target.h
+++ b/internal_include/bt_target.h
@@ -353,7 +353,7 @@
 
 /* The number of SCO links. */
 #ifndef BTM_MAX_SCO_LINKS
-#define BTM_MAX_SCO_LINKS 3
+#define BTM_MAX_SCO_LINKS 6
 #endif
 
 /* The number of security records for peer devices. */
diff --git a/internal_include/stack_config.h b/internal_include/stack_config.h
index 81396ab..d623fa4 100644
--- a/internal_include/stack_config.h
+++ b/internal_include/stack_config.h
@@ -27,6 +27,7 @@
 
 typedef struct {
   bool (*get_trace_config_enabled)(void);
+  bool (*get_pts_avrcp_test)(void);
   bool (*get_pts_secure_only_mode)(void);
   bool (*get_pts_conn_updates_disabled)(void);
   bool (*get_pts_crosskey_sdp_disable)(void);
diff --git a/linux_include/log/log.h b/linux_include/log/log.h
new file mode 100644
index 0000000..7149470
--- /dev/null
+++ b/linux_include/log/log.h
@@ -0,0 +1,26 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+/* This file provides empty implementation of android_errorWriteLog, which is
+ * not required on linux. It should be on include path only for linux build. */
+
+#if defined(OS_GENERIC)
+
+inline int android_errorWriteLog(int, const char*) { return 0; };
+
+#endif
diff --git a/main/BUILD.gn b/main/BUILD.gn
index ccbc49f..8152e3b 100644
--- a/main/BUILD.gn
+++ b/main/BUILD.gn
@@ -70,6 +70,7 @@
     "//btcore",
     "//btif",
     "//device",
+    "//embdrv/g722",
     "//embdrv/sbc",
     "//hci",
     "//osi",
diff --git a/main/stack_config.cc b/main/stack_config.cc
index 6b937aa..0e56250 100644
--- a/main/stack_config.cc
+++ b/main/stack_config.cc
@@ -27,6 +27,7 @@
 
 namespace {
 const char* TRACE_CONFIG_ENABLED_KEY = "TraceConf";
+const char* PTS_AVRCP_TEST = "PTS_AvrcpTest";
 const char* PTS_SECURE_ONLY_MODE = "PTS_SecurePairOnly";
 const char* PTS_LE_CONN_UPDATED_DISABLED = "PTS_DisableConnUpdates";
 const char* PTS_DISABLE_SDP_LE_PAIR = "PTS_DisableSDPOnLEPair";
@@ -77,6 +78,11 @@
                          TRACE_CONFIG_ENABLED_KEY, false);
 }
 
+static bool get_pts_avrcp_test(void) {
+  return config_get_bool(*config, CONFIG_DEFAULT_SECTION, PTS_AVRCP_TEST,
+                         false);
+}
+
 static bool get_pts_secure_only_mode(void) {
   return config_get_bool(*config, CONFIG_DEFAULT_SECTION, PTS_SECURE_ONLY_MODE,
                          false);
@@ -104,12 +110,10 @@
 
 static config_t* get_all(void) { return config.get(); }
 
-const stack_config_t interface = {get_trace_config_enabled,
-                                  get_pts_secure_only_mode,
-                                  get_pts_conn_updates_disabled,
-                                  get_pts_crosskey_sdp_disable,
-                                  get_pts_smp_options,
-                                  get_pts_smp_failure_case,
-                                  get_all};
+const stack_config_t interface = {
+    get_trace_config_enabled,     get_pts_avrcp_test,
+    get_pts_secure_only_mode,     get_pts_conn_updates_disabled,
+    get_pts_crosskey_sdp_disable, get_pts_smp_options,
+    get_pts_smp_failure_case,     get_all};
 
 const stack_config_t* stack_config_get_interface(void) { return &interface; }
diff --git a/osi/include/metrics.h b/osi/include/metrics.h
index 4f968d2..504a16a 100644
--- a/osi/include/metrics.h
+++ b/osi/include/metrics.h
@@ -105,6 +105,8 @@
   int32_t buffer_overruns_total = -1;
   float buffer_underruns_average = -1;
   int32_t buffer_underruns_count = -1;
+  int64_t codec_index = -1;
+  bool is_a2dp_offload = false;
 };
 
 class BluetoothMetricsLogger {
diff --git a/osi/src/metrics.cc b/osi/src/metrics.cc
index 96907c0..73450b9 100644
--- a/osi/src/metrics.cc
+++ b/osi/src/metrics.cc
@@ -29,6 +29,7 @@
 
 #include <base/base64.h>
 #include <base/logging.h>
+#include <include/hardware/bt_av.h>
 
 #include "bluetooth/metrics/bluetooth.pb.h"
 #include "osi/include/leaky_bonded_queue.h"
@@ -42,6 +43,7 @@
 namespace system_bt_osi {
 
 using bluetooth::metrics::BluetoothMetricsProto::A2DPSession;
+using bluetooth::metrics::BluetoothMetricsProto::A2dpSourceCodec;
 using bluetooth::metrics::BluetoothMetricsProto::BluetoothLog;
 using bluetooth::metrics::BluetoothMetricsProto::BluetoothSession;
 using bluetooth::metrics::BluetoothMetricsProto::
@@ -140,6 +142,12 @@
       buffer_underruns_count += metrics.buffer_underruns_count;
     }
   }
+  if (codec_index < 0) {
+    codec_index = metrics.codec_index;
+  }
+  if (!is_a2dp_offload) {
+    is_a2dp_offload = metrics.is_a2dp_offload;
+  }
 }
 
 bool A2dpSessionMetrics::operator==(const A2dpSessionMetrics& rhs) const {
@@ -151,7 +159,9 @@
          buffer_overruns_max_count == rhs.buffer_overruns_max_count &&
          buffer_overruns_total == rhs.buffer_overruns_total &&
          buffer_underruns_average == rhs.buffer_underruns_average &&
-         buffer_underruns_count == rhs.buffer_underruns_count;
+         buffer_underruns_count == rhs.buffer_underruns_count &&
+         codec_index == rhs.codec_index &&
+         is_a2dp_offload == rhs.is_a2dp_offload;
 }
 
 static DeviceInfo_DeviceType get_device_type(device_type_t type) {
@@ -230,6 +240,23 @@
   }
 }
 
+static A2dpSourceCodec get_a2dp_source_codec(int64_t codec_index) {
+  switch (codec_index) {
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC:
+      return A2dpSourceCodec::A2DP_SOURCE_CODEC_SBC;
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC:
+      return A2dpSourceCodec::A2DP_SOURCE_CODEC_AAC;
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX:
+      return A2dpSourceCodec::A2DP_SOURCE_CODEC_APTX;
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD:
+      return A2dpSourceCodec::A2DP_SOURCE_CODEC_APTX_HD;
+    case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC:
+      return A2dpSourceCodec::A2DP_SOURCE_CODEC_LDAC;
+    default:
+      return A2dpSourceCodec::A2DP_SOURCE_CODEC_UNKNOWN;
+  }
+}
+
 struct BluetoothMetricsLogger::impl {
   impl(size_t max_bluetooth_session, size_t max_pair_event,
        size_t max_wake_event, size_t max_scan_event)
@@ -356,6 +383,7 @@
       get_disconnect_reason_type(disconnect_reason));
   pimpl_->bt_session_queue_->Enqueue(pimpl_->bluetooth_session_);
   pimpl_->bluetooth_session_ = nullptr;
+  pimpl_->a2dp_session_metrics_ = A2dpSessionMetrics();
   {
     std::lock_guard<std::recursive_mutex> log_lock(pimpl_->bluetooth_log_lock_);
     pimpl_->bluetooth_log_->set_num_bluetooth_session(
@@ -404,6 +432,10 @@
       pimpl_->a2dp_session_metrics_.buffer_underruns_average);
   a2dp_session->set_buffer_underruns_count(
       pimpl_->a2dp_session_metrics_.buffer_underruns_count);
+  a2dp_session->set_source_codec(
+      get_a2dp_source_codec(pimpl_->a2dp_session_metrics_.codec_index));
+  a2dp_session->set_is_a2dp_offload(
+      pimpl_->a2dp_session_metrics_.is_a2dp_offload);
 }
 
 void BluetoothMetricsLogger::LogHeadsetProfileRfcConnection(
diff --git a/osi/src/metrics_linux.cc b/osi/src/metrics_linux.cc
index 76c1fde..5065a67 100644
--- a/osi/src/metrics_linux.cc
+++ b/osi/src/metrics_linux.cc
@@ -176,6 +176,11 @@
   // TODO(siyuanh): Implement for linux
 }
 
+void BluetoothMetricsLogger::LogHeadsetProfileRfcConnection(
+    tBTA_SERVICE_ID service_id) {
+  // TODO: Implement for linux
+}
+
 void BluetoothMetricsLogger::WriteString(std::string* serialized) {
   // TODO(siyuanh): Implement for linux
 }
diff --git a/osi/test/metrics_test.cc b/osi/test/metrics_test.cc
index 2c9762f..bfb548f 100644
--- a/osi/test/metrics_test.cc
+++ b/osi/test/metrics_test.cc
@@ -25,6 +25,7 @@
 #include <gtest/gtest.h>
 
 #include <base/logging.h>
+#include <include/hardware/bt_av.h>
 
 #include "bluetooth/metrics/bluetooth.pb.h"
 #include "osi/include/metrics.h"
@@ -35,6 +36,7 @@
 namespace testing {
 
 using bluetooth::metrics::BluetoothMetricsProto::A2DPSession;
+using bluetooth::metrics::BluetoothMetricsProto::A2dpSourceCodec;
 using bluetooth::metrics::BluetoothMetricsProto::BluetoothLog;
 using bluetooth::metrics::BluetoothMetricsProto::BluetoothSession;
 using bluetooth::metrics::BluetoothMetricsProto::
@@ -111,7 +113,8 @@
   return event;
 }
 
-A2DPSession* MakeA2DPSession(const A2dpSessionMetrics& metrics) {
+A2DPSession* MakeA2DPSession(const A2dpSessionMetrics& metrics,
+                             A2dpSourceCodec source_codec) {
   A2DPSession* session = new A2DPSession();
   session->set_media_timer_min_millis(metrics.media_timer_min_ms);
   session->set_media_timer_max_millis(metrics.media_timer_max_ms);
@@ -121,6 +124,8 @@
   session->set_buffer_underruns_average(metrics.buffer_underruns_average);
   session->set_buffer_underruns_count(metrics.buffer_underruns_count);
   session->set_audio_duration_millis(metrics.audio_duration_ms);
+  session->set_source_codec(source_codec);
+  session->set_is_a2dp_offload(metrics.is_a2dp_offload);
   return session;
 }
 
@@ -187,6 +192,8 @@
                 FloatNear((b).buffer_underruns_average, 0.01));              \
     (a).buffer_underruns_average = (b).buffer_underruns_average;             \
     EXPECT_EQ((a).buffer_underruns_count, (b).buffer_underruns_count);       \
+    EXPECT_EQ((a).codec_index, (b).codec_index);                             \
+    EXPECT_EQ((a).is_a2dp_offload, (b).is_a2dp_offload);                     \
   } while (0)
 
 /*
@@ -220,6 +227,12 @@
   metrics2.buffer_underruns_count = 2400;
   metrics_sum.buffer_underruns_average = 113.33333333;
   metrics_sum.buffer_underruns_count = 3600;
+  metrics1.codec_index = -1;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+  metrics_sum.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+  metrics1.is_a2dp_offload = false;
+  metrics2.is_a2dp_offload = true;
+  metrics_sum.is_a2dp_offload = true;
   metrics1.Update(metrics2);
   COMPARE_A2DP_METRICS(metrics1, metrics_sum);
   EXPECT_TRUE(metrics1 == metrics_sum);
@@ -246,6 +259,10 @@
   metrics2.buffer_underruns_count = 2400;
   metrics_sum.buffer_underruns_average = 130;
   metrics_sum.buffer_underruns_count = 2400;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_APTX;
+  metrics_sum.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_APTX;
+  metrics2.is_a2dp_offload = true;
+  metrics_sum.is_a2dp_offload = true;
   metrics1.Update(metrics2);
   COMPARE_A2DP_METRICS(metrics1, metrics_sum);
   EXPECT_TRUE(metrics1 == metrics_sum);
@@ -272,6 +289,10 @@
   metrics2.buffer_underruns_count = 2400;
   metrics_sum.buffer_underruns_average = 130;
   metrics_sum.buffer_underruns_count = 2400;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD;
+  metrics_sum.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD;
+  metrics2.is_a2dp_offload = true;
+  metrics_sum.is_a2dp_offload = true;
   metrics2.Update(metrics1);
   COMPARE_A2DP_METRICS(metrics2, metrics_sum);
   EXPECT_TRUE(metrics2 == metrics_sum);
@@ -560,10 +581,17 @@
   metrics2.buffer_underruns_count = 2400;
   metrics_sum.buffer_underruns_average = 113.33333333;
   metrics_sum.buffer_underruns_count = 3600;
+  metrics1.codec_index = -1;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+  metrics_sum.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+  metrics1.is_a2dp_offload = false;
+  metrics2.is_a2dp_offload = true;
+  metrics_sum.is_a2dp_offload = true;
   DeviceInfo* info = MakeDeviceInfo(
       BTM_COD_MAJOR_AUDIO_TEST,
       DeviceInfo_DeviceType::DeviceInfo_DeviceType_DEVICE_TYPE_BREDR);
-  A2DPSession* session = MakeA2DPSession(metrics_sum);
+  A2DPSession* session =
+      MakeA2DPSession(metrics_sum, A2dpSourceCodec::A2DP_SOURCE_CODEC_AAC);
   bt_sessions_.push_back(MakeBluetoothSession(
       10,
       BluetoothSession_ConnectionTechnologyType::
@@ -618,10 +646,13 @@
   metrics1.buffer_underruns_count = 1200;
   metrics2.buffer_underruns_average = 130;
   metrics2.buffer_underruns_count = 2400;
+  metrics1.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
   DeviceInfo* info = MakeDeviceInfo(
       BTM_COD_MAJOR_AUDIO_TEST,
       DeviceInfo_DeviceType::DeviceInfo_DeviceType_DEVICE_TYPE_BREDR);
-  A2DPSession* session = MakeA2DPSession(metrics1);
+  A2DPSession* session =
+      MakeA2DPSession(metrics1, A2dpSourceCodec::A2DP_SOURCE_CODEC_SBC);
   bt_sessions_.push_back(MakeBluetoothSession(
       1,
       BluetoothSession_ConnectionTechnologyType::
@@ -643,7 +674,7 @@
   info = MakeDeviceInfo(
       BTM_COD_MAJOR_AUDIO_TEST,
       DeviceInfo_DeviceType::DeviceInfo_DeviceType_DEVICE_TYPE_BREDR);
-  session = MakeA2DPSession(metrics2);
+  session = MakeA2DPSession(metrics2, A2dpSourceCodec::A2DP_SOURCE_CODEC_AAC);
   bt_sessions_.push_back(MakeBluetoothSession(
       1,
       BluetoothSession_ConnectionTechnologyType::
@@ -662,6 +693,86 @@
 }
 
 /*
+ * Test Case: A2DPSessionTwoUpdatesSeparatedbyEndTest
+ *
+ * 1. Create Instance
+ * 2. LogBluetoothSessionStart
+ * 3. LogA2dpSession
+ * 4. LogBluetoothSessionEnd
+ * 5. LogBluetoothSessionStart
+ * 6. LogA2dpSession
+ * 7. LogBluetoothSessionEnd
+ * 8. WriteString
+ *
+ */
+TEST_F(BluetoothMetricsLoggerTest, A2DPSessionTwoUpdatesSeparatedbyEndTest) {
+  /* Same metrics from BluetoothA2DPSessionMetricsTest.TestUpdateNormal */
+  A2dpSessionMetrics metrics1;
+  metrics1.audio_duration_ms = 10;
+  metrics1.media_timer_min_ms = 10;
+  metrics1.media_timer_max_ms = 100;
+  metrics1.media_timer_avg_ms = 50;
+  metrics1.total_scheduling_count = 50;
+  metrics1.buffer_overruns_max_count = 70;
+  metrics1.buffer_underruns_average = 80;
+  metrics1.buffer_underruns_count = 1200;
+  metrics1.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC;
+  DeviceInfo* info = MakeDeviceInfo(
+      BTM_COD_MAJOR_AUDIO_TEST,
+      DeviceInfo_DeviceType::DeviceInfo_DeviceType_DEVICE_TYPE_BREDR);
+  A2DPSession* session =
+      MakeA2DPSession(metrics1, A2dpSourceCodec::A2DP_SOURCE_CODEC_SBC);
+  bt_sessions_.push_back(MakeBluetoothSession(
+      1,
+      BluetoothSession_ConnectionTechnologyType::
+          BluetoothSession_ConnectionTechnologyType_CONNECTION_TECHNOLOGY_TYPE_BREDR,
+      BluetoothSession_DisconnectReasonType::
+          BluetoothSession_DisconnectReasonType_UNKNOWN,
+      info, nullptr, session));
+  UpdateLog();
+  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
+      system_bt_osi::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
+  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionDeviceInfo(
+      BTM_COD_MAJOR_AUDIO_TEST, system_bt_osi::DEVICE_TYPE_BREDR);
+  BluetoothMetricsLogger::GetInstance()->LogA2dpSession(metrics1);
+  sleep_ms(1000);
+  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
+      system_bt_osi::DISCONNECT_REASON_UNKNOWN, 0);
+  std::string msg_str;
+  BluetoothMetricsLogger::GetInstance()->WriteString(&msg_str);
+  EXPECT_THAT(msg_str, StrEq(bt_log_str_));
+  ClearLog();
+  A2dpSessionMetrics metrics2;
+  metrics2.audio_duration_ms = 25;
+  metrics2.media_timer_min_ms = 25;
+  metrics2.media_timer_max_ms = 200;
+  metrics2.media_timer_avg_ms = 100;
+  metrics2.total_scheduling_count = 50;
+  metrics2.buffer_overruns_max_count = 80;
+  metrics2.buffer_underruns_average = 130;
+  metrics2.buffer_underruns_count = 2400;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+  session = MakeA2DPSession(metrics2, A2dpSourceCodec::A2DP_SOURCE_CODEC_AAC);
+  bt_sessions_.push_back(MakeBluetoothSession(
+      1,
+      BluetoothSession_ConnectionTechnologyType::
+          BluetoothSession_ConnectionTechnologyType_CONNECTION_TECHNOLOGY_TYPE_BREDR,
+      BluetoothSession_DisconnectReasonType::
+          BluetoothSession_DisconnectReasonType_UNKNOWN,
+      nullptr, nullptr, session));
+  UpdateLog();
+  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionStart(
+      system_bt_osi::CONNECTION_TECHNOLOGY_TYPE_BREDR, 0);
+  sleep_ms(1000);
+  BluetoothMetricsLogger::GetInstance()->LogA2dpSession(metrics2);
+  BluetoothMetricsLogger::GetInstance()->LogBluetoothSessionEnd(
+      system_bt_osi::DISCONNECT_REASON_UNKNOWN, 0);
+  msg_str.clear();
+  BluetoothMetricsLogger::GetInstance()->WriteString(&msg_str);
+  EXPECT_THAT(msg_str, StrEq(bt_log_str_));
+}
+
+/*
  * Test Case 1: A2DPSessionOnlyTest
  *
  * 1. Create Instance
@@ -700,10 +811,14 @@
   metrics2.buffer_underruns_count = 2400;
   metrics_sum.buffer_underruns_average = 113.33333333;
   metrics_sum.buffer_underruns_count = 3600;
+  metrics1.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+  metrics_sum.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC;
   DeviceInfo* info = MakeDeviceInfo(
       BTM_COD_MAJOR_AUDIO_TEST,
       DeviceInfo_DeviceType::DeviceInfo_DeviceType_DEVICE_TYPE_BREDR);
-  A2DPSession* session = MakeA2DPSession(metrics_sum);
+  A2DPSession* session =
+      MakeA2DPSession(metrics_sum, A2dpSourceCodec::A2DP_SOURCE_CODEC_SBC);
   bt_sessions_.push_back(MakeBluetoothSession(
       1,
       BluetoothSession_ConnectionTechnologyType::
@@ -762,6 +877,9 @@
   metrics2.buffer_underruns_count = 2400;
   metrics_sum.buffer_underruns_average = 113.33333333;
   metrics_sum.buffer_underruns_count = 3600;
+  metrics1.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC;
+  metrics2.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_AAC;
+  metrics_sum.codec_index = BTAV_A2DP_CODEC_INDEX_SOURCE_SBC;
   DeviceInfo* info = MakeDeviceInfo(
       BTM_COD_MAJOR_AUDIO_TEST,
       DeviceInfo_DeviceType::DeviceInfo_DeviceType_DEVICE_TYPE_BREDR);
@@ -785,7 +903,8 @@
   info = MakeDeviceInfo(
       BTM_COD_MAJOR_AUDIO_TEST,
       DeviceInfo_DeviceType::DeviceInfo_DeviceType_DEVICE_TYPE_BREDR);
-  A2DPSession* session = MakeA2DPSession(metrics_sum);
+  A2DPSession* session =
+      MakeA2DPSession(metrics_sum, A2dpSourceCodec::A2DP_SOURCE_CODEC_SBC);
   bt_sessions_.push_back(MakeBluetoothSession(
       1,
       BluetoothSession_ConnectionTechnologyType::
diff --git a/packet/tests/avrcp/avrcp_test_packets.h b/packet/tests/avrcp/avrcp_test_packets.h
index e7e820b..13994d0 100644
--- a/packet/tests/avrcp/avrcp_test_packets.h
+++ b/packet/tests/avrcp/avrcp_test_packets.h
@@ -105,6 +105,10 @@
     0x03, 0x48, 0x00, 0x00, 0x19, 0x58, 0x31, 0x00,
     0x00, 0x05, 0x0d, 0x00, 0x00, 0x00, 0x00};
 
+// AVRCP Register Notification without any parameter
+std::vector<uint8_t> register_notification_invalid = {
+    0x03, 0x48, 0x00, 0x00, 0x19, 0x58, 0x31, 0x00, 0x00, 0x05};
+
 // AVRCP Interim Playback Status Notification
 std::vector<uint8_t> interim_play_status_notification = {
     0x0f, 0x48, 0x00, 0x00, 0x19, 0x58, 0x31, 0x00, 0x00, 0x02, 0x01, 0x00};
@@ -297,8 +301,12 @@
     0x72, 0x74, 0x69, 0x73, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00, 0x6a, 0x00,
     0x0a, 0x54, 0x65, 0x73, 0x74, 0x20, 0x41, 0x6c, 0x62, 0x75, 0x6d};
 
-// AVRCP Set Addressed Player Request with player_id = 1
+// AVRCP Set Addressed Player Request with player_id = 0
 std::vector<uint8_t> set_addressed_player_request = {
+    0x00, 0x48, 0x00, 0x00, 0x19, 0x58, 0x60, 0x00, 0x00, 0x02, 0x00, 0x00};
+
+// AVRCP Set Addressed Player Request with player_id = 1
+std::vector<uint8_t> set_addressed_player_id_1_request = {
     0x00, 0x48, 0x00, 0x00, 0x19, 0x58, 0x60, 0x00, 0x00, 0x02, 0x00, 0x01};
 
 // AVRCP Set Addressed Player Response with status success
diff --git a/packet/tests/avrcp/set_addressed_player_packet_test.cc b/packet/tests/avrcp/set_addressed_player_packet_test.cc
index 24d674a..a6ced9e 100644
--- a/packet/tests/avrcp/set_addressed_player_packet_test.cc
+++ b/packet/tests/avrcp/set_addressed_player_packet_test.cc
@@ -39,7 +39,7 @@
   auto test_packet =
       TestSetAddrPlayerPacket::Make(set_addressed_player_request);
 
-  ASSERT_EQ(test_packet->GetPlayerId(), 0x0001u);
+  ASSERT_EQ(test_packet->GetPlayerId(), 0x0000u);
 }
 
 TEST(SetAddressedPlayerRequestTest, validTest) {
@@ -60,4 +60,4 @@
 }
 
 }  // namespace avrcp
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/profile/avrcp/Android.bp b/profile/avrcp/Android.bp
index b10a56d..1e27861 100644
--- a/profile/avrcp/Android.bp
+++ b/profile/avrcp/Android.bp
@@ -4,6 +4,7 @@
     host_supported: true,
     include_dirs: [
         "system/bt",
+        "system/bt/btcore/include",
         "system/bt/internal_include",
         "system/bt/stack/include",
     ],
@@ -25,6 +26,7 @@
     host_supported: true,
     include_dirs: [
         "system/bt",
+        "system/bt/btcore/include",
         "system/bt/internal_include",
         "system/bt/stack/include",
     ],
diff --git a/profile/avrcp/BUILD.gn b/profile/avrcp/BUILD.gn
index 9144bb8..c37e0a7 100644
--- a/profile/avrcp/BUILD.gn
+++ b/profile/avrcp/BUILD.gn
@@ -22,6 +22,7 @@
 
   include_dirs = [
     "//",
+    "//btcore/include",
     "//internal_include",
     "//stack/include",
     "//profile/avrcp",
diff --git a/profile/avrcp/avrcp_message_converter.h b/profile/avrcp/avrcp_message_converter.h
index 495009c..8de704b 100644
--- a/profile/avrcp/avrcp_message_converter.h
+++ b/profile/avrcp/avrcp_message_converter.h
@@ -41,7 +41,7 @@
 
   const std::vector<uint8_t>& GetData() { return *data_; };
 
-  virtual std::string ToString() const {
+  virtual std::string ToString() const override {
     std::stringstream ss;
     ss << "VectorPacket:" << std::endl;
     ss << "  â”” Payload =";
diff --git a/profile/avrcp/device.cc b/profile/avrcp/device.cc
index 2b2172d..8efadc8 100644
--- a/profile/avrcp/device.cc
+++ b/profile/avrcp/device.cc
@@ -18,6 +18,7 @@
 
 #include "connection_handler.h"
 #include "device.h"
+#include "stack_config.h"
 
 #include "packet/avrcp/avrcp_reject_packet.h"
 #include "packet/avrcp/general_reject_packet.h"
@@ -132,9 +133,9 @@
       // this currently since the current implementation only has one
       // player and the player will never change, but we need it for a
       // more complete implementation.
-      auto response =
-          SetAddressedPlayerResponseBuilder::MakeBuilder(Status::NO_ERROR);
-      send_message(label, false, std::move(response));
+      media_interface_->GetMediaPlayerList(base::Bind(
+          &Device::HandleSetAddressedPlayer, weak_ptr_factory_.GetWeakPtr(),
+          label, Packet::Specialize<SetAddressedPlayerRequest>(pkt)));
     } break;
 
     default: {
@@ -188,6 +189,14 @@
 
 void Device::HandleNotification(
     uint8_t label, const std::shared_ptr<RegisterNotificationRequest>& pkt) {
+  if (!pkt->IsValid()) {
+    DEVICE_LOG(ERROR) << __func__ << ": Request packet is not valid";
+    auto response = RejectBuilder::MakeBuilder(pkt->GetCommandPdu(),
+                                               Status::INVALID_PARAMETER);
+    send_message(label, false, std::move(response));
+    return;
+  }
+
   DEVICE_VLOG(4) << __func__ << ": event=" << pkt->GetEventRegistered();
 
   switch (pkt->GetEventRegistered()) {
@@ -233,7 +242,7 @@
       addr_player_changed_ = Notification(true, label);
       media_interface_->GetMediaPlayerList(
           base::Bind(&Device::AddressedPlayerNotificationResponse,
-                     weak_ptr_factory_.GetWeakPtr(), label, false));
+                     weak_ptr_factory_.GetWeakPtr(), label, true));
     } break;
 
     case Event::UIDS_CHANGED: {
@@ -366,6 +375,15 @@
     }
   }
 
+  if (curr_song_id == "") {
+    DEVICE_LOG(WARNING) << "Empty media ID";
+    uid = 0;
+    if (stack_config_get_interface()->get_pts_avrcp_test()) {
+      DEVICE_LOG(WARNING) << __func__ << ": pts test mode";
+      uid = 0xffffffffffffffff;
+    }
+  }
+
   auto response = RegisterNotificationResponseBuilder::MakeTrackChangedBuilder(
       interim, uid);
   send_message_cb_.Run(label, false, std::move(response));
@@ -462,12 +480,28 @@
 
   auto response =
       RegisterNotificationResponseBuilder::MakeAddressedPlayerBuilder(
-          true, curr_player, 0x0000);
+          interim, curr_player, 0x0000);
   send_message_cb_.Run(label, false, std::move(response));
 
   if (!interim) {
     active_labels_.erase(label);
     addr_player_changed_ = Notification(false, 0);
+    RejectNotification();
+  }
+}
+
+void Device::RejectNotification() {
+  DEVICE_VLOG(1) << __func__;
+  Notification* rejectNotification[] = {&play_status_changed_, &track_changed_,
+                                        &play_pos_changed_,
+                                        &now_playing_changed_};
+  for (int i = 0; i < 4; i++) {
+    uint8_t label = rejectNotification[i]->second;
+    auto response = RejectBuilder::MakeBuilder(
+        CommandPdu::REGISTER_NOTIFICATION, Status::ADDRESSED_PLAYER_CHANGED);
+    send_message_cb_.Run(label, false, std::move(response));
+    active_labels_.erase(label);
+    rejectNotification[i] = new Notification(false, 0);
   }
 }
 
@@ -586,8 +620,8 @@
 
   if (media_id == "") {
     DEVICE_VLOG(2) << "Could not find item";
-    auto response =
-        PlayItemResponseBuilder::MakeBuilder(Status::DOES_NOT_EXIST);
+    auto response = RejectBuilder::MakeBuilder(CommandPdu::PLAY_ITEM,
+                                               Status::DOES_NOT_EXIST);
     send_message(label, false, std::move(response));
     return;
   }
@@ -599,6 +633,24 @@
   send_message(label, false, std::move(response));
 }
 
+void Device::HandleSetAddressedPlayer(
+    uint8_t label, std::shared_ptr<SetAddressedPlayerRequest> pkt,
+    uint16_t curr_player, std::vector<MediaPlayerInfo> players) {
+  DEVICE_VLOG(2) << __func__ << ": PlayerId=" << pkt->GetPlayerId();
+
+  if (curr_player != pkt->GetPlayerId()) {
+    DEVICE_VLOG(2) << "Reject invalid addressed player ID";
+    auto response = RejectBuilder::MakeBuilder(CommandPdu::SET_ADDRESSED_PLAYER,
+                                               Status::INVALID_PLAYER_ID);
+    send_message(label, false, std::move(response));
+    return;
+  }
+
+  auto response =
+      SetAddressedPlayerResponseBuilder::MakeBuilder(Status::NO_ERROR);
+  send_message(label, false, std::move(response));
+}
+
 void Device::BrowseMessageReceived(uint8_t label,
                                    std::shared_ptr<BrowsePacket> pkt) {
   DEVICE_VLOG(1) << __func__ << ": pdu=" << pkt->GetPdu();
@@ -1091,16 +1143,12 @@
   DEVICE_VLOG(4) << __func__;
 
   if (available_players) {
-    // TODO (apanicke): Right now this isn't needed since we only show one
-    // player. Implement this in the future for a more complete
-    // implementation though.
+    HandleAvailablePlayerUpdate();
   }
 
   if (addressed_player) {
-    // TODO (apanicke): See above TODO.
+    HandleAddressedPlayerUpdate();
   }
-
-  CHECK(false) << "NEED TO IMPLEMENT";
 }
 
 void Device::HandleTrackUpdate() {
@@ -1175,6 +1223,37 @@
       play_pos_changed_.second, false));
 }
 
+void Device::HandleAvailablePlayerUpdate() {
+  DEVICE_VLOG(1) << __func__;
+
+  if (!avail_players_changed_.first) {
+    LOG(WARNING) << "Device is not registered for available player updates";
+    return;
+  }
+
+  auto response =
+      RegisterNotificationResponseBuilder::MakeAvailablePlayersBuilder(false);
+  send_message_cb_.Run(avail_players_changed_.second, false,
+                       std::move(response));
+
+  if (!avail_players_changed_.first) {
+    active_labels_.erase(avail_players_changed_.second);
+    avail_players_changed_ = Notification(false, 0);
+  }
+}
+
+void Device::HandleAddressedPlayerUpdate() {
+  DEVICE_VLOG(1) << __func__;
+  if (!addr_player_changed_.first) {
+    DEVICE_LOG(WARNING)
+        << "Device is not registered for addressed player updates";
+    return;
+  }
+  media_interface_->GetMediaPlayerList(base::Bind(
+      &Device::AddressedPlayerNotificationResponse,
+      weak_ptr_factory_.GetWeakPtr(), addr_player_changed_.second, false));
+}
+
 void Device::DeviceDisconnected() {
   DEVICE_LOG(INFO) << "Device was disconnected";
   play_pos_update_cb_.Cancel();
diff --git a/profile/avrcp/device.h b/profile/avrcp/device.h
index 6280905..1be19c0 100644
--- a/profile/avrcp/device.h
+++ b/profile/avrcp/device.h
@@ -35,6 +35,7 @@
 #include "packet/avrcp/get_total_number_of_items.h"
 #include "packet/avrcp/play_item.h"
 #include "packet/avrcp/register_notification_packet.h"
+#include "packet/avrcp/set_addressed_player.h"
 #include "packet/avrcp/set_browsed_player.h"
 #include "packet/avrcp/vendor_packet.h"
 #include "profile/avrcp/media_id_map.h"
@@ -160,7 +161,12 @@
       uint8_t label, std::shared_ptr<GetElementAttributesRequest> pkt,
       SongInfo info);
 
+  // AVAILABLE PLAYER CHANGED
+  virtual void HandleAvailablePlayerUpdate();
+
   // ADDRESSED PLAYER CHANGED
+  virtual void HandleAddressedPlayerUpdate();
+  virtual void RejectNotification();
   virtual void AddressedPlayerNotificationResponse(
       uint8_t label, bool interim, uint16_t curr_player,
       std::vector<MediaPlayerInfo> /* unused */);
@@ -216,6 +222,11 @@
   virtual void HandlePlayItem(uint8_t label,
                               std::shared_ptr<PlayItemRequest> request);
 
+  // SET ADDRESSED PLAYER
+  virtual void HandleSetAddressedPlayer(
+      uint8_t label, std::shared_ptr<SetAddressedPlayerRequest> request,
+      uint16_t curr_player, std::vector<MediaPlayerInfo> players);
+
   /********************
    * MESSAGE REQUESTS
    ********************/
diff --git a/profile/avrcp/tests/avrcp_device_test.cc b/profile/avrcp/tests/avrcp_device_test.cc
index 63bd208..56f03e4 100644
--- a/profile/avrcp/tests/avrcp_device_test.cc
+++ b/profile/avrcp/tests/avrcp_device_test.cc
@@ -26,6 +26,7 @@
 #include "avrcp_packet.h"
 #include "avrcp_test_helper.h"
 #include "device.h"
+#include "stack_config.h"
 #include "tests/avrcp/avrcp_test_packets.h"
 #include "tests/packet_test_helper.h"
 
@@ -45,6 +46,12 @@
 using ::testing::NiceMock;
 using ::testing::Return;
 
+bool get_pts_avrcp_test(void) { return false; }
+
+const stack_config_t interface = {
+    nullptr, get_pts_avrcp_test, nullptr, nullptr, nullptr, nullptr, nullptr,
+    nullptr};
+
 // TODO (apanicke): All the tests below are just basic positive unit tests.
 // Add more tests to increase code coverage.
 class AvrcpDeviceTest : public ::testing::Test {
@@ -706,6 +713,23 @@
 
   test_device->RegisterInterfaces(&interface, &a2dp_interface, nullptr);
 
+  MediaPlayerInfo info = {0, "Test Player", true};
+  std::vector<MediaPlayerInfo> list = {info};
+
+  EXPECT_CALL(interface, GetMediaPlayerList(_))
+      .WillRepeatedly(InvokeCb<0>(0, list));
+
+  auto set_addr_player_rej_rsp = RejectBuilder::MakeBuilder(
+      CommandPdu::SET_ADDRESSED_PLAYER, Status::INVALID_PLAYER_ID);
+
+  EXPECT_CALL(response_cb,
+              Call(1, false, matchPacket(std::move(set_addr_player_rej_rsp))))
+      .Times(1);
+
+  auto player_id_1_request =
+      TestAvrcpPacket::Make(set_addressed_player_id_1_request);
+  SendMessage(1, player_id_1_request);
+
   auto set_addr_player_rsp =
       SetAddressedPlayerResponseBuilder::MakeBuilder(Status::NO_ERROR);
 
@@ -1024,5 +1048,25 @@
   SendBrowseMessage(1, request);
 }
 
+TEST_F(AvrcpDeviceTest, invalidRegisterNotificationTest) {
+  MockMediaInterface interface;
+  NiceMock<MockA2dpInterface> a2dp_interface;
+
+  test_device->RegisterInterfaces(&interface, &a2dp_interface, nullptr);
+
+  auto reg_notif_rej_rsp = RejectBuilder::MakeBuilder(
+      CommandPdu::REGISTER_NOTIFICATION, Status::INVALID_PARAMETER);
+  EXPECT_CALL(response_cb,
+              Call(1, false, matchPacket(std::move(reg_notif_rej_rsp))))
+      .Times(1);
+
+  auto reg_notif_request = TestAvrcpPacket::Make(register_notification_invalid);
+  SendMessage(1, reg_notif_request);
+}
+
 }  // namespace avrcp
 }  // namespace bluetooth
+
+const stack_config_t* stack_config_get_interface(void) {
+  return &bluetooth::avrcp::interface;
+}
diff --git a/proto/bluetooth/metrics/bluetooth.proto b/proto/bluetooth/metrics/bluetooth.proto
index 67efac5..b1e28c3 100644
--- a/proto/bluetooth/metrics/bluetooth.proto
+++ b/proto/bluetooth/metrics/bluetooth.proto
@@ -133,6 +133,15 @@
   optional int32 tx_bytes = 2;
 }
 
+enum A2dpSourceCodec {
+  A2DP_SOURCE_CODEC_UNKNOWN = 0;
+  A2DP_SOURCE_CODEC_SBC = 1;
+  A2DP_SOURCE_CODEC_AAC = 2;
+  A2DP_SOURCE_CODEC_APTX = 3;
+  A2DP_SOURCE_CODEC_APTX_HD = 4;
+  A2DP_SOURCE_CODEC_LDAC = 5;
+}
+
 // Session information that gets logged for A2DP session.
 message A2DPSession {
   // Media timer in milliseconds.
@@ -158,6 +167,12 @@
 
   // Total audio time in this A2DP session
   optional int64 audio_duration_millis = 8;
+
+  // Audio codec used in this A2DP session in A2DP source role
+  optional A2dpSourceCodec source_codec = 9;
+
+  // Whether A2DP offload is enabled in this A2DP session
+  optional bool is_a2dp_offload = 10;
 }
 
 message PairEvent {
diff --git a/stack/Android.bp b/stack/Android.bp
index 76cb172..0c7897c 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -1,3 +1,9 @@
+crypto_toolbox_srcs = [
+    "crypto_toolbox/aes.cc",
+    "crypto_toolbox/aes_cmac.cc",
+    "crypto_toolbox/crypto_toolbox.cc",
+]
+
 // Bluetooth stack static library for target
 // ========================================================
 cc_library_static {
@@ -40,7 +46,7 @@
         "system/bt/bta/sys",
         "system/bt/utils/include",
     ],
-    srcs: [
+    srcs: crypto_toolbox_srcs + [
         "a2dp/a2dp_aac.cc",
         "a2dp/a2dp_aac_decoder.cc",
         "a2dp/a2dp_aac_encoder.cc",
@@ -155,14 +161,12 @@
         "sdp/sdp_main.cc",
         "sdp/sdp_server.cc",
         "sdp/sdp_utils.cc",
-        "smp/aes.cc",
         "smp/p_256_curvepara.cc",
         "smp/p_256_ecc_pp.cc",
         "smp/p_256_multprecision.cc",
         "smp/smp_act.cc",
         "smp/smp_api.cc",
         "smp/smp_br_main.cc",
-        "smp/smp_cmac.cc",
         "smp/smp_keys.cc",
         "smp/smp_l2c.cc",
         "smp/smp_main.cc",
@@ -182,7 +186,7 @@
     required: [
         "libldacBT_enc",
         "libldacBT_abr",
-    ]
+    ],
 }
 
 // Bluetooth stack unit tests for target
@@ -224,53 +228,53 @@
 }
 
 cc_test {
-  name: "net_test_stack_rfcomm",
-  defaults: ["fluoride_defaults"],
-  host_supported: true,
-  local_include_dirs: [
-      "include",
-      "btm",
-      "l2cap",
-      "smp",
-      "rfcomm",
-      "test/common",
-  ],
-  include_dirs: [
-      "system/bt",
-      "system/bt/internal_include",
-      "system/bt/btcore/include",
-      "system/bt/hci/include",
-      "system/bt/utils/include",
-  ],
-  srcs: [
-      "rfcomm/port_api.cc",
-      "rfcomm/port_rfc.cc",
-      "rfcomm/port_utils.cc",
-      "rfcomm/rfc_l2cap_if.cc",
-      "rfcomm/rfc_mx_fsm.cc",
-      "rfcomm/rfc_port_fsm.cc",
-      "rfcomm/rfc_port_if.cc",
-      "rfcomm/rfc_ts_frames.cc",
-      "rfcomm/rfc_utils.cc",
-      "test/common/mock_btm_layer.cc",
-      "test/common/mock_btu_layer.cc",
-      "test/common/mock_l2cap_layer.cc",
-      "test/common/stack_test_packet_utils.cc",
-      "test/rfcomm/stack_rfcomm_test.cc",
-      "test/rfcomm/stack_rfcomm_test_main.cc",
-      "test/rfcomm/stack_rfcomm_test_utils.cc",
-      "test/rfcomm/stack_rfcomm_test_utils_test.cc",
-  ],
-  shared_libs: [
-      "libcutils",
-      "libprotobuf-cpp-lite",
-  ],
-  static_libs: [
-      "liblog",
-      "libgmock",
-      "libosi",
-      "libbt-protos-lite",
-  ],
+    name: "net_test_stack_rfcomm",
+    defaults: ["fluoride_defaults"],
+    host_supported: true,
+    local_include_dirs: [
+        "include",
+        "btm",
+        "l2cap",
+        "smp",
+        "rfcomm",
+        "test/common",
+    ],
+    include_dirs: [
+        "system/bt",
+        "system/bt/internal_include",
+        "system/bt/btcore/include",
+        "system/bt/hci/include",
+        "system/bt/utils/include",
+    ],
+    srcs: [
+        "rfcomm/port_api.cc",
+        "rfcomm/port_rfc.cc",
+        "rfcomm/port_utils.cc",
+        "rfcomm/rfc_l2cap_if.cc",
+        "rfcomm/rfc_mx_fsm.cc",
+        "rfcomm/rfc_port_fsm.cc",
+        "rfcomm/rfc_port_if.cc",
+        "rfcomm/rfc_ts_frames.cc",
+        "rfcomm/rfc_utils.cc",
+        "test/common/mock_btm_layer.cc",
+        "test/common/mock_btu_layer.cc",
+        "test/common/mock_l2cap_layer.cc",
+        "test/common/stack_test_packet_utils.cc",
+        "test/rfcomm/stack_rfcomm_test.cc",
+        "test/rfcomm/stack_rfcomm_test_main.cc",
+        "test/rfcomm/stack_rfcomm_test_utils.cc",
+        "test/rfcomm/stack_rfcomm_test_utils_test.cc",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "liblog",
+        "libgmock",
+        "libosi",
+        "libbt-protos-lite",
+    ],
 }
 
 // Bluetooth stack smp unit tests for target
@@ -291,12 +295,15 @@
         "system/bt/hci/include",
         "system/bt/utils/include",
     ],
-    srcs: [
+    srcs: crypto_toolbox_srcs + [
         "smp/smp_keys.cc",
-        "smp/aes.cc",
+        "smp/p_256_curvepara.cc",
+        "smp/p_256_ecc_pp.cc",
+        "smp/p_256_multprecision.cc",
         "smp/smp_api.cc",
         "smp/smp_main.cc",
         "smp/smp_utils.cc",
+        "test/crypto_toolbox_test.cc",
         "test/stack_smp_test.cc",
     ],
     shared_libs: [
@@ -309,7 +316,6 @@
     ],
 }
 
-
 // Bluetooth stack multi-advertising unit tests for target
 // ========================================================
 cc_test {
diff --git a/stack/BUILD.gn b/stack/BUILD.gn
index 7920f70..a9926e1 100644
--- a/stack/BUILD.gn
+++ b/stack/BUILD.gn
@@ -14,6 +14,22 @@
 #  limitations under the License.
 #
 
+static_library("crypto_toolbox") {
+  sources = [
+    "crypto_toolbox/crypto_toolbox.cc",
+    "crypto_toolbox/aes.cc",
+    "crypto_toolbox/aes_cmac.cc",
+  ]
+
+  include_dirs = [
+    "//",
+  ]
+
+  deps = [
+    "//third_party/libchrome:base",
+  ]
+}
+
 static_library("stack") {
   sources = [
     "a2dp/a2dp_aac.cc",
@@ -130,14 +146,12 @@
     "sdp/sdp_main.cc",
     "sdp/sdp_server.cc",
     "sdp/sdp_utils.cc",
-    "smp/aes.cc",
     "smp/p_256_curvepara.cc",
     "smp/p_256_ecc_pp.cc",
     "smp/p_256_multprecision.cc",
     "smp/smp_act.cc",
     "smp/smp_api.cc",
     "smp/smp_br_main.cc",
-    "smp/smp_cmac.cc",
     "smp/smp_keys.cc",
     "smp/smp_l2c.cc",
     "smp/smp_main.cc",
@@ -180,6 +194,7 @@
   ]
 
   deps = [
+    ":crypto_toolbox",
     "//types",
     "//third_party/libchrome:base",
     "//third_party/libldac:libldacBT_enc",
@@ -236,6 +251,73 @@
   ]
 }
 
+executable("net_test_stack_crypto_toolbox") {
+  testonly = true
+  sources = [
+    "test/crypto_toolbox_test.cc",
+  ]
+
+  include_dirs = [
+    "//",
+  ]
+
+  deps = [
+    ":crypto_toolbox",
+    "//third_party/googletest:gmock_main",
+    "//third_party/libchrome:base",
+  ]
+}
+
+executable("net_test_stack_smp") {
+  testonly = true
+  sources = [
+        "smp/smp_keys.cc",
+        "smp/smp_api.cc",
+        "smp/smp_main.cc",
+        "smp/smp_utils.cc",
+        "test/stack_smp_test.cc",
+  ]
+
+  include_dirs = [
+    "//",
+    "//internal_include",
+    "//btcore/include",
+    "//hci/include",
+    "//utils/include",
+    "//bta/include",
+    "//bta/sys",
+    "//btcore/include",
+    "//embdrv/sbc/encoder/include",
+    "//hci/include",
+    "//internal_include",
+    "//stack/a2dp",
+    "//stack/l2cap",
+    "//stack/btm",
+    "//stack/include",
+    "//third_party/tinyxml2",
+    "//udrv/include",
+    "//utils/include",
+    "//vnd/include"
+  ]
+
+  libs = [
+    "-ldl",
+    "-lpthread",
+    "-lresolv",
+    "-lrt",
+    "-lz",
+    "-latomic",
+  ]
+
+  deps = [
+    ":crypto_toolbox",
+    "//osi",
+    "//types",
+    "//third_party/googletest:gmock_main",
+    "//third_party/libchrome:base",
+  ]
+}
+
 executable("net_test_stack_multi_adv") {
   testonly = true
   sources = [
diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc
index 196b5f8..47856fe 100644
--- a/stack/a2dp/a2dp_codec_config.cc
+++ b/stack/a2dp/a2dp_codec_config.cc
@@ -596,8 +596,10 @@
     }
 
     // In offload mode, disable the codecs based on the property
-    if (a2dp_offload_status && (offload_codec_support[i] != true))
+    if ((codec_index < BTAV_A2DP_CODEC_INDEX_SOURCE_MAX) &&
+        a2dp_offload_status && (offload_codec_support[i] != true)) {
       codec_priority = BTAV_A2DP_CODEC_PRIORITY_DISABLED;
+    }
 
     A2dpCodecConfig* codec_config =
         A2dpCodecConfig::createCodec(codec_index, codec_priority);
diff --git a/stack/a2dp/a2dp_vendor_ldac.cc b/stack/a2dp/a2dp_vendor_ldac.cc
index 266db81..f8238b6 100644
--- a/stack/a2dp/a2dp_vendor_ldac.cc
+++ b/stack/a2dp/a2dp_vendor_ldac.cc
@@ -910,6 +910,7 @@
         codec_capability_.sample_rate = codec_user_config_.sample_rate;
         codec_config_.sample_rate = codec_user_config_.sample_rate;
       }
+      break;
     case BTAV_A2DP_CODEC_SAMPLE_RATE_16000:
     case BTAV_A2DP_CODEC_SAMPLE_RATE_24000:
     case BTAV_A2DP_CODEC_SAMPLE_RATE_NONE:
diff --git a/stack/avrc/avrc_pars_ct.cc b/stack/avrc/avrc_pars_ct.cc
index d48b721..ce8f1f6 100644
--- a/stack/avrc/avrc_pars_ct.cc
+++ b/stack/avrc/avrc_pars_ct.cc
@@ -132,6 +132,9 @@
       break;
 
     case AVRC_EVT_ADDR_PLAYER_CHANGE:
+      BE_STREAM_TO_UINT16(p_rsp->param.addr_player.player_id, p_stream);
+      BE_STREAM_TO_UINT16(p_rsp->param.addr_player.uid_counter, p_stream);
+
       break;
 
     case AVRC_EVT_UIDS_CHANGE:
diff --git a/stack/btm/btm_acl.cc b/stack/btm/btm_acl.cc
index 11c5a48..ee2530a 100644
--- a/stack/btm/btm_acl.cc
+++ b/stack/btm/btm_acl.cc
@@ -31,6 +31,8 @@
  *
  *****************************************************************************/
 
+#define LOG_TAG "btm_acl"
+
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -48,6 +50,7 @@
 #include "hcidefs.h"
 #include "hcimsgs.h"
 #include "l2c_int.h"
+#include "osi/include/log.h"
 #include "osi/include/osi.h"
 
 static void btm_read_remote_features(uint16_t handle);
@@ -540,7 +543,10 @@
   tBTM_STATUS status;
   tBTM_PM_MODE pwr_mode;
   tBTM_PM_PWR_MD settings;
-  VLOG(1) << __func__ << " BDA: " << remote_bd_addr;
+
+  LOG_INFO(LOG_TAG, "%s: peer %s new_role=0x%x p_cb=%p p_switch_role_cb=%p",
+           __func__, remote_bd_addr.ToString().c_str(), new_role, p_cb,
+           btm_cb.devcb.p_switch_role_cb);
 
   /* Make sure the local device supports switching */
   if (!controller_get_interface()->supports_master_slave_role_switch())
diff --git a/stack/btm/btm_ble.cc b/stack/btm/btm_ble.cc
index 9edfebb..7a9a1e4 100644
--- a/stack/btm/btm_ble.cc
+++ b/stack/btm/btm_ble.cc
@@ -42,11 +42,8 @@
 #include "l2c_int.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
-#include "smp_api.h"
+#include "stack/crypto_toolbox/crypto_toolbox.h"
 
-extern bool aes_cipher_msg_auth_code(BT_OCTET16 key, uint8_t* input,
-                                     uint16_t length, uint16_t tlen,
-                                     uint8_t* p_signature);
 extern void gatt_notify_phy_updated(uint8_t status, uint16_t handle,
                                     uint8_t tx_phy, uint8_t rx_phy);
 extern void btm_ble_advertiser_notify_terminated_legacy(
@@ -184,8 +181,7 @@
         break;
 
       case BTM_BLE_KEY_TYPE_ER:
-        memcpy(p_devcb->ble_encryption_key_value, p_key->er,
-               sizeof(BT_OCTET16));
+        p_devcb->ble_encryption_key_value = p_key->er;
         break;
 
       default:
@@ -195,54 +191,16 @@
   }
 }
 
-/*******************************************************************************
- *
- * Function         BTM_GetDeviceEncRoot
- *
- * Description      This function is called to read the local device encryption
- *                  root.
- *
- * Returns          void
- *                  the local device ER is copied into ble_encr_key_value
- *
- ******************************************************************************/
-void BTM_GetDeviceEncRoot(BT_OCTET16 ble_encr_key_value) {
-  BTM_TRACE_DEBUG("%s", __func__);
-  memcpy(ble_encr_key_value, btm_cb.devcb.ble_encryption_key_value,
-         BT_OCTET16_LEN);
+/** Returns local device encryption root (ER) */
+const Octet16& BTM_GetDeviceEncRoot() {
+  return btm_cb.devcb.ble_encryption_key_value;
 }
 
-/*******************************************************************************
- *
- * Function         BTM_GetDeviceIDRoot
- *
- * Description      This function is called to read the local device identity
- *                  root.
- *
- * Returns          void
- *                  the local device IR is copied into irk
- *
- ******************************************************************************/
-void BTM_GetDeviceIDRoot(BT_OCTET16 irk) {
-  BTM_TRACE_DEBUG("BTM_GetDeviceIDRoot ");
+/** Returns local device identity root (IR). */
+const Octet16& BTM_GetDeviceIDRoot() { return btm_cb.devcb.id_keys.irk; }
 
-  memcpy(irk, btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN);
-}
-
-/*******************************************************************************
- *
- * Function         BTM_GetDeviceDHK
- *
- * Description      This function is called to read the local device DHK.
- *
- * Returns          void
- *                  the local device DHK is copied into dhk
- *
- ******************************************************************************/
-void BTM_GetDeviceDHK(BT_OCTET16 dhk) {
-  BTM_TRACE_DEBUG("BTM_GetDeviceDHK");
-  memcpy(dhk, btm_cb.devcb.id_keys.dhk, BT_OCTET16_LEN);
-}
+/** Return local device DHK. */
+const Octet16& BTM_GetDeviceDHK() { return btm_cb.devcb.id_keys.dhk; }
 
 /*******************************************************************************
  *
@@ -475,8 +433,8 @@
   memset(&oob, 0, sizeof(tSMP_SC_OOB_DATA));
 
   oob.peer_oob_data.present = true;
-  memcpy(&oob.peer_oob_data.randomizer, p_r, BT_OCTET16_LEN);
-  memcpy(&oob.peer_oob_data.commitment, p_c, BT_OCTET16_LEN);
+  memcpy(&oob.peer_oob_data.randomizer, p_r, OCTET16_LEN);
+  memcpy(&oob.peer_oob_data.commitment, p_c, OCTET16_LEN);
   oob.peer_oob_data.addr_rcvd_from.type = p_dev_rec->ble.ble_addr_type;
   oob.peer_oob_data.addr_rcvd_from.bda = bd_addr;
 
@@ -795,6 +753,7 @@
 tBTM_STATUS BTM_SetBleDataLength(const RawAddress& bd_addr,
                                  uint16_t tx_pdu_length) {
   tACL_CONN* p_acl = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE);
+  uint16_t tx_time = BTM_BLE_DATA_TX_TIME_MAX_LEGACY;
 
   if (p_acl == NULL) {
     BTM_TRACE_ERROR("%s: Wrong mode: no LE link exist or LE not supported",
@@ -819,9 +778,10 @@
   else if (tx_pdu_length < BTM_BLE_DATA_SIZE_MIN)
     tx_pdu_length = BTM_BLE_DATA_SIZE_MIN;
 
-  /* always set the TxTime to be max, as controller does not care for now */
-  btsnd_hcic_ble_set_data_length(p_acl->hci_handle, tx_pdu_length,
-                                 BTM_BLE_DATA_TX_TIME_MAX);
+  if (controller_get_interface()->get_bt_version()->hci_version >= HCI_PROTO_VERSION_5_0)
+    tx_time = BTM_BLE_DATA_TX_TIME_MAX;
+
+  btsnd_hcic_ble_set_data_length(p_acl->hci_handle, tx_pdu_length, tx_time);
 
   return BTM_SUCCESS;
 }
@@ -1177,7 +1137,7 @@
       if (op_code == HCI_BLE_RAND)
         params.param_len = BT_OCTET8_LEN;
       else
-        params.param_len = BT_OCTET16_LEN;
+        params.param_len = OCTET16_LEN;
 
       /* Fetch return info from HCI event message */
       memcpy(p_dest, p, params.param_len);
@@ -1278,7 +1238,6 @@
                          tBTM_LE_KEY_VALUE* p_keys, bool pass_to_application) {
   tBTM_SEC_DEV_REC* p_rec;
   tBTM_LE_EVT_DATA cb_data;
-  uint8_t i;
 
   BTM_TRACE_DEBUG("btm_sec_save_le_key key_type=0x%x pass_to_application=%d",
                   key_type, pass_to_application);
@@ -1292,7 +1251,7 @@
 
     switch (key_type) {
       case BTM_LE_KEY_PENC:
-        memcpy(p_rec->ble.keys.pltk, p_keys->penc_key.ltk, BT_OCTET16_LEN);
+        p_rec->ble.keys.pltk = p_keys->penc_key.ltk;
         memcpy(p_rec->ble.keys.rand, p_keys->penc_key.rand, BT_OCTET8_LEN);
         p_rec->ble.keys.sec_level = p_keys->penc_key.sec_level;
         p_rec->ble.keys.ediv = p_keys->penc_key.ediv;
@@ -1309,12 +1268,7 @@
         break;
 
       case BTM_LE_KEY_PID:
-        for (i = 0; i < BT_OCTET16_LEN; i++) {
-          p_rec->ble.keys.irk[i] = p_keys->pid_key.irk[i];
-        }
-
-        // memcpy( p_rec->ble.keys.irk, p_keys->pid_key, BT_OCTET16_LEN); todo
-        // will crash the system
+        p_rec->ble.keys.irk = p_keys->pid_key.irk;
         p_rec->ble.static_addr = p_keys->pid_key.static_addr;
         p_rec->ble.static_addr_type = p_keys->pid_key.addr_type;
         p_rec->ble.key_type |= BTM_LE_KEY_PID;
@@ -1330,7 +1284,7 @@
         break;
 
       case BTM_LE_KEY_PCSRK:
-        memcpy(p_rec->ble.keys.pcsrk, p_keys->pcsrk_key.csrk, BT_OCTET16_LEN);
+        p_rec->ble.keys.pcsrk = p_keys->pcsrk_key.csrk;
         p_rec->ble.keys.srk_sec_level = p_keys->pcsrk_key.sec_level;
         p_rec->ble.keys.counter = p_keys->pcsrk_key.counter;
         p_rec->ble.key_type |= BTM_LE_KEY_PCSRK;
@@ -1348,7 +1302,7 @@
         break;
 
       case BTM_LE_KEY_LENC:
-        memcpy(p_rec->ble.keys.lltk, p_keys->lenc_key.ltk, BT_OCTET16_LEN);
+        p_rec->ble.keys.lltk = p_keys->lenc_key.ltk;
         p_rec->ble.keys.div = p_keys->lenc_key.div; /* update DIV */
         p_rec->ble.keys.sec_level = p_keys->lenc_key.sec_level;
         p_rec->ble.keys.key_size = p_keys->lenc_key.key_size;
@@ -1362,7 +1316,7 @@
         break;
 
       case BTM_LE_KEY_LCSRK: /* local CSRK has been delivered */
-        memcpy(p_rec->ble.keys.lcsrk, p_keys->lcsrk_key.csrk, BT_OCTET16_LEN);
+        p_rec->ble.keys.lcsrk = p_keys->lcsrk_key.csrk;
         p_rec->ble.keys.div = p_keys->lcsrk_key.div; /* update DIV */
         p_rec->ble.keys.local_csrk_sec_level = p_keys->lcsrk_key.sec_level;
         p_rec->ble.keys.local_counter = p_keys->lcsrk_key.counter;
@@ -1601,7 +1555,6 @@
 void btm_ble_ltk_request(uint16_t handle, uint8_t rand[8], uint16_t ediv) {
   tBTM_CB* p_cb = &btm_cb;
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev_by_handle(handle);
-  BT_OCTET8 dummy_stk = {0};
 
   BTM_TRACE_DEBUG("btm_ble_ltk_request");
 
@@ -1610,23 +1563,17 @@
   memcpy(p_cb->enc_rand, rand, BT_OCTET8_LEN);
 
   if (p_dev_rec != NULL) {
-    if (!smp_proc_ltk_request(p_dev_rec->bd_addr))
-      btm_ble_ltk_request_reply(p_dev_rec->bd_addr, false, dummy_stk);
+    if (!smp_proc_ltk_request(p_dev_rec->bd_addr)) {
+      btm_ble_ltk_request_reply(p_dev_rec->bd_addr, false, Octet16{0});
+    }
   }
 }
 
-/*******************************************************************************
- *
- * Function         btm_ble_start_encrypt
- *
- * Description      This function is called to start LE encryption.
- *
- *
- * Returns          BTM_SUCCESS if encryption was started successfully
- *
- ******************************************************************************/
+/** This function is called to start LE encryption.
+ * Returns BTM_SUCCESS if encryption was started successfully
+ */
 tBTM_STATUS btm_ble_start_encrypt(const RawAddress& bda, bool use_stk,
-                                  BT_OCTET16 stk) {
+                                  Octet16* p_stk) {
   tBTM_CB* p_cb = &btm_cb;
   tBTM_SEC_DEV_REC* p_rec = btm_find_dev(bda);
   BT_OCTET8 dummy_rand = {0};
@@ -1646,7 +1593,7 @@
   p_cb->enc_handle = p_rec->ble_hci_handle;
 
   if (use_stk) {
-    btsnd_hcic_ble_start_enc(p_rec->ble_hci_handle, dummy_rand, 0, stk);
+    btsnd_hcic_ble_start_enc(p_rec->ble_hci_handle, dummy_rand, 0, *p_stk);
   } else if (p_rec->ble.key_type & BTM_LE_KEY_PENC) {
     btsnd_hcic_ble_start_enc(p_rec->ble_hci_handle, p_rec->ble.keys.rand,
                              p_rec->ble.keys.ediv, p_rec->ble.keys.pltk);
@@ -1716,7 +1663,7 @@
  *
  ******************************************************************************/
 void btm_ble_ltk_request_reply(const RawAddress& bda, bool use_stk,
-                               BT_OCTET16 stk) {
+                               const Octet16& stk) {
   tBTM_SEC_DEV_REC* p_rec = btm_find_dev(bda);
   tBTM_CB* p_cb = &btm_cb;
 
@@ -2016,9 +1963,20 @@
  *
  *****************************************************************************/
 void btm_ble_create_ll_conn_complete(uint8_t status) {
-  if (status != HCI_SUCCESS) {
-    btm_ble_set_conn_st(BLE_CONN_IDLE);
-    btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, NULL, status);
+  if (status == HCI_SUCCESS) return;
+
+  btm_ble_set_conn_st(BLE_CONN_IDLE);
+  btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, NULL, status);
+
+  LOG(WARNING) << "LE Create Connection attempt failed, status="
+               << loghex(status);
+
+  if (status == HCI_ERR_COMMAND_DISALLOWED) {
+    /* There is already either direct connect, or whitelist connection
+     * pending, but we don't know which one, or to which state should we
+     * transition now. This can be triggered only in case of rare race
+     * condition. Crash to recover. */
+    LOG(FATAL) << "LE Create Connection - command disallowed";
   }
 }
 /*****************************************************************************
@@ -2166,45 +2124,42 @@
   tBTM_SEC_DEV_REC* p_rec = btm_find_dev(bd_addr);
 
   BTM_TRACE_DEBUG("%s", __func__);
-  bool ret = false;
   if (p_rec == NULL) {
     BTM_TRACE_ERROR("%s-data signing can not be done from unknown device",
                     __func__);
-  } else {
-    uint8_t* p_mac = (uint8_t*)signature;
-    uint8_t* pp;
-    uint8_t* p_buf = (uint8_t*)osi_malloc(len + 4);
-
-    BTM_TRACE_DEBUG("%s-Start to generate Local CSRK", __func__);
-    pp = p_buf;
-    /* prepare plain text */
-    if (p_text) {
-      memcpy(p_buf, p_text, len);
-      pp = (p_buf + len);
-    }
-
-    UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter);
-    UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter);
-
-    ret = aes_cipher_msg_auth_code(p_rec->ble.keys.lcsrk, p_buf,
-                                   (uint16_t)(len + 4), BTM_CMAC_TLEN_SIZE,
-                                   p_mac);
-    if (ret) {
-      btm_ble_increment_sign_ctr(bd_addr, true);
-    }
-
-    BTM_TRACE_DEBUG("%s p_mac = %d", __func__, p_mac);
-    BTM_TRACE_DEBUG(
-        "p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = "
-        "0x%02x",
-        *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3));
-    BTM_TRACE_DEBUG(
-        "p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = "
-        "0x%02x",
-        *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7));
-    osi_free(p_buf);
+    return false;
   }
-  return ret;
+
+  uint8_t* p_mac = (uint8_t*)signature;
+  uint8_t* pp;
+  uint8_t* p_buf = (uint8_t*)osi_malloc(len + 4);
+
+  BTM_TRACE_DEBUG("%s-Start to generate Local CSRK", __func__);
+  pp = p_buf;
+  /* prepare plain text */
+  if (p_text) {
+    memcpy(p_buf, p_text, len);
+    pp = (p_buf + len);
+  }
+
+  UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter);
+  UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter);
+
+  crypto_toolbox::aes_cmac(p_rec->ble.keys.lcsrk, p_buf, (uint16_t)(len + 4),
+                           BTM_CMAC_TLEN_SIZE, p_mac);
+  btm_ble_increment_sign_ctr(bd_addr, true);
+
+  BTM_TRACE_DEBUG("%s p_mac = %d", __func__, p_mac);
+  BTM_TRACE_DEBUG(
+      "p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = "
+      "0x%02x",
+      *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3));
+  BTM_TRACE_DEBUG(
+      "p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = "
+      "0x%02x",
+      *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7));
+  osi_free(p_buf);
+  return true;
 }
 
 /*******************************************************************************
@@ -2238,12 +2193,11 @@
     BTM_TRACE_DEBUG("%s rcv_cnt=%d >= expected_cnt=%d", __func__, counter,
                     p_rec->ble.keys.counter);
 
-    if (aes_cipher_msg_auth_code(p_rec->ble.keys.pcsrk, p_orig, len,
-                                 BTM_CMAC_TLEN_SIZE, p_mac)) {
-      if (memcmp(p_mac, p_comp, BTM_CMAC_TLEN_SIZE) == 0) {
-        btm_ble_increment_sign_ctr(bd_addr, false);
-        verified = true;
-      }
+    crypto_toolbox::aes_cmac(p_rec->ble.keys.pcsrk, p_orig, len,
+                             BTM_CMAC_TLEN_SIZE, p_mac);
+    if (memcmp(p_mac, p_comp, BTM_CMAC_TLEN_SIZE) == 0) {
+      btm_ble_increment_sign_ctr(bd_addr, false);
+      verified = true;
     }
   }
   return verified;
@@ -2377,16 +2331,7 @@
 /*******************************************************************************
  *  Utility functions for LE device IR/ER generation
  ******************************************************************************/
-/*******************************************************************************
- *
- * Function         btm_notify_new_key
- *
- * Description      This function is to notify application new keys have been
- *                  generated.
- *
- * Returns          void
- *
- ******************************************************************************/
+/** This function is to notify application new keys have been generated. */
 static void btm_notify_new_key(uint8_t key_type) {
   tBTM_BLE_LOCAL_KEYS* p_local_keys = NULL;
 
@@ -2414,112 +2359,67 @@
   }
 }
 
-/*******************************************************************************
- *
- * Function         btm_ble_process_irk
- *
- * Description      This function is called when IRK is generated, store it in
- *                  local control block.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btm_ble_process_irk(tSMP_ENC* p) {
-  BTM_TRACE_DEBUG("btm_ble_process_irk");
-  if (p && p->opcode == HCI_BLE_ENCRYPT) {
-    memcpy(btm_cb.devcb.id_keys.irk, p->param_buf, BT_OCTET16_LEN);
-    btm_notify_new_key(BTM_BLE_KEY_TYPE_ID);
+/** implementation of btm_ble_reset_id */
+static void btm_ble_reset_id_impl(const Octet16& rand1, const Octet16& rand2) {
+  /* Regenerate Identity Root */
+  btm_cb.devcb.id_keys.ir = rand1;
+  uint8_t btm_ble_dhk_pt = 0x03;
+
+  /* generate DHK= Eir({0x03, 0x00, 0x00 ...}) */
+  btm_cb.devcb.id_keys.dhk =
+      crypto_toolbox::aes_128(btm_cb.devcb.id_keys.ir, &btm_ble_dhk_pt, 1);
+
+  uint8_t btm_ble_irk_pt = 0x01;
+  /* IRK = D1(IR, 1) */
+  btm_cb.devcb.id_keys.irk =
+      crypto_toolbox::aes_128(btm_cb.devcb.id_keys.ir, &btm_ble_irk_pt, 1);
+
+  btm_notify_new_key(BTM_BLE_KEY_TYPE_ID);
 
 #if (BLE_PRIVACY_SPT == TRUE)
-    /* if privacy is enabled, new RPA should be calculated */
-    if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) {
-      btm_gen_resolvable_private_addr(base::Bind(&btm_gen_resolve_paddr_low));
-    }
-#endif
-  } else {
-    BTM_TRACE_ERROR("Generating IRK exception.");
+  /* if privacy is enabled, new RPA should be calculated */
+  if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) {
+    btm_gen_resolvable_private_addr(base::Bind(&btm_gen_resolve_paddr_low));
   }
+#endif
 
   /* proceed generate ER */
-  btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand1) {
-    memcpy(&btm_cb.devcb.ble_encryption_key_value[0], rand1, BT_OCTET8_LEN);
-
-    btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand2) {
-      memcpy(&btm_cb.devcb.ble_encryption_key_value[8], rand2, BT_OCTET8_LEN);
-      btm_notify_new_key(BTM_BLE_KEY_TYPE_ER);
-    }));
-
-  }));
+  btm_cb.devcb.ble_encryption_key_value = rand2;
+  btm_notify_new_key(BTM_BLE_KEY_TYPE_ER);
 }
 
-/*******************************************************************************
- *
- * Function         btm_ble_process_dhk
- *
- * Description      This function is called when DHK is calculated, store it in
- *                  local control block, and proceed to generate ER, a 128-bits
- *                  random number.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btm_ble_process_dhk(tSMP_ENC* p) {
-  uint8_t btm_ble_irk_pt = 0x01;
-  tSMP_ENC output;
+struct reset_id_data {
+  Octet16 rand1;
+  Octet16 rand2;
+};
 
-  BTM_TRACE_DEBUG("btm_ble_process_dhk");
-
-  if (p && p->opcode == HCI_BLE_ENCRYPT) {
-    memcpy(btm_cb.devcb.id_keys.dhk, p->param_buf, BT_OCTET16_LEN);
-    BTM_TRACE_DEBUG("BLE DHK generated.");
-
-    /* IRK = D1(IR, 1) */
-    if (!SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_irk_pt,
-                     1, &output)) {
-      /* reset all identity root related key */
-      memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS));
-    } else {
-      btm_ble_process_irk(&output);
-    }
-  } else {
-    /* reset all identity root related key */
-    memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS));
-  }
-}
-
-/*******************************************************************************
- *
- * Function         btm_ble_reset_id
- *
- * Description      This function is called to reset LE device identity.
- *
- * Returns          void
- *
- ******************************************************************************/
+/** This function is called to reset LE device identity. */
 void btm_ble_reset_id(void) {
   BTM_TRACE_DEBUG("btm_ble_reset_id");
 
-  /* Regenerate Identity Root*/
+  /* In order to reset identity, we need four random numbers. Make four nested
+   * calls to generate them first, then proceed to perform the actual reset in
+   * btm_ble_reset_id_impl. */
   btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) {
-    BTM_TRACE_DEBUG("btm_ble_process_ir1");
-    memcpy(btm_cb.devcb.id_keys.ir, rand, BT_OCTET8_LEN);
-
-    btsnd_hcic_ble_rand(base::Bind([](BT_OCTET8 rand) {
-      uint8_t btm_ble_dhk_pt = 0x03;
-      tSMP_ENC output;
-
-      BTM_TRACE_DEBUG("btm_ble_process_ir2");
-
-      /* remembering in control block */
-      memcpy(&btm_cb.devcb.id_keys.ir[8], rand, BT_OCTET8_LEN);
-      /* generate DHK= Eir({0x03, 0x00, 0x00 ...}) */
-
-      SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_dhk_pt, 1,
-                  &output);
-      btm_ble_process_dhk(&output);
-
-      BTM_TRACE_DEBUG("BLE IR generated.");
-    }));
+    reset_id_data tmp;
+    memcpy(tmp.rand1.data(), rand, BT_OCTET8_LEN);
+    btsnd_hcic_ble_rand(base::Bind(
+        [](reset_id_data tmp, BT_OCTET8 rand) {
+          memcpy(tmp.rand1.data() + 8, rand, BT_OCTET8_LEN);
+          btsnd_hcic_ble_rand(base::Bind(
+              [](reset_id_data tmp, BT_OCTET8 rand) {
+                memcpy(tmp.rand2.data(), rand, BT_OCTET8_LEN);
+                btsnd_hcic_ble_rand(base::Bind(
+                    [](reset_id_data tmp, BT_OCTET8 rand) {
+                      memcpy(tmp.rand2.data() + 8, rand, BT_OCTET8_LEN);
+                      // when all random numbers are ready, do the actual reset.
+                      btm_ble_reset_id_impl(tmp.rand1, tmp.rand2);
+                    },
+                    tmp));
+              },
+              tmp));
+        },
+        tmp));
   }));
 }
 
diff --git a/stack/btm/btm_ble_addr.cc b/stack/btm/btm_ble_addr.cc
index 2d0acc6..2528915 100644
--- a/stack/btm/btm_ble_addr.cc
+++ b/stack/btm/btm_ble_addr.cc
@@ -33,88 +33,66 @@
 #include "hcimsgs.h"
 
 #include "btm_ble_int.h"
-#include "smp_api.h"
+#include "stack/crypto_toolbox/crypto_toolbox.h"
 
-/*******************************************************************************
- *
- * Function         btm_gen_resolve_paddr_cmpl
- *
- * Description      This is callback functioin when resolvable private address
- *                  generation is complete.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void btm_gen_resolve_paddr_cmpl(tSMP_ENC* p) {
-  tBTM_LE_RANDOM_CB* p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb;
-  BTM_TRACE_EVENT("btm_gen_resolve_paddr_cmpl");
+/* This function generates Resolvable Private Address (RPA) from Identity
+ * Resolving Key |irk| and |random|*/
+RawAddress generate_rpa_from_irk_and_rand(const Octet16& irk,
+                                          BT_OCTET8 random) {
+  random[2] &= (~BLE_RESOLVE_ADDR_MASK);
+  random[2] |= BLE_RESOLVE_ADDR_MSB;
 
-  if (p) {
-    /* set hash to be LSB of rpAddress */
-    p_cb->private_addr.address[5] = p->param_buf[0];
-    p_cb->private_addr.address[4] = p->param_buf[1];
-    p_cb->private_addr.address[3] = p->param_buf[2];
-    /* set it to controller */
-    btm_ble_set_random_address(p_cb->private_addr);
+  RawAddress address;
+  address.address[2] = random[0];
+  address.address[1] = random[1];
+  address.address[0] = random[2];
 
-    p_cb->own_addr_type = BLE_ADDR_RANDOM;
+  /* encrypt with IRK */
+  Octet16 p = crypto_toolbox::aes_128(irk, random, 3);
 
-    /* start a periodical timer to refresh random addr */
-    period_ms_t interval_ms = BTM_BLE_PRIVATE_ADDR_INT_MS;
-#if (BTM_BLE_CONFORMANCE_TESTING == TRUE)
-    interval_ms = btm_cb.ble_ctr_cb.rpa_tout * 1000;
-#endif
-    alarm_set_on_mloop(p_cb->refresh_raddr_timer, interval_ms,
-                       btm_ble_refresh_raddr_timer_timeout, NULL);
-  } else {
-    /* random address set failure */
-    BTM_TRACE_DEBUG("set random address failed");
-  }
+  /* set hash to be LSB of rpAddress */
+  address.address[5] = p[0];
+  address.address[4] = p[1];
+  address.address[3] = p[2];
+  return address;
 }
-/*******************************************************************************
- *
- * Function         btm_gen_resolve_paddr_low
- *
- * Description      This function is called when random address has generate the
- *                  random number base for low 3 byte bd address.
- *
- * Returns          void
- *
- ******************************************************************************/
-void btm_gen_resolve_paddr_low(BT_OCTET8 rand) {
+
+/** This function is called when random address for local controller was
+ * generated */
+void btm_gen_resolve_paddr_low(const RawAddress& address) {
   tBTM_LE_RANDOM_CB* p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb;
-  tSMP_ENC output;
 
   BTM_TRACE_EVENT("btm_gen_resolve_paddr_low");
-  rand[2] &= (~BLE_RESOLVE_ADDR_MASK);
-  rand[2] |= BLE_RESOLVE_ADDR_MSB;
 
-  p_cb->private_addr.address[2] = rand[0];
-  p_cb->private_addr.address[1] = rand[1];
-  p_cb->private_addr.address[0] = rand[2];
+  p_cb->private_addr = address;
 
-  /* encrypt with ur IRK */
-  if (!SMP_Encrypt(btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN, rand, 3,
-                   &output)) {
-    btm_gen_resolve_paddr_cmpl(NULL);
-  } else {
-    btm_gen_resolve_paddr_cmpl(&output);
-  }
+  /* set it to controller */
+  btm_ble_set_random_address(p_cb->private_addr);
+
+  p_cb->own_addr_type = BLE_ADDR_RANDOM;
+
+  /* start a periodical timer to refresh random addr */
+  period_ms_t interval_ms = BTM_BLE_PRIVATE_ADDR_INT_MS;
+#if (BTM_BLE_CONFORMANCE_TESTING == TRUE)
+  interval_ms = btm_cb.ble_ctr_cb.rpa_tout * 1000;
+#endif
+  alarm_set_on_mloop(p_cb->refresh_raddr_timer, interval_ms,
+                     btm_ble_refresh_raddr_timer_timeout, NULL);
 }
-/*******************************************************************************
- *
- * Function         btm_gen_resolvable_private_addr
- *
- * Description      This function generate a resolvable private address.
- *
- * Returns          void
- *
- ******************************************************************************/
-void btm_gen_resolvable_private_addr(base::Callback<void(BT_OCTET8)> cb) {
+
+/** This function generate a resolvable private address using local IRK */
+void btm_gen_resolvable_private_addr(
+    base::Callback<void(const RawAddress&)> cb) {
   BTM_TRACE_EVENT("%s", __func__);
   /* generate 3B rand as BD LSB, SRK with it, get BD MSB */
-  btsnd_hcic_ble_rand(std::move(cb));
+  btsnd_hcic_ble_rand(base::Bind(
+      [](base::Callback<void(const RawAddress&)> cb, BT_OCTET8 random) {
+        const Octet16& irk = BTM_GetDeviceIDRoot();
+        cb.Run(generate_rpa_from_irk_and_rand(irk, random));
+      },
+      std::move(cb)));
 }
+
 /*******************************************************************************
  *
  * Function         btm_gen_non_resolve_paddr_cmpl
@@ -169,33 +147,6 @@
 /*******************************************************************************
  *  Utility functions for Random address resolving
  ******************************************************************************/
-/*******************************************************************************
- *
- * Function         btm_ble_proc_resolve_x
- *
- * Description      This function compares the X with random address 3 MSO bytes
- *                  to find a match.
- *
- * Returns          true on match, false otherwise
- *
- ******************************************************************************/
-static bool btm_ble_proc_resolve_x(const tSMP_ENC& encrypt_output,
-                                   const RawAddress& random_bda) {
-  BTM_TRACE_EVENT("btm_ble_proc_resolve_x");
-
-  /* compare the hash with 3 LSB of bd address */
-  uint8_t comp[3];
-  comp[0] = random_bda.address[5];
-  comp[1] = random_bda.address[4];
-  comp[2] = random_bda.address[3];
-
-  if (!memcmp(encrypt_output.param_buf, comp, 3)) {
-    BTM_TRACE_EVENT("match is found");
-    return true;
-  }
-
-  return false;
-}
 
 /*******************************************************************************
  *
@@ -217,71 +168,56 @@
   return false;
 }
 
-/*******************************************************************************
- *
- * Function         btm_ble_addr_resolvable
- *
- * Description      This function checks if a RPA is resolvable by the device
- *                  key.
- *
- * Returns          true is resolvable; false otherwise.
- *
- ******************************************************************************/
+/* Return true if given Resolvable Privae Address |rpa| matches Identity
+ * Resolving Key |irk| */
+static bool rpa_matches_irk(const RawAddress& rpa, const Octet16& irk) {
+  /* use the 3 MSB of bd address as prand */
+  uint8_t rand[3];
+  rand[0] = rpa.address[2];
+  rand[1] = rpa.address[1];
+  rand[2] = rpa.address[0];
+
+  /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */
+  Octet16 x = crypto_toolbox::aes_128(irk, &rand[0], 3);
+
+  rand[0] = rpa.address[5];
+  rand[1] = rpa.address[4];
+  rand[2] = rpa.address[3];
+
+  if (memcmp(x.data(), &rand[0], 3) == 0) {
+    // match
+    return true;
+  }
+  // not a match
+  return false;
+}
+
+/** This function checks if a RPA is resolvable by the device key.
+ *  Returns true is resolvable; false otherwise.
+ */
 bool btm_ble_addr_resolvable(const RawAddress& rpa,
                              tBTM_SEC_DEV_REC* p_dev_rec) {
-  bool rt = false;
+  if (!BTM_BLE_IS_RESOLVE_BDA(rpa)) return false;
 
-  if (!BTM_BLE_IS_RESOLVE_BDA(rpa)) return rt;
-
-  uint8_t rand[3];
-  tSMP_ENC output;
   if ((p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) &&
       (p_dev_rec->ble.key_type & BTM_LE_KEY_PID)) {
     BTM_TRACE_DEBUG("%s try to resolve", __func__);
-    /* use the 3 MSB of bd address as prand */
-    rand[0] = rpa.address[2];
-    rand[1] = rpa.address[1];
-    rand[2] = rpa.address[0];
 
-    /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */
-    SMP_Encrypt(p_dev_rec->ble.keys.irk, BT_OCTET16_LEN, &rand[0], 3, &output);
-
-    rand[0] = rpa.address[5];
-    rand[1] = rpa.address[4];
-    rand[2] = rpa.address[3];
-
-    if (!memcmp(output.param_buf, &rand[0], 3)) {
+    if (rpa_matches_irk(rpa, p_dev_rec->ble.keys.irk)) {
       btm_ble_init_pseudo_addr(p_dev_rec, rpa);
-      rt = true;
+      return true;
     }
   }
-  return rt;
+  return false;
 }
 
-/*******************************************************************************
- *
- * Function         btm_ble_match_random_bda
- *
- * Description      This function match the random address to the appointed
- *                  device record, starting from calculating IRK. If the record
- *                  index exceeds the maximum record number, matching failed and
- *                  send a callback.
- *
- * Returns          None.
- *
- ******************************************************************************/
+/** This function match the random address to the appointed device record,
+ * starting from calculating IRK. If the record index exceeds the maximum record
+ * number, matching failed and send a callback. */
 static bool btm_ble_match_random_bda(void* data, void* context) {
-  RawAddress* random_bda = (RawAddress*)context;
-  /* use the 3 MSB of bd address as prand */
-
-  uint8_t rand[3];
-  rand[0] = random_bda->address[2];
-  rand[1] = random_bda->address[1];
-  rand[2] = random_bda->address[0];
-
   BTM_TRACE_EVENT("%s next iteration", __func__);
+  RawAddress* random_bda = (RawAddress*)context;
 
-  tSMP_ENC output;
   tBTM_SEC_DEV_REC* p_dev_rec = static_cast<tBTM_SEC_DEV_REC*>(data);
 
   BTM_TRACE_DEBUG("sec_flags = %02x device_type = %d", p_dev_rec->sec_flags,
@@ -291,22 +227,20 @@
       !(p_dev_rec->ble.key_type & BTM_LE_KEY_PID))
     return true;
 
-  /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */
-  SMP_Encrypt(p_dev_rec->ble.keys.irk, BT_OCTET16_LEN, &rand[0], 3, &output);
-  // if it was match, finish iteration, otherwise continue
-  return !btm_ble_proc_resolve_x(output, *random_bda);
+  if (rpa_matches_irk(*random_bda, p_dev_rec->ble.keys.irk)) {
+    BTM_TRACE_EVENT("match is found");
+    // if it was match, finish iteration, otherwise continue
+    return false;
+  }
+
+  // not a match, continue iteration
+  return true;
 }
 
-/*******************************************************************************
- *
- * Function         btm_ble_resolve_random_addr
- *
- * Description      This function is called to resolve a random address.
- *
- * Returns          pointer to the security record of the device whom a random
- *                  address is matched to.
- *
- ******************************************************************************/
+/** This function is called to resolve a random address.
+ * Returns pointer to the security record of the device whom a random address is
+ * matched to.
+ */
 tBTM_SEC_DEV_REC* btm_ble_resolve_random_addr(const RawAddress& random_bda) {
   BTM_TRACE_EVENT("%s", __func__);
 
diff --git a/stack/btm/btm_ble_bgconn.cc b/stack/btm/btm_ble_bgconn.cc
index cb3e053..4c8dcd8 100644
--- a/stack/btm/btm_ble_bgconn.cc
+++ b/stack/btm/btm_ble_bgconn.cc
@@ -23,50 +23,37 @@
  ******************************************************************************/
 
 #include <base/logging.h>
-#include <string.h>
 #include <unordered_map>
 
 #include "bt_types.h"
-#include "bt_utils.h"
 #include "btm_int.h"
 #include "btu.h"
 #include "device/include/controller.h"
 #include "hcimsgs.h"
 #include "l2c_int.h"
-#include "osi/include/allocator.h"
-#include "osi/include/osi.h"
-
-#ifndef BTM_BLE_SCAN_PARAM_TOUT
-#define BTM_BLE_SCAN_PARAM_TOUT 50 /* 50 seconds */
-#endif
-
-static void btm_suspend_wl_activity(tBTM_BLE_WL_STATE wl_state);
-static void btm_resume_wl_activity(tBTM_BLE_WL_STATE wl_state);
 
 // Unfortunately (for now?) we have to maintain a copy of the device whitelist
 // on the host to determine if a device is pending to be connected or not. This
 // controls whether the host should keep trying to scan for whitelisted
 // peripherals or not.
 // TODO: Move all of this to controller/le/background_list or similar?
-typedef struct background_connection_t {
+struct BackgroundConnection {
   RawAddress address;
   uint8_t addr_type;
-
   bool in_controller_wl;
   uint8_t addr_type_in_wl;
-
   bool pending_removal;
-} background_connection_t;
+};
 
 struct BgConnHash {
-  bool operator()(const RawAddress& x) const {
+  std::size_t operator()(const RawAddress& x) const {
     const uint8_t* a = x.address;
     return a[0] ^ (a[1] << 8) ^ (a[2] << 16) ^ (a[3] << 24) ^ a[4] ^
            (a[5] << 8);
   }
 };
 
-static std::unordered_map<RawAddress, background_connection_t, BgConnHash>
+static std::unordered_map<RawAddress, BackgroundConnection, BgConnHash>
     background_connections;
 
 static void background_connection_add(uint8_t addr_type,
@@ -74,9 +61,9 @@
   auto map_iter = background_connections.find(address);
   if (map_iter == background_connections.end()) {
     background_connections[address] =
-        background_connection_t{address, addr_type, false, 0, false};
+        BackgroundConnection{address, addr_type, false, 0, false};
   } else {
-    background_connection_t* connection = &map_iter->second;
+    BackgroundConnection* connection = &map_iter->second;
     connection->addr_type = addr_type;
     connection->pending_removal = false;
   }
@@ -97,7 +84,7 @@
 
 static bool background_connections_pending() {
   for (auto& map_el : background_connections) {
-    background_connection_t* connection = &map_el.second;
+    BackgroundConnection* connection = &map_el.second;
     if (connection->pending_removal) continue;
     const bool connected =
         BTM_IsAclConnectionUp(connection->address, BT_TRANSPORT_LE);
@@ -159,7 +146,7 @@
 
   auto map_it = background_connections.find(bd_addr);
   if (map_it != background_connections.end()) {
-    background_connection_t* connection = &map_it->second;
+    BackgroundConnection* connection = &map_it->second;
     if (!connection->in_controller_wl && !connection->pending_removal &&
         !BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_LE)) {
       btm_ble_start_auto_conn(false);
@@ -229,7 +216,7 @@
   // handle removals first to avoid filling up controller's white list
   for (auto map_it = background_connections.begin();
        map_it != background_connections.end();) {
-    background_connection_t* connection = &map_it->second;
+    BackgroundConnection* connection = &map_it->second;
     if (connection->pending_removal) {
       btsnd_hcic_ble_remove_from_white_list(connection->addr_type_in_wl,
                                             connection->address);
@@ -238,7 +225,7 @@
       ++map_it;
   }
   for (auto& map_el : background_connections) {
-    background_connection_t* connection = &map_el.second;
+    BackgroundConnection* connection = &map_el.second;
     const bool connected =
         BTM_IsAclConnectionUp(connection->address, BT_TRANSPORT_LE);
     if (!connection->in_controller_wl && !connected) {
@@ -276,9 +263,11 @@
     return false;
   }
 
-  btm_suspend_wl_activity(p_cb->wl_state);
+  if (p_cb->wl_state & BTM_BLE_WL_INIT) {
+    btm_ble_start_auto_conn(false);
+  }
   btm_add_dev_to_controller(to_add, bd_addr);
-  btm_resume_wl_activity(p_cb->wl_state);
+  btm_ble_resume_bg_conn();
   return true;
 }
 
@@ -289,7 +278,7 @@
  * Description      This function clears the white list.
  *
  ******************************************************************************/
-void btm_ble_clear_white_list(void) {
+void btm_ble_clear_white_list() {
   BTM_TRACE_EVENT("btm_ble_clear_white_list");
   btsnd_hcic_ble_clear_white_list();
   background_connections_clear();
@@ -352,8 +341,10 @@
     /* This is a sign that logic around keeping connection state is broken */
     LOG(ERROR)
         << "Attempt to cancel LE connection, when no connection is pending.";
-    btm_ble_set_conn_st(BLE_CONN_IDLE);
-    btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, nullptr, status);
+    if (btm_ble_get_conn_st() == BLE_CONN_CANCEL) {
+      btm_ble_set_conn_st(BLE_CONN_IDLE);
+      btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, nullptr, status);
+    }
   }
 }
 
@@ -409,8 +400,12 @@
 bool btm_ble_start_auto_conn(bool start) {
   tBTM_BLE_CB* p_cb = &btm_cb.ble_ctr_cb;
   bool exec = true;
-  uint16_t scan_int;
-  uint16_t scan_win;
+  uint16_t scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF)
+                          ? BTM_BLE_SCAN_SLOW_INT_1
+                          : p_cb->scan_int;
+  uint16_t scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF)
+                          ? BTM_BLE_SCAN_SLOW_WIN_1
+                          : p_cb->scan_win;
   uint8_t own_addr_type = p_cb->addr_mgnt_cb.own_addr_type;
   uint8_t peer_addr_type = BLE_ADDR_PUBLIC;
 
@@ -429,15 +424,6 @@
 
 #if (BLE_PRIVACY_SPT == TRUE)
       btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_INIT);
-#endif
-      scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF)
-                     ? BTM_BLE_SCAN_SLOW_INT_1
-                     : p_cb->scan_int;
-      scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF)
-                     ? BTM_BLE_SCAN_SLOW_WIN_1
-                     : p_cb->scan_win;
-
-#if (BLE_PRIVACY_SPT == TRUE)
       if (btm_cb.ble_ctr_cb.rl_state != BTM_BLE_RL_IDLE &&
           controller_get_interface()->supports_ble_privacy()) {
         own_addr_type |= BLE_ADDR_TYPE_ID_BIT;
@@ -497,32 +483,7 @@
 
   return false;
 }
-/*******************************************************************************
- *
- * Function         btm_suspend_wl_activity
- *
- * Description      This function is to suspend white list related activity
- *
- * Returns          none.
- *
- ******************************************************************************/
-static void btm_suspend_wl_activity(tBTM_BLE_WL_STATE wl_state) {
-  if (wl_state & BTM_BLE_WL_INIT) {
-    btm_ble_start_auto_conn(false);
-  }
-}
-/*******************************************************************************
- *
- * Function         btm_resume_wl_activity
- *
- * Description      This function is to resume white list related activity
- *
- * Returns          none.
- *
- ******************************************************************************/
-static void btm_resume_wl_activity(tBTM_BLE_WL_STATE wl_state) {
-  btm_ble_resume_bg_conn();
-}
+
 /*******************************************************************************
  *
  * Function         btm_ble_resume_bg_conn
@@ -620,6 +581,7 @@
     }
   }
 }
+
 /*******************************************************************************
  *
  * Function         btm_send_pending_direct_conn
diff --git a/stack/btm/btm_ble_int.h b/stack/btm/btm_ble_int.h
index d4cb411..0544830 100644
--- a/stack/btm/btm_ble_int.h
+++ b/stack/btm/btm_ble_int.h
@@ -80,7 +80,7 @@
                                    tBTM_LE_AUTH_REQ auth_req,
                                    tBTM_BLE_SEC_REQ_ACT* p_sec_req_act);
 extern void btm_ble_ltk_request_reply(const RawAddress& bda, bool use_stk,
-                                      BT_OCTET16 stk);
+                                      const Octet16& stk);
 extern uint8_t btm_proc_smp_cback(tSMP_EVT event, const RawAddress& bd_addr,
                                   tSMP_EVT_DATA* p_data);
 extern tBTM_STATUS btm_ble_set_encryption(const RawAddress& bd_addr,
@@ -89,7 +89,7 @@
 extern void btm_ble_ltk_request(uint16_t handle, uint8_t rand[8],
                                 uint16_t ediv);
 extern tBTM_STATUS btm_ble_start_encrypt(const RawAddress& bda, bool use_stk,
-                                         BT_OCTET16 stk);
+                                         Octet16* p_stk);
 extern void btm_ble_link_encrypted(const RawAddress& bd_addr,
                                    uint8_t encr_enable);
 
@@ -154,12 +154,13 @@
 extern void btm_ble_dequeue_direct_conn_req(const RawAddress& rem_bda);
 
 /* BLE address management */
-extern void btm_gen_resolvable_private_addr(base::Callback<void(BT_OCTET8)> cb);
+extern void btm_gen_resolvable_private_addr(
+    base::Callback<void(const RawAddress& rpa)> cb);
 extern void btm_gen_non_resolvable_private_addr(tBTM_BLE_ADDR_CBACK* p_cback,
                                                 void* p);
 extern tBTM_SEC_DEV_REC* btm_ble_resolve_random_addr(
     const RawAddress& random_bda);
-extern void btm_gen_resolve_paddr_low(BT_OCTET8 rand);
+extern void btm_gen_resolve_paddr_low(const RawAddress& address);
 
 /*  privacy function */
 #if (BLE_PRIVACY_SPT == TRUE)
diff --git a/stack/btm/btm_ble_int_types.h b/stack/btm/btm_ble_int_types.h
index 2167f2f..68f46f2 100644
--- a/stack/btm/btm_ble_int_types.h
+++ b/stack/btm/btm_ble_int_types.h
@@ -185,9 +185,8 @@
 } tBTM_LE_BG_CONN_DEV;
 
 /* white list using state as a bit mask */
-#define BTM_BLE_WL_IDLE 0
-#define BTM_BLE_WL_INIT 1
-typedef uint8_t tBTM_BLE_WL_STATE;
+constexpr uint8_t BTM_BLE_WL_IDLE = 0;
+constexpr uint8_t BTM_BLE_WL_INIT = 1;
 
 /* resolving list using state as a bit mask */
 #define BTM_BLE_RL_IDLE 0
@@ -288,11 +287,11 @@
 
   /* background connection procedure cb value */
   tBTM_BLE_CONN_TYPE bg_conn_type;
-  uint32_t scan_int;
-  uint32_t scan_win;
+  uint16_t scan_int;
+  uint16_t scan_win;
 
   /* white list information */
-  tBTM_BLE_WL_STATE wl_state;
+  uint8_t wl_state;
 
   fixed_queue_t* conn_pending_q;
   tBTM_BLE_CONN_ST conn_state;
diff --git a/stack/btm/btm_ble_multi_adv.cc b/stack/btm/btm_ble_multi_adv.cc
index f34e9de..a7960cd 100644
--- a/stack/btm/btm_ble_multi_adv.cc
+++ b/stack/btm/btm_ble_multi_adv.cc
@@ -44,7 +44,7 @@
     uint8_t /* inst_id */, int8_t /* tx_power */, uint8_t /* status */)>;
 using SetEnableData = BleAdvertiserHciInterface::SetEnableData;
 extern void btm_gen_resolvable_private_addr(
-    base::Callback<void(uint8_t[8])> cb);
+    base::Callback<void(const RawAddress& rpa)> cb);
 
 constexpr int ADV_DATA_LEN_MAX = 251;
 
@@ -187,38 +187,8 @@
     }
   }
 
-  void OnRpaGenerationComplete(base::Callback<void(RawAddress)> cb,
-                               uint8_t rand[8]) {
-    VLOG(1) << __func__;
-
-    RawAddress bda;
-
-    rand[2] &= (~BLE_RESOLVE_ADDR_MASK);
-    rand[2] |= BLE_RESOLVE_ADDR_MSB;
-
-    bda.address[2] = rand[0];
-    bda.address[1] = rand[1];
-    bda.address[0] = rand[2];
-
-    BT_OCTET16 irk;
-    BTM_GetDeviceIDRoot(irk);
-    tSMP_ENC output;
-
-    if (!SMP_Encrypt(irk, BT_OCTET16_LEN, rand, 3, &output))
-      LOG_ASSERT(false) << "SMP_Encrypt failed";
-
-    /* set hash to be LSB of rpAddress */
-    bda.address[5] = output.param_buf[0];
-    bda.address[4] = output.param_buf[1];
-    bda.address[3] = output.param_buf[2];
-
-    cb.Run(bda);
-  }
-
-  void GenerateRpa(base::Callback<void(RawAddress)> cb) {
-    btm_gen_resolvable_private_addr(
-        Bind(&BleAdvertisingManagerImpl::OnRpaGenerationComplete,
-             weak_factory_.GetWeakPtr(), std::move(cb)));
+  void GenerateRpa(base::Callback<void(const RawAddress&)> cb) {
+    btm_gen_resolvable_private_addr(std::move(cb));
   }
 
   void ConfigureRpa(AdvertisingInstance* p_inst, MultiAdvCb configuredCb) {
@@ -236,7 +206,7 @@
 
     GenerateRpa(Bind(
         [](AdvertisingInstance* p_inst, MultiAdvCb configuredCb,
-           RawAddress bda) {
+           const RawAddress& bda) {
           /* Connectable advertising set must be disabled when updating RPA */
           bool restart = p_inst->IsEnabled() && p_inst->IsConnectable();
 
@@ -285,7 +255,7 @@
             [](AdvertisingInstance* p_inst,
                base::Callback<void(uint8_t /* inst_id */, uint8_t /* status */)>
                    cb,
-               RawAddress bda) {
+               const RawAddress& bda) {
               p_inst->own_address = bda;
 
               alarm_set_on_mloop(p_inst->adv_raddr_timer,
diff --git a/stack/btm/btm_ble_privacy.cc b/stack/btm/btm_ble_privacy.cc
index 3845ba6..6581f25 100644
--- a/stack/btm/btm_ble_privacy.cc
+++ b/stack/btm/btm_ble_privacy.cc
@@ -728,8 +728,8 @@
 
   btm_ble_update_resolving_list(p_dev_rec->bd_addr, true);
   if (controller_get_interface()->supports_ble_privacy()) {
-    uint8_t* peer_irk = p_dev_rec->ble.keys.irk;
-    uint8_t* local_irk = btm_cb.devcb.id_keys.irk;
+    const Octet16& peer_irk = p_dev_rec->ble.keys.irk;
+    const Octet16& local_irk = btm_cb.devcb.id_keys.irk;
 
     if (p_dev_rec->ble.static_addr.IsEmpty()) {
       p_dev_rec->ble.static_addr = p_dev_rec->bd_addr;
@@ -754,7 +754,7 @@
     uint8_t* p = param;
 
     UINT8_TO_STREAM(p, BTM_BLE_META_ADD_IRK_ENTRY);
-    ARRAY_TO_STREAM(p, p_dev_rec->ble.keys.irk, BT_OCTET16_LEN);
+    ARRAY_TO_STREAM(p, p_dev_rec->ble.keys.irk, OCTET16_LEN);
     UINT8_TO_STREAM(p, p_dev_rec->ble.static_addr_type);
     BDADDR_TO_STREAM(p, p_dev_rec->ble.static_addr);
 
diff --git a/stack/btm/btm_dev.cc b/stack/btm/btm_dev.cc
index 66382e8..e999fed 100644
--- a/stack/btm/btm_dev.cc
+++ b/stack/btm/btm_dev.cc
@@ -60,7 +60,7 @@
  ******************************************************************************/
 bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
                       BD_NAME bd_name, uint8_t* features,
-                      uint32_t trusted_mask[], LINK_KEY link_key,
+                      uint32_t trusted_mask[], LinkKey* p_link_key,
                       uint8_t key_type, tBTM_IO_CAP io_cap,
                       uint8_t pin_length) {
   BTM_TRACE_API("%s: link key type:%x", __func__, key_type);
@@ -120,10 +120,10 @@
 
   BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask);
 
-  if (link_key) {
+  if (p_link_key) {
     VLOG(2) << __func__ << ": BDA: " << bd_addr;
     p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN;
-    memcpy(p_dev_rec->link_key, link_key, LINK_KEY_LEN);
+    p_dev_rec->link_key = *p_link_key;
     p_dev_rec->link_key_type = key_type;
     p_dev_rec->pin_code_length = pin_length;
 
diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h
index 21f7ce3..1a696c3 100644
--- a/stack/btm/btm_int.h
+++ b/stack/btm/btm_int.h
@@ -256,7 +256,7 @@
 extern void btm_keypress_notif_evt(uint8_t* p);
 extern void btm_simple_pair_complete(uint8_t* p);
 extern void btm_sec_link_key_notification(const RawAddress& p_bda,
-                                          uint8_t* p_link_key,
+                                          const Octet16& link_key,
                                           uint8_t key_type);
 extern void btm_sec_link_key_request(const RawAddress& p_bda);
 extern void btm_sec_pin_code_request(const RawAddress& p_bda);
diff --git a/stack/btm/btm_int_types.h b/stack/btm/btm_int_types.h
index 8f91cef..95acb5d 100644
--- a/stack/btm/btm_int_types.h
+++ b/stack/btm/btm_int_types.h
@@ -170,7 +170,7 @@
   uint8_t le_supported_states[BTM_LE_SUPPORT_STATE_SIZE];
 
   tBTM_BLE_LOCAL_ID_KEYS id_keys;      /* local BLE ID keys */
-  BT_OCTET16 ble_encryption_key_value; /* BLE encryption key */
+  Octet16 ble_encryption_key_value;    /* BLE encryption key */
 
 #if (BTM_BLE_CONFORMANCE_TESTING == TRUE)
   bool no_disc_if_pair_fail;
@@ -415,12 +415,12 @@
 
 /* LE Security information of device in Slave Role */
 typedef struct {
-  BT_OCTET16 irk;   /* peer diverified identity root */
-  BT_OCTET16 pltk;  /* peer long term key */
-  BT_OCTET16 pcsrk; /* peer SRK peer device used to secured sign local data  */
+  Octet16 irk;   /* peer diverified identity root */
+  Octet16 pltk;  /* peer long term key */
+  Octet16 pcsrk; /* peer SRK peer device used to secured sign local data  */
 
-  BT_OCTET16 lltk;  /* local long term key */
-  BT_OCTET16 lcsrk; /* local SRK peer device used to secured sign local data  */
+  Octet16 lltk;  /* local long term key */
+  Octet16 lcsrk; /* local SRK peer device used to secured sign local data  */
 
   BT_OCTET8 rand;        /* random vector for LTK generation */
   uint16_t ediv;         /* LTK diversifier of this slave device */
@@ -479,7 +479,7 @@
   uint16_t clock_offset;   /* Latest known clock offset          */
   RawAddress bd_addr;      /* BD_ADDR of the device              */
   DEV_CLASS dev_class;     /* DEV_CLASS of the device            */
-  LINK_KEY link_key;       /* Device link key                    */
+  LinkKey link_key;        /* Device link key                    */
   uint8_t pin_code_length; /* Length of the pin_code used for paring */
 
 #define BTM_SEC_AUTHORIZED BTM_SEC_FLAG_AUTHORIZED       /* 0x01 */
diff --git a/stack/btm/btm_sec.cc b/stack/btm/btm_sec.cc
index 442fb77..7ca9976 100644
--- a/stack/btm/btm_sec.cc
+++ b/stack/btm/btm_sec.cc
@@ -224,7 +224,6 @@
  *
  ******************************************************************************/
 bool BTM_SecRegister(const tBTM_APPL_INFO* p_cb_info) {
-  BT_OCTET16 temp_value = {0};
 
   BTM_TRACE_EVENT("%s application registered", __func__);
 
@@ -233,8 +232,9 @@
   if (p_cb_info->p_le_callback) {
     BTM_TRACE_EVENT("%s SMP_Register( btm_proc_smp_cback )", __func__);
     SMP_Register(btm_proc_smp_cback);
+    Octet16 zero{0};
     /* if no IR is loaded, need to regenerate all the keys */
-    if (memcmp(btm_cb.devcb.id_keys.ir, &temp_value, sizeof(BT_OCTET16)) == 0) {
+    if (btm_cb.devcb.id_keys.ir == zero) {
       btm_ble_reset_id();
     }
   } else {
@@ -1169,15 +1169,15 @@
  *                  the device or device record does not contain link key info
  *
  * Parameters:      bd_addr      - Address of the device
- *                  link_key     - Link Key is copied into this array
+ *                  link_key     - Link Key is copied into this pointer
  *
  ******************************************************************************/
 tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr,
-                                    LINK_KEY link_key) {
+                                    LinkKey* link_key) {
   tBTM_SEC_DEV_REC* p_dev_rec;
   p_dev_rec = btm_find_dev(bd_addr);
   if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) {
-    memcpy(link_key, p_dev_rec->link_key, LINK_KEY_LEN);
+    *link_key = p_dev_rec->link_key;
     return (BTM_SUCCESS);
   }
   return (BTM_UNKNOWN_ADDR);
@@ -1558,7 +1558,7 @@
  *
  ******************************************************************************/
 void BTM_RemoteOobDataReply(tBTM_STATUS res, const RawAddress& bd_addr,
-                            BT_OCTET16 c, BT_OCTET16 r) {
+                            const Octet16& c, const Octet16& r) {
   BTM_TRACE_EVENT("%s() - State: %s res: %d", __func__,
                   btm_pair_state_descr(btm_cb.pairing_state), res);
 
@@ -1596,8 +1596,8 @@
  * Returns          Number of bytes in p_data.
  *
  ******************************************************************************/
-uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len, BT_OCTET16 c,
-                          BT_OCTET16 r, uint8_t name_len) {
+uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len, const Octet16& c,
+                          const Octet16& r, uint8_t name_len) {
   uint8_t* p = p_data;
   uint16_t len = 0;
   uint16_t name_size;
@@ -1618,7 +1618,7 @@
     if (max_len >= delta) {
       *p++ = BTM_OOB_HASH_C_SIZE + 1;
       *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE;
-      ARRAY_TO_STREAM(p, c, BTM_OOB_HASH_C_SIZE);
+      ARRAY_TO_STREAM(p, c.data(), BTM_OOB_HASH_C_SIZE);
       len += delta;
       max_len -= delta;
     }
@@ -1628,7 +1628,7 @@
     if (max_len >= delta) {
       *p++ = BTM_OOB_RAND_R_SIZE + 1;
       *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE;
-      ARRAY_TO_STREAM(p, r, BTM_OOB_RAND_R_SIZE);
+      ARRAY_TO_STREAM(p, r.data(), BTM_OOB_RAND_R_SIZE);
       len += delta;
       max_len -= delta;
     }
@@ -3602,8 +3602,8 @@
 void btm_rem_oob_req(uint8_t* p) {
   tBTM_SP_RMT_OOB evt_data;
   tBTM_SEC_DEV_REC* p_dev_rec;
-  BT_OCTET16 c;
-  BT_OCTET16 r;
+  Octet16 c;
+  Octet16 r;
 
   RawAddress& p_bda = evt_data.bd_addr;
 
@@ -3648,8 +3648,8 @@
   BTM_TRACE_EVENT("btm_read_local_oob_complete:%d", status);
   if (status == HCI_SUCCESS) {
     evt_data.status = BTM_SUCCESS;
-    STREAM_TO_ARRAY16(evt_data.c, p);
-    STREAM_TO_ARRAY16(evt_data.r, p);
+    STREAM_TO_ARRAY16(evt_data.c.data(), p);
+    STREAM_TO_ARRAY16(evt_data.r.data(), p);
   } else
     evt_data.status = BTM_ERR_PROCESSING;
 
@@ -4039,8 +4039,6 @@
                       __func__, p_dev_rec, p_dev_rec->p_callback);
       p_dev_rec->p_callback = NULL;
       l2cu_resubmit_pending_sec_req(&p_dev_rec->bd_addr);
-    } else if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) {
-      p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
     }
     return;
   }
@@ -4104,6 +4102,7 @@
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bda);
   uint8_t res;
   bool is_pairing_device = false;
+  bool addr_matched;
   tACL_CONN* p_acl_cb;
   uint8_t bit_shift = 0;
 
@@ -4193,8 +4192,9 @@
 
   p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
 
-  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
-      (btm_cb.pairing_bda == bda)) {
+  addr_matched = (btm_cb.pairing_bda == bda);
+
+  if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) && addr_matched) {
     /* if we rejected incoming connection from bonding device */
     if ((status == HCI_ERR_HOST_REJECT_DEVICE) &&
         (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT)) {
@@ -4290,7 +4290,7 @@
       }
     }
 
-    if (btm_cb.pairing_bda != bda) {
+    if (!addr_matched) {
       /* Don't callback unless this Connection-Complete-failure event has the
        * same mac address as the bonding device */
       VLOG(1) << __func__
@@ -4551,18 +4551,9 @@
   }
 }
 
-/*******************************************************************************
- *
- * Function         btm_sec_link_key_notification
- *
- * Description      This function is called when a new connection link key is
- *                  generated
- *
- * Returns          Pointer to the record or NULL
- *
- ******************************************************************************/
-void btm_sec_link_key_notification(const RawAddress& p_bda, uint8_t* p_link_key,
-                                   uint8_t key_type) {
+/** This function is called when a new connection link key is generated */
+void btm_sec_link_key_notification(const RawAddress& p_bda,
+                                   const Octet16& link_key, uint8_t key_type) {
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(p_bda);
   bool we_are_bonding = false;
   bool ltk_derived_lk = false;
@@ -4595,7 +4586,7 @@
 
   /* BR/EDR connection, update the encryption key size to be 16 as always */
   p_dev_rec->enc_key_size = 16;
-  memcpy(p_dev_rec->link_key, p_link_key, LINK_KEY_LEN);
+  p_dev_rec->link_key = link_key;
 
   if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) &&
       (btm_cb.pairing_bda == p_bda)) {
@@ -4611,7 +4602,7 @@
       BTM_TRACE_DEBUG("%s() Save LTK derived LK (key_type = %d)", __func__,
                       p_dev_rec->link_key_type);
       (*btm_cb.api.p_link_key_callback)(p_bda, p_dev_rec->dev_class,
-                                        p_dev_rec->sec_bd_name, p_link_key,
+                                        p_dev_rec->sec_bd_name, link_key,
                                         p_dev_rec->link_key_type);
     }
   } else {
@@ -4675,7 +4666,7 @@
             p_dev_rec->link_key_type);
       } else {
         (*btm_cb.api.p_link_key_callback)(p_bda, p_dev_rec->dev_class,
-                                          p_dev_rec->sec_bd_name, p_link_key,
+                                          p_dev_rec->sec_bd_name, link_key,
                                           p_dev_rec->link_key_type);
       }
     }
@@ -4695,7 +4686,6 @@
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(bda);
 
   VLOG(2) << __func__ << " bda: " << bda;
-  p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
 
   if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) &&
       (btm_cb.collision_start_time != 0) &&
diff --git a/stack/btu/btu_hcif.cc b/stack/btu/btu_hcif.cc
index eac71a6..0f54a69 100644
--- a/stack/btu/btu_hcif.cc
+++ b/stack/btu/btu_hcif.cc
@@ -1381,11 +1381,11 @@
  ******************************************************************************/
 static void btu_hcif_link_key_notification_evt(uint8_t* p) {
   RawAddress bda;
-  LINK_KEY key;
+  Octet16 key;
   uint8_t key_type;
 
   STREAM_TO_BDADDR(bda, p);
-  STREAM_TO_ARRAY16(key, p);
+  STREAM_TO_ARRAY16(key.data(), p);
   STREAM_TO_UINT8(key_type, p);
 
   btm_sec_link_key_notification(bda, key, key_type);
diff --git a/stack/smp/aes.cc b/stack/crypto_toolbox/aes.cc
similarity index 99%
rename from stack/smp/aes.cc
rename to stack/crypto_toolbox/aes.cc
index 362a4ee..f53894e 100644
--- a/stack/smp/aes.cc
+++ b/stack/crypto_toolbox/aes.cc
@@ -43,12 +43,9 @@
 #endif
 #endif
 
+#include <stdint.h>
 #include <stdlib.h>
 
-/* add the target configuration to allow using internal data types and
- * compilation options */
-#include "bt_target.h"
-
 /* define if you have fast 32-bit types on your system */
 #if 1
 #define HAVE_UINT_32T
diff --git a/stack/smp/aes.h b/stack/crypto_toolbox/aes.h
similarity index 100%
rename from stack/smp/aes.h
rename to stack/crypto_toolbox/aes.h
diff --git a/stack/crypto_toolbox/aes_cmac.cc b/stack/crypto_toolbox/aes_cmac.cc
new file mode 100644
index 0000000..8b8246e
--- /dev/null
+++ b/stack/crypto_toolbox/aes_cmac.cc
@@ -0,0 +1,217 @@
+/******************************************************************************
+ *
+ *  Copyright 2008-2012 Broadcom Corporation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ *  This file contains the implementation of the AES128 and AES CMAC algorithm.
+ *
+ ******************************************************************************/
+
+#include "stack/crypto_toolbox/aes.h"
+#include "stack/crypto_toolbox/crypto_toolbox.h"
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+
+namespace crypto_toolbox {
+
+namespace {
+
+typedef struct {
+  uint8_t* text;
+  uint16_t len;
+  uint16_t round;
+} tCMAC_CB;
+
+tCMAC_CB cmac_cb;
+
+/* Rb for AES-128 as block cipher, LSB as [0] */
+Octet16 const_Rb{0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+/** utility function to do an biteise exclusive-OR of two bit strings of the
+ * length of OCTET16_LEN. Result is stored in first argument.
+ */
+static void xor_128(Octet16* a, const Octet16& b) {
+  CHECK(a);
+  uint8_t i, *aa = a->data();
+  const uint8_t* bb = b.data();
+
+  for (i = 0; i < OCTET16_LEN; i++) {
+    aa[i] = aa[i] ^ bb[i];
+  }
+}
+}  // namespace
+
+/* This function computes AES_128(key, message) */
+Octet16 aes_128(const Octet16& key, const Octet16& message) {
+  Octet16 key_reversed;
+  Octet16 message_reversed;
+  Octet16 output;
+
+  std::reverse_copy(key.begin(), key.end(), key_reversed.begin());
+  std::reverse_copy(message.begin(), message.end(), message_reversed.begin());
+
+  aes_context ctx;
+  aes_set_key(key_reversed.data(), key_reversed.size(), &ctx);
+  aes_encrypt(message_reversed.data(), output.data(), &ctx);
+
+  std::reverse(output.begin(), output.end());
+  return output;
+}
+
+/** utility function to padding the given text to be a 128 bits data. The
+ * parameter dest is input and output parameter, it must point to a
+ * OCTET16_LEN memory space; where include length bytes valid data. */
+static void padding(Octet16* dest, uint8_t length) {
+  uint8_t i, *p = dest->data();
+  /* original last block */
+  for (i = length; i < OCTET16_LEN; i++)
+    p[OCTET16_LEN - i - 1] = (i == length) ? 0x80 : 0;
+}
+
+/** utility function to left shift one bit for a 128 bits value. */
+static void leftshift_onebit(uint8_t* input, uint8_t* output) {
+  uint8_t i, overflow = 0, next_overflow = 0;
+  DVLOG(2) << __func__;
+  /* input[0] is LSB */
+  for (i = 0; i < OCTET16_LEN; i++) {
+    next_overflow = (input[i] & 0x80) ? 1 : 0;
+    output[i] = (input[i] << 1) | overflow;
+    overflow = next_overflow;
+  }
+  return;
+}
+
+/** This function is the calculation of block cipher using AES-128. */
+static Octet16 cmac_aes_k_calculate(const Octet16& key) {
+  Octet16 output;
+  Octet16 x{0};  // zero initialized
+
+  DVLOG(2) << __func__;
+
+  uint8_t i = 1;
+  while (i <= cmac_cb.round) {
+    /* Mi' := Mi (+) X  */
+    xor_128((Octet16*)&cmac_cb.text[(cmac_cb.round - i) * OCTET16_LEN], x);
+
+    output = aes_128(key, &cmac_cb.text[(cmac_cb.round - i) * OCTET16_LEN],
+                     OCTET16_LEN);
+    x = output;
+    i++;
+  }
+
+  return output;
+}
+
+/** This function proceeed to prepare the last block of message Mn depending on
+ * the size of the message.
+ */
+static void cmac_prepare_last_block(const Octet16& k1, const Octet16& k2) {
+  //    uint8_t     x[16] = {0};
+  bool flag;
+
+  DVLOG(2) << __func__;
+  /* last block is a complete block set flag to 1 */
+  flag = ((cmac_cb.len % OCTET16_LEN) == 0 && cmac_cb.len != 0) ? true : false;
+
+  DVLOG(2) << "flag=" << flag << " round=" << cmac_cb.round;
+
+  if (flag) { /* last block is complete block */
+    xor_128((Octet16*)&cmac_cb.text[0], k1);
+  } else /* padding then xor with k2 */
+  {
+    padding((Octet16*)&cmac_cb.text[0], (uint8_t)(cmac_cb.len % 16));
+
+    xor_128((Octet16*)&cmac_cb.text[0], k2);
+  }
+}
+
+/** This is the function to generate the two subkeys.
+ * |key| is CMAC key, expect SRK when used by SMP.
+ */
+static void cmac_generate_subkey(const Octet16& key) {
+  DVLOG(2) << __func__;
+
+  Octet16 zero{};
+  Octet16 p = aes_128(key, zero.data(), OCTET16_LEN);
+
+  Octet16 k1, k2;
+  uint8_t* pp = p.data();
+
+  /* If MSB(L) = 0, then K1 = L << 1 */
+  if ((pp[OCTET16_LEN - 1] & 0x80) != 0) {
+    /* Else K1 = ( L << 1 ) (+) Rb */
+    leftshift_onebit(pp, k1.data());
+    xor_128(&k1, const_Rb);
+  } else {
+    leftshift_onebit(pp, k1.data());
+  }
+
+  if ((k1[OCTET16_LEN - 1] & 0x80) != 0) {
+    /* K2 =  (K1 << 1) (+) Rb */
+    leftshift_onebit(k1.data(), k2.data());
+    xor_128(&k2, const_Rb);
+  } else {
+    /* If MSB(K1) = 0, then K2 = K1 << 1 */
+    leftshift_onebit(k1.data(), k2.data());
+  }
+
+  cmac_prepare_last_block(k1, k2);
+}
+
+/** key - CMAC key in little endian order
+ *  input - text to be signed in little endian byte order.
+ *  length - length of the input in byte.
+ */
+Octet16 aes_cmac(const Octet16& key, const uint8_t* input, uint16_t length) {
+  uint16_t len, diff;
+  /* n is number of rounds */
+  uint16_t n = (length + OCTET16_LEN - 1) / OCTET16_LEN;
+
+  DVLOG(2) << __func__;
+
+  if (n == 0) n = 1;
+  len = n * OCTET16_LEN;
+
+  DVLOG(2) << "AES128_CMAC started, allocate buffer size=" << len;
+  /* allocate a memory space of multiple of 16 bytes to hold text  */
+  cmac_cb.text = (uint8_t*)alloca(len);
+  cmac_cb.round = n;
+  diff = len - length;
+
+  if (input != NULL && length > 0) {
+    memcpy(&cmac_cb.text[diff], input, (int)length);
+    cmac_cb.len = length;
+  } else {
+    cmac_cb.len = 0;
+  }
+
+  /* prepare calculation for subkey s and last block of data */
+  cmac_generate_subkey(key);
+  /* start calculation */
+  Octet16 signature = cmac_aes_k_calculate(key);
+
+  /* clean up */
+  memset(&cmac_cb, 0, sizeof(tCMAC_CB));
+  // cmac_cb.text is auto-freed by alloca
+
+  return signature;
+}
+
+}  // namespace crypto_toolbox
diff --git a/stack/crypto_toolbox/crypto_toolbox.cc b/stack/crypto_toolbox/crypto_toolbox.cc
new file mode 100644
index 0000000..ae33bd9
--- /dev/null
+++ b/stack/crypto_toolbox/crypto_toolbox.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stack/crypto_toolbox/crypto_toolbox.h"
+#include "stack/crypto_toolbox/aes.h"
+
+#include <algorithm>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+
+using base::HexEncode;
+
+namespace crypto_toolbox {
+
+Octet16 h6(const Octet16& w, std::array<uint8_t, 4> keyid) {
+  return aes_cmac(w, keyid.data(), keyid.size());
+}
+
+Octet16 h7(const Octet16& salt, const Octet16& w) {
+  return aes_cmac(salt, w.data(), w.size());
+}
+
+Octet16 f4(uint8_t* u, uint8_t* v, const Octet16& x, uint8_t z) {
+  constexpr size_t msg_len = BT_OCTET32_LEN /* U size */ +
+                             BT_OCTET32_LEN /* V size */ + 1 /* Z size */;
+
+  DVLOG(2) << "U=" << HexEncode(u, BT_OCTET32_LEN)
+           << ", V=" << HexEncode(v, BT_OCTET32_LEN)
+           << ", X=" << HexEncode(x.data(), x.size()) << ", Z=" << std::hex
+           << +z;
+
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(&z, &z + 1, it);
+  it = std::copy(v, v + BT_OCTET32_LEN, it);
+  it = std::copy(u, u + BT_OCTET32_LEN, it);
+  return aes_cmac(x, msg.data(), msg.size());
+}
+
+/** helper for f5 */
+static Octet16 calculate_mac_key_or_ltk(const Octet16& t, uint8_t counter,
+                                        uint8_t* key_id, const Octet16& n1,
+                                        const Octet16& n2, uint8_t* a1,
+                                        uint8_t* a2, uint8_t* length) {
+  constexpr size_t msg_len = 1 /* Counter size */ + 4 /* keyID size */ +
+                             OCTET16_LEN /* N1 size */ +
+                             OCTET16_LEN /* N2 size */ + 7 /* A1 size*/ +
+                             7 /* A2 size*/ + 2 /* Length size */;
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(length, length + 2, it);
+  it = std::copy(a2, a2 + 7, it);
+  it = std::copy(a1, a1 + 7, it);
+  it = std::copy(n2.begin(), n2.end(), it);
+  it = std::copy(n1.begin(), n1.end(), it);
+  it = std::copy(key_id, key_id + 4, it);
+  it = std::copy(&counter, &counter + 1, it);
+
+  return aes_cmac(t, msg.data(), msg.size());
+}
+
+void f5(uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1,
+        uint8_t* a2, Octet16* mac_key, Octet16* ltk) {
+  DVLOG(2) << __func__ << "W=" << HexEncode(w, BT_OCTET32_LEN)
+           << ", N1=" << HexEncode(n1.data(), n1.size())
+           << ", N2=" << HexEncode(n2.data(), n2.size())
+           << ", A1=" << HexEncode(a1, 7) << ", A2=" << HexEncode(a2, 7);
+
+  const Octet16 salt{0xBE, 0x83, 0x60, 0x5A, 0xDB, 0x0B, 0x37, 0x60,
+                     0x38, 0xA5, 0xF5, 0xAA, 0x91, 0x83, 0x88, 0x6C};
+  Octet16 t = aes_cmac(salt, w, BT_OCTET32_LEN);
+
+  DVLOG(2) << "T=" << HexEncode(t.data(), t.size());
+
+  uint8_t key_id[4] = {0x65, 0x6c, 0x74, 0x62}; /* 0x62746c65 */
+  uint8_t length[2] = {0x00, 0x01};             /* 0x0100 */
+
+  *mac_key = calculate_mac_key_or_ltk(t, 0, key_id, n1, n2, a1, a2, length);
+
+  *ltk = calculate_mac_key_or_ltk(t, 1, key_id, n1, n2, a1, a2, length);
+
+  DVLOG(2) << "mac_key=" << HexEncode(mac_key->data(), mac_key->size());
+  DVLOG(2) << "ltk=" << HexEncode(ltk->data(), ltk->size());
+}
+
+Octet16 f6(const Octet16& w, const Octet16& n1, const Octet16& n2,
+           const Octet16& r, uint8_t* iocap, uint8_t* a1, uint8_t* a2) {
+  const uint8_t msg_len = OCTET16_LEN /* N1 size */ +
+                          OCTET16_LEN /* N2 size */ + OCTET16_LEN /* R size */ +
+                          3 /* IOcap size */ + 7 /* A1 size*/
+                          + 7 /* A2 size*/;
+
+  DVLOG(2) << __func__ << "W=" << HexEncode(w.data(), w.size())
+           << ", N1=" << HexEncode(n1.data(), n1.size())
+           << ", N2=" << HexEncode(n2.data(), n2.size())
+           << ", R=" << HexEncode(r.data(), r.size())
+           << ", IOcap=" << HexEncode(iocap, 3) << ", A1=" << HexEncode(a1, 7)
+           << ", A2=" << HexEncode(a2, 7);
+
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(a2, a2 + 7, it);
+  it = std::copy(a1, a1 + 7, it);
+  it = std::copy(iocap, iocap + 3, it);
+  it = std::copy(r.begin(), r.end(), it);
+  it = std::copy(n2.begin(), n2.end(), it);
+  it = std::copy(n1.begin(), n1.end(), it);
+
+  return aes_cmac(w, msg.data(), msg.size());
+}
+
+uint32_t g2(uint8_t* u, uint8_t* v, const Octet16& x, const Octet16& y) {
+  constexpr size_t msg_len = BT_OCTET32_LEN /* U size */ +
+                             BT_OCTET32_LEN /* V size */
+                             + OCTET16_LEN /* Y size */;
+
+  DVLOG(2) << __func__ << "U=" << HexEncode(u, BT_OCTET32_LEN)
+           << ", V=" << HexEncode(v, BT_OCTET32_LEN)
+           << ", X=" << HexEncode(x.data(), x.size())
+           << ", Y=" << HexEncode(y.data(), y.size());
+
+  std::array<uint8_t, msg_len> msg;
+  auto it = msg.begin();
+  it = std::copy(y.begin(), y.end(), it);
+  it = std::copy(v, v + BT_OCTET32_LEN, it);
+  it = std::copy(u, u + BT_OCTET32_LEN, it);
+
+  Octet16 cmac = aes_cmac(x, msg.data(), msg.size());
+
+  /* vres = cmac mod 2**32 mod 10**6 */
+  uint32_t vres;
+  uint8_t* p = cmac.data();
+  STREAM_TO_UINT32(vres, p);
+
+  vres = vres % 1000000;
+  return vres;
+}
+
+Octet16 ltk_to_link_key(const Octet16& ltk, bool use_h7) {
+  Octet16 ilk; /* intermidiate link key */
+  if (use_h7) {
+    constexpr Octet16 salt{0x31, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    ilk = h7(salt, ltk);
+  } else {
+    /* "tmp1" mapping to extended ASCII, little endian*/
+    constexpr std::array<uint8_t, 4> keyID_tmp1 = {0x31, 0x70, 0x6D, 0x74};
+    ilk = h6(ltk, keyID_tmp1);
+  }
+
+  /* "lebr" mapping to extended ASCII, little endian */
+  constexpr std::array<uint8_t, 4> keyID_lebr = {0x72, 0x62, 0x65, 0x6c};
+  return h6(ilk, keyID_lebr);
+}
+
+Octet16 link_key_to_ltk(const Octet16& link_key, bool use_h7) {
+  Octet16 iltk; /* intermidiate long term key */
+  if (use_h7) {
+    constexpr Octet16 salt{0x32, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
+                           0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    iltk = h7(salt, link_key);
+  } else {
+    /* "tmp2" mapping to extended ASCII, little endian */
+    constexpr std::array<uint8_t, 4> keyID_tmp2 = {0x32, 0x70, 0x6D, 0x74};
+    iltk = h6(link_key, keyID_tmp2);
+  }
+
+  /* "brle" mapping to extended ASCII, little endian */
+  constexpr std::array<uint8_t, 4> keyID_brle = {0x65, 0x6c, 0x72, 0x62};
+  return h6(iltk, keyID_brle);
+}
+
+}  // namespace crypto_toolbox
diff --git a/stack/crypto_toolbox/crypto_toolbox.h b/stack/crypto_toolbox/crypto_toolbox.h
new file mode 100644
index 0000000..90d7392
--- /dev/null
+++ b/stack/crypto_toolbox/crypto_toolbox.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "stack/include/bt_types.h"
+
+namespace crypto_toolbox {
+
+extern Octet16 aes_128(const Octet16& key, const Octet16& message);
+extern Octet16 aes_cmac(const Octet16& key, const uint8_t* message,
+                        uint16_t length);
+extern Octet16 f4(uint8_t* u, uint8_t* v, const Octet16& x, uint8_t z);
+extern void f5(uint8_t* w, const Octet16& n1, const Octet16& n2, uint8_t* a1,
+               uint8_t* a2, Octet16* mac_key, Octet16* ltk);
+extern Octet16 f6(const Octet16& w, const Octet16& n1, const Octet16& n2,
+                  const Octet16& r, uint8_t* iocap, uint8_t* a1, uint8_t* a2);
+extern Octet16 h6(const Octet16& w, std::array<uint8_t, 4> keyid);
+extern Octet16 h7(const Octet16& salt, const Octet16& w);
+extern uint32_t g2(uint8_t* u, uint8_t* v, const Octet16& x, const Octet16& y);
+extern Octet16 ltk_to_link_key(const Octet16& ltk, bool use_h7);
+extern Octet16 link_key_to_ltk(const Octet16& link_key, bool use_h7);
+
+/* This function computes AES_128(key, message). |key| must be 128bit.
+ * |message| can be at most 16 bytes long, it's length in bytes is given in
+ * |length| */
+inline Octet16 aes_128(const Octet16& key, const uint8_t* message,
+                       const uint8_t length) {
+  CHECK(length <= OCTET16_LEN) << "you tried aes_128 more than 16 bytes!";
+  Octet16 msg{0};
+  std::copy(message, message + length, msg.begin());
+  return aes_128(key, msg);
+}
+
+// |tlen| - lenth of mac desired
+// |p_signature| - data pointer to where signed data to be stored, tlen long.
+inline void aes_cmac(const Octet16& key, const uint8_t* message,
+                     uint16_t length, uint16_t tlen, uint8_t* p_signature) {
+  Octet16 signature = aes_cmac(key, message, length);
+
+  uint8_t* p_mac = signature.data() + (OCTET16_LEN - tlen);
+  memcpy(p_signature, p_mac, tlen);
+}
+
+inline Octet16 aes_cmac(const Octet16& key, const Octet16& message) {
+  return aes_cmac(key, message.data(), message.size());
+}
+
+}  // namespace crypto_toolbox
\ No newline at end of file
diff --git a/stack/gatt/gatt_api.cc b/stack/gatt/gatt_api.cc
index 93a35ca..2d37e9d 100644
--- a/stack/gatt/gatt_api.cc
+++ b/stack/gatt/gatt_api.cc
@@ -613,20 +613,22 @@
  *
  * Parameters       conn_id: connection identifier.
  *                  disc_type:discovery type.
- *                  p_param: parameters of discovery requirement.
+ *                  start_handle and end_handle: range of handles for discovery
+ *                  uuid: uuid to discovery. set to Uuid::kEmpty for requests
+ *                        that don't need it
  *
  * Returns          GATT_SUCCESS if command received/sent successfully.
  *
  ******************************************************************************/
 tGATT_STATUS GATTC_Discover(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
-                            tGATT_DISC_PARAM* p_param) {
+                            uint16_t start_handle, uint16_t end_handle,
+                            const Uuid& uuid) {
   tGATT_IF gatt_if = GATT_GET_GATT_IF(conn_id);
   uint8_t tcb_idx = GATT_GET_TCB_IDX(conn_id);
   tGATT_TCB* p_tcb = gatt_get_tcb_by_idx(tcb_idx);
   tGATT_REG* p_reg = gatt_get_regcb(gatt_if);
 
-  if ((p_tcb == NULL) || (p_reg == NULL) || (p_param == NULL) ||
-      (disc_type >= GATT_DISC_MAX)) {
+  if ((p_tcb == NULL) || (p_reg == NULL) || (disc_type >= GATT_DISC_MAX)) {
     LOG(ERROR) << __func__ << " Illegal param: disc_type=" << +disc_type
                << " conn_id=" << loghex(conn_id);
     return GATT_ILLEGAL_PARAMETER;
@@ -634,13 +636,13 @@
 
   LOG(INFO) << __func__ << " conn_id=" << loghex(conn_id)
             << ", disc_type=" << +disc_type
-            << ", s_handle=" << loghex(p_param->s_handle)
-            << ", e_handle=" << loghex(p_param->e_handle);
+            << ", s_handle=" << loghex(start_handle)
+            << ", e_handle=" << loghex(end_handle);
 
-  if (!GATT_HANDLE_IS_VALID(p_param->s_handle) ||
-      !GATT_HANDLE_IS_VALID(p_param->e_handle) ||
+  if (!GATT_HANDLE_IS_VALID(start_handle) ||
+      !GATT_HANDLE_IS_VALID(end_handle) ||
       /* search by type does not have a valid UUID param */
-      (disc_type == GATT_DISC_SRVC_BY_UUID && p_param->service.IsEmpty())) {
+      (disc_type == GATT_DISC_SRVC_BY_UUID && uuid.IsEmpty())) {
     return GATT_ILLEGAL_PARAMETER;
   }
 
@@ -654,14 +656,20 @@
 
   p_clcb->operation = GATTC_OPTYPE_DISCOVERY;
   p_clcb->op_subtype = disc_type;
-  p_clcb->s_handle = p_param->s_handle;
-  p_clcb->e_handle = p_param->e_handle;
-  p_clcb->uuid = p_param->service;
+  p_clcb->s_handle = start_handle;
+  p_clcb->e_handle = end_handle;
+  p_clcb->uuid = uuid;
 
   gatt_act_discovery(p_clcb);
   return GATT_SUCCESS;
 }
 
+tGATT_STATUS GATTC_Discover(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
+                            uint16_t start_handle, uint16_t end_handle) {
+  return GATTC_Discover(conn_id, disc_type, start_handle, end_handle,
+                        Uuid::kEmpty);
+}
+
 /*******************************************************************************
  *
  * Function         GATTC_Read
diff --git a/stack/gatt/gatt_attr.cc b/stack/gatt/gatt_attr.cc
index 75a8057..8861a7a 100644
--- a/stack/gatt/gatt_attr.cc
+++ b/stack/gatt/gatt_attr.cc
@@ -385,41 +385,35 @@
  *
  ******************************************************************************/
 static void gatt_cl_start_config_ccc(tGATT_PROFILE_CLCB* p_clcb) {
-  tGATT_DISC_PARAM srvc_disc_param;
-  tGATT_VALUE ccc_value;
 
   VLOG(1) << __func__ << ": stage: " << +p_clcb->ccc_stage;
 
-  memset(&srvc_disc_param, 0, sizeof(tGATT_DISC_PARAM));
-  memset(&ccc_value, 0, sizeof(tGATT_VALUE));
-
   switch (p_clcb->ccc_stage) {
     case GATT_SVC_CHANGED_SERVICE: /* discover GATT service */
-      srvc_disc_param.s_handle = 1;
-      srvc_disc_param.e_handle = 0xffff;
-      srvc_disc_param.service = Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER);
-      GATTC_Discover(p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, &srvc_disc_param);
+      GATTC_Discover(p_clcb->conn_id, GATT_DISC_SRVC_BY_UUID, 0x0001, 0xffff,
+                     Uuid::From16Bit(UUID_SERVCLASS_GATT_SERVER));
       break;
 
     case GATT_SVC_CHANGED_CHARACTERISTIC: /* discover service change char */
-      srvc_disc_param.s_handle = 1;
-      srvc_disc_param.e_handle = p_clcb->e_handle;
-      srvc_disc_param.service = Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD);
-      GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR, &srvc_disc_param);
+      GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR, 0x0001, p_clcb->e_handle,
+                     Uuid::From16Bit(GATT_UUID_GATT_SRV_CHGD));
       break;
 
     case GATT_SVC_CHANGED_DESCRIPTOR: /* discover service change ccc */
-      srvc_disc_param.s_handle = p_clcb->s_handle;
-      srvc_disc_param.e_handle = p_clcb->e_handle;
-      GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, &srvc_disc_param);
+      GATTC_Discover(p_clcb->conn_id, GATT_DISC_CHAR_DSCPT, p_clcb->s_handle,
+                     p_clcb->e_handle);
       break;
 
     case GATT_SVC_CHANGED_CONFIGURE_CCCD: /* write ccc */
+    {
+      tGATT_VALUE ccc_value;
+      memset(&ccc_value, 0, sizeof(tGATT_VALUE));
       ccc_value.handle = p_clcb->s_handle;
       ccc_value.len = 2;
       ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION;
       GATTC_Write(p_clcb->conn_id, GATT_WRITE, &ccc_value);
       break;
+    }
   }
 }
 
diff --git a/stack/gatt/gatt_sr.cc b/stack/gatt/gatt_sr.cc
index 35bd993..008702c 100644
--- a/stack/gatt/gatt_sr.cc
+++ b/stack/gatt/gatt_sr.cc
@@ -327,7 +327,7 @@
   } else /* nothing needs to be executed , send response now */
   {
     LOG(ERROR) << "gatt_process_exec_write_req: no prepare write pending";
-    gatt_send_error_rsp(tcb, GATT_INVALID_OFFSET, GATT_REQ_EXEC_WRITE, 0, false);
+    gatt_send_error_rsp(tcb, GATT_ERROR, GATT_REQ_EXEC_WRITE, 0, false);
   }
 }
 
diff --git a/stack/hcic/hciblecmds.cc b/stack/hcic/hciblecmds.cc
index 30d8d75..40c89e1 100644
--- a/stack/hcic/hciblecmds.cc
+++ b/stack/hcic/hciblecmds.cc
@@ -405,8 +405,7 @@
 
 void btsnd_hcic_ble_start_enc(uint16_t handle,
                               uint8_t rand[HCIC_BLE_RAND_DI_SIZE],
-                              uint16_t ediv,
-                              uint8_t ltk[HCIC_BLE_ENCRYT_KEY_SIZE]) {
+                              uint16_t ediv, const Octet16& ltk) {
   BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
   uint8_t* pp = (uint8_t*)(p + 1);
 
@@ -419,13 +418,12 @@
   UINT16_TO_STREAM(pp, handle);
   ARRAY_TO_STREAM(pp, rand, HCIC_BLE_RAND_DI_SIZE);
   UINT16_TO_STREAM(pp, ediv);
-  ARRAY_TO_STREAM(pp, ltk, HCIC_BLE_ENCRYT_KEY_SIZE);
+  ARRAY_TO_STREAM(pp, ltk.data(), HCIC_BLE_ENCRYT_KEY_SIZE);
 
   btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
 }
 
-void btsnd_hcic_ble_ltk_req_reply(uint16_t handle,
-                                  uint8_t ltk[HCIC_BLE_ENCRYT_KEY_SIZE]) {
+void btsnd_hcic_ble_ltk_req_reply(uint16_t handle, const Octet16& ltk) {
   BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
   uint8_t* pp = (uint8_t*)(p + 1);
 
@@ -436,7 +434,7 @@
   UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_LTK_REQ_REPLY);
 
   UINT16_TO_STREAM(pp, handle);
-  ARRAY_TO_STREAM(pp, ltk, HCIC_BLE_ENCRYT_KEY_SIZE);
+  ARRAY_TO_STREAM(pp, ltk.data(), HCIC_BLE_ENCRYT_KEY_SIZE);
 
   btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
 }
@@ -560,9 +558,10 @@
 }
 #endif
 
-void btsnd_hcic_ble_add_device_resolving_list(
-    uint8_t addr_type_peer, const RawAddress& bda_peer,
-    uint8_t irk_peer[HCIC_BLE_IRK_SIZE], uint8_t irk_local[HCIC_BLE_IRK_SIZE]) {
+void btsnd_hcic_ble_add_device_resolving_list(uint8_t addr_type_peer,
+                                              const RawAddress& bda_peer,
+                                              const Octet16& irk_peer,
+                                              const Octet16& irk_local) {
   BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
   uint8_t* pp = (uint8_t*)(p + 1);
 
@@ -573,8 +572,8 @@
   UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_BLE_ADD_DEV_RESOLVING_LIST);
   UINT8_TO_STREAM(pp, addr_type_peer);
   BDADDR_TO_STREAM(pp, bda_peer);
-  ARRAY_TO_STREAM(pp, irk_peer, HCIC_BLE_ENCRYT_KEY_SIZE);
-  ARRAY_TO_STREAM(pp, irk_local, HCIC_BLE_ENCRYT_KEY_SIZE);
+  ARRAY_TO_STREAM(pp, irk_peer.data(), HCIC_BLE_ENCRYT_KEY_SIZE);
+  ARRAY_TO_STREAM(pp, irk_local.data(), HCIC_BLE_ENCRYT_KEY_SIZE);
 
   btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
 }
diff --git a/stack/hcic/hcicmds.cc b/stack/hcic/hcicmds.cc
index 56ff381..ebed273 100644
--- a/stack/hcic/hcicmds.cc
+++ b/stack/hcic/hcicmds.cc
@@ -207,7 +207,7 @@
 }
 
 void btsnd_hcic_link_key_req_reply(const RawAddress& bd_addr,
-                                   LINK_KEY link_key) {
+                                   const LinkKey& link_key) {
   BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
   uint8_t* pp = (uint8_t*)(p + 1);
 
@@ -218,7 +218,7 @@
   UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY);
 
   BDADDR_TO_STREAM(pp, bd_addr);
-  ARRAY16_TO_STREAM(pp, link_key);
+  ARRAY16_TO_STREAM(pp, link_key.data());
 
   btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
 }
@@ -1196,8 +1196,8 @@
   btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
 }
 
-void btsnd_hcic_rem_oob_reply(const RawAddress& bd_addr, uint8_t* p_c,
-                              uint8_t* p_r) {
+void btsnd_hcic_rem_oob_reply(const RawAddress& bd_addr, const Octet16& c,
+                              const Octet16& r) {
   BT_HDR* p = (BT_HDR*)osi_malloc(HCI_CMD_BUF_SIZE);
   uint8_t* pp = (uint8_t*)(p + 1);
 
@@ -1208,8 +1208,8 @@
   UINT8_TO_STREAM(pp, HCIC_PARAM_SIZE_REM_OOB_REPLY);
 
   BDADDR_TO_STREAM(pp, bd_addr);
-  ARRAY16_TO_STREAM(pp, p_c);
-  ARRAY16_TO_STREAM(pp, p_r);
+  ARRAY16_TO_STREAM(pp, c.data());
+  ARRAY16_TO_STREAM(pp, r.data());
 
   btu_hcif_send_cmd(LOCAL_BR_EDR_CONTROLLER_ID, p);
 }
diff --git a/stack/include/bt_types.h b/stack/include/bt_types.h
index 54a2eb2..6844dad 100644
--- a/stack/include/bt_types.h
+++ b/stack/include/bt_types.h
@@ -561,15 +561,22 @@
 #define BT_OCTET8_LEN 8
 typedef uint8_t BT_OCTET8[BT_OCTET8_LEN]; /* octet array: size 16 */
 
-#define LINK_KEY_LEN 16
-typedef uint8_t LINK_KEY[LINK_KEY_LEN]; /* Link Key */
-
 #define AMP_LINK_KEY_LEN 32
 typedef uint8_t
     AMP_LINK_KEY[AMP_LINK_KEY_LEN]; /* Dedicated AMP and GAMP Link Keys */
 
-#define BT_OCTET16_LEN 16
-typedef uint8_t BT_OCTET16[BT_OCTET16_LEN]; /* octet array: size 16 */
+/* Some C files include this header file */
+#ifdef __cplusplus
+
+#include <array>
+
+constexpr int OCTET16_LEN = 16;
+typedef std::array<uint8_t, OCTET16_LEN> Octet16;
+
+constexpr int LINK_KEY_LEN = OCTET16_LEN;
+typedef Octet16 LinkKey; /* Link Key */
+
+#endif
 
 #define PIN_CODE_LEN 16
 typedef uint8_t PIN_CODE[PIN_CODE_LEN]; /* Pin Code (upto 128 bits) MSB is 0 */
diff --git a/stack/include/btm_api.h b/stack/include/btm_api.h
index 146a1b8..457ae3d 100644
--- a/stack/include/btm_api.h
+++ b/stack/include/btm_api.h
@@ -1407,7 +1407,7 @@
  ******************************************************************************/
 extern bool BTM_SecAddDevice(const RawAddress& bd_addr, DEV_CLASS dev_class,
                              BD_NAME bd_name, uint8_t* features,
-                             uint32_t trusted_mask[], LINK_KEY link_key,
+                             uint32_t trusted_mask[], LinkKey* link_key,
                              uint8_t key_type, tBTM_IO_CAP io_cap,
                              uint8_t pin_length);
 
@@ -1445,7 +1445,7 @@
  *
  ******************************************************************************/
 extern tBTM_STATUS BTM_SecGetDeviceLinkKey(const RawAddress& bd_addr,
-                                           LINK_KEY link_key);
+                                           LinkKey* link_key);
 
 /*******************************************************************************
  *
@@ -1661,7 +1661,7 @@
  *
  ******************************************************************************/
 extern void BTM_RemoteOobDataReply(tBTM_STATUS res, const RawAddress& bd_addr,
-                                   BT_OCTET16 c, BT_OCTET16 r);
+                                   const Octet16& c, const Octet16& r);
 
 /*******************************************************************************
  *
@@ -1682,7 +1682,8 @@
  *
  ******************************************************************************/
 extern uint16_t BTM_BuildOobData(uint8_t* p_data, uint16_t max_len,
-                                 BT_OCTET16 c, BT_OCTET16 r, uint8_t name_len);
+                                 const Octet16& c, const Octet16& r,
+                                 uint8_t name_len);
 
 /*******************************************************************************
  *
diff --git a/stack/include/btm_api_types.h b/stack/include/btm_api_types.h
index df7b64d..1ae47fe 100644
--- a/stack/include/btm_api_types.h
+++ b/stack/include/btm_api_types.h
@@ -1330,8 +1330,8 @@
 */
 typedef uint8_t(tBTM_LINK_KEY_CALLBACK)(const RawAddress& bd_addr,
                                         DEV_CLASS dev_class,
-                                        tBTM_BD_NAME bd_name, uint8_t* key,
-                                        uint8_t key_type);
+                                        tBTM_BD_NAME bd_name,
+                                        const LinkKey& key, uint8_t key_type);
 
 /* Remote Name Resolved.  Parameters are
  *              BD Address of remote
@@ -1478,8 +1478,8 @@
 /* data type for BTM_SP_LOC_OOB_EVT */
 typedef struct {
   tBTM_STATUS status; /* */
-  BT_OCTET16 c;       /* Simple Pairing Hash C */
-  BT_OCTET16 r;       /* Simple Pairing Randomnizer R */
+  Octet16 c;          /* Simple Pairing Hash C */
+  Octet16 r;          /* Simple Pairing Randomnizer R */
 } tBTM_SP_LOC_OOB;
 
 /* data type for BTM_SP_RMT_OOB_EVT */
@@ -1629,7 +1629,7 @@
 
 /* BLE encryption keys */
 typedef struct {
-  BT_OCTET16 ltk;
+  Octet16 ltk;
   BT_OCTET8 rand;
   uint16_t ediv;
   uint8_t sec_level;
@@ -1639,13 +1639,13 @@
 /* BLE CSRK keys */
 typedef struct {
   uint32_t counter;
-  BT_OCTET16 csrk;
+  Octet16 csrk;
   uint8_t sec_level;
 } tBTM_LE_PCSRK_KEYS;
 
 /* BLE Encryption reproduction keys */
 typedef struct {
-  BT_OCTET16 ltk;
+  Octet16 ltk;
   uint16_t div;
   uint8_t key_size;
   uint8_t sec_level;
@@ -1656,11 +1656,11 @@
   uint32_t counter;
   uint16_t div;
   uint8_t sec_level;
-  BT_OCTET16 csrk;
+  Octet16 csrk;
 } tBTM_LE_LCSRK_KEYS;
 
 typedef struct {
-  BT_OCTET16 irk;
+  Octet16 irk;
   tBLE_ADDR_TYPE addr_type;
   RawAddress static_addr;
 } tBTM_LE_PID_KEYS;
@@ -1702,15 +1702,15 @@
 #define BTM_BLE_KEY_TYPE_COUNTER 3  // tobe obsolete
 
 typedef struct {
-  BT_OCTET16 ir;
-  BT_OCTET16 irk;
-  BT_OCTET16 dhk;
+  Octet16 ir;
+  Octet16 irk;
+  Octet16 dhk;
 
 } tBTM_BLE_LOCAL_ID_KEYS;
 
 typedef union {
   tBTM_BLE_LOCAL_ID_KEYS id_keys;
-  BT_OCTET16 er;
+  Octet16 er;
 } tBTM_BLE_LOCAL_KEYS;
 
 /* New LE identity key for local device.
@@ -1806,63 +1806,6 @@
 
 } tBTM_DELETE_STORED_LINK_KEY_COMPLETE;
 
-/* MIP evnets, callbacks    */
-enum {
-  BTM_MIP_MODE_CHG_EVT,
-  BTM_MIP_DISCONNECT_EVT,
-  BTM_MIP_PKTS_COMPL_EVT,
-  BTM_MIP_RXDATA_EVT
-};
-typedef uint8_t tBTM_MIP_EVT;
-
-typedef struct {
-  tBTM_MIP_EVT event;
-  RawAddress bd_addr;
-  uint16_t mip_id;
-} tBTM_MIP_MODE_CHANGE;
-
-typedef struct {
-  tBTM_MIP_EVT event;
-  uint16_t mip_id;
-  uint8_t disc_reason;
-} tBTM_MIP_CONN_TIMEOUT;
-
-#define BTM_MIP_MAX_RX_LEN 17
-
-typedef struct {
-  tBTM_MIP_EVT event;
-  uint16_t mip_id;
-  uint8_t rx_len;
-  uint8_t rx_data[BTM_MIP_MAX_RX_LEN];
-} tBTM_MIP_RXDATA;
-
-typedef struct {
-  tBTM_MIP_EVT event;
-  RawAddress bd_addr;
-  uint8_t data[11]; /* data[0] shows Vender-specific device type */
-} tBTM_MIP_EIR_HANDSHAKE;
-
-typedef struct {
-  tBTM_MIP_EVT event;
-  uint16_t num_sent; /* Completed packet count at the controller */
-} tBTM_MIP_PKTS_COMPL;
-
-typedef union {
-  tBTM_MIP_EVT event;
-  tBTM_MIP_MODE_CHANGE mod_chg;
-  tBTM_MIP_CONN_TIMEOUT conn_tmo;
-  tBTM_MIP_EIR_HANDSHAKE eir;
-  tBTM_MIP_PKTS_COMPL completed;
-  tBTM_MIP_RXDATA rxdata;
-} tBTM_MIP_EVENT_DATA;
-
-/* MIP event callback function  */
-typedef void(tBTM_MIP_EVENTS_CB)(tBTM_MIP_EVT event, tBTM_MIP_EVENT_DATA data);
-
-/* MIP Device query callback function  */
-typedef bool(tBTM_MIP_QUERY_CB)(const RawAddress& dev_addr, uint8_t* p_mode,
-                                LINK_KEY link_key);
-
 /* ACL link on, SCO link ongoing, sniff mode */
 #define BTM_CONTRL_ACTIVE 1
 /* Scan state - paging/inquiry/trying to connect*/
diff --git a/stack/include/btm_ble_api.h b/stack/include/btm_ble_api.h
index f0fb9e6..bd991ef 100644
--- a/stack/include/btm_ble_api.h
+++ b/stack/include/btm_ble_api.h
@@ -196,43 +196,14 @@
                                   tBTM_INQ_RESULTS_CB* p_results_cb,
                                   tBTM_CMPL_CB* p_cmpl_cb);
 
-/*******************************************************************************
- *
- * Function         BTM_GetDeviceIDRoot
- *
- * Description      This function is called to read the local device identity
- *                  root.
- *
- * Returns          void
- *                  the local device ER is copied into er
- *
- ******************************************************************************/
-extern void BTM_GetDeviceIDRoot(BT_OCTET16 ir);
+/** Returns local device encryption root (ER) */
+const Octet16& BTM_GetDeviceEncRoot();
 
-/*******************************************************************************
- *
- * Function         BTM_GetDeviceEncRoot
- *
- * Description      This function is called to read the local device encryption
- *                  root.
- *
- * Returns          void
- *                  the local device ER is copied into er
- *
- ******************************************************************************/
-extern void BTM_GetDeviceEncRoot(BT_OCTET16 er);
+/** Returns local device identity root (IR) */
+extern const Octet16& BTM_GetDeviceIDRoot();
 
-/*******************************************************************************
- *
- * Function         BTM_GetDeviceDHK
- *
- * Description      This function is called to read the local device DHK.
- *
- * Returns          void
- *                  the local device DHK is copied into dhk
- *
- ******************************************************************************/
-extern void BTM_GetDeviceDHK(BT_OCTET16 dhk);
+/** Return local device DHK. */
+extern const Octet16& BTM_GetDeviceDHK();
 
 /*******************************************************************************
  *
diff --git a/stack/include/btm_ble_api_types.h b/stack/include/btm_ble_api_types.h
index 327a2e6..d7b223f 100644
--- a/stack/include/btm_ble_api_types.h
+++ b/stack/include/btm_ble_api_types.h
@@ -135,7 +135,7 @@
 #define BTM_BLE_CONN_SUP_TOUT_MAX 0x0C80
 /* use this value when a specific value not to be overwritten */
 #define BTM_BLE_CONN_PARAM_UNDEF 0xffff
-#define BTM_BLE_SCAN_PARAM_UNDEF 0xffffffff
+#define BTM_BLE_SCAN_PARAM_UNDEF 0xffff
 
 /* default connection parameters if not configured, use GAP recommended value
  * for auto/selective connection */
@@ -279,7 +279,7 @@
   uint8_t status;
   uint8_t param_len;
   uint16_t opcode;
-  uint8_t param_buf[BT_OCTET16_LEN];
+  uint8_t param_buf[OCTET16_LEN];
 } tBTM_RAND_ENC;
 
 /* General callback function for notifying an application that a synchronous
@@ -335,7 +335,8 @@
 /*  Preferred maximum number of microseconds that the local Controller
     should use to transmit a single Link Layer Data Channel PDU. */
 #define BTM_BLE_DATA_TX_TIME_MIN 0x0148
-#define BTM_BLE_DATA_TX_TIME_MAX 0x0848
+#define BTM_BLE_DATA_TX_TIME_MAX_LEGACY  0x0848
+#define BTM_BLE_DATA_TX_TIME_MAX         0x4290
 
 /* adv tx power in dBm */
 typedef struct {
diff --git a/stack/include/gatt_api.h b/stack/include/gatt_api.h
index f9c0642..74a9cf0 100644
--- a/stack/include/gatt_api.h
+++ b/stack/include/gatt_api.h
@@ -410,14 +410,6 @@
 };
 typedef uint8_t tGATT_DISC_TYPE;
 
-/* Discover parameters of different discovery types
-*/
-typedef struct {
-  bluetooth::Uuid service;
-  uint16_t s_handle;
-  uint16_t e_handle;
-} tGATT_DISC_PARAM;
-
 /* GATT read type enumeration
 */
 enum {
@@ -825,13 +817,19 @@
  *
  * Parameters       conn_id: connection identifier.
  *                  disc_type:discovery type.
- *                  p_param: parameters of discovery requirement.
+ *                  start_handle and end_handle: range of handles for discovery
+ *                  uuid: uuid to discovery. set to Uuid::kEmpty for requests
+ *                        that don't need it
  *
  * Returns          GATT_SUCCESS if command received/sent successfully.
  *
  ******************************************************************************/
 extern tGATT_STATUS GATTC_Discover(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
-                                   tGATT_DISC_PARAM* p_param);
+                                   uint16_t start_handle, uint16_t end_handle,
+                                   const bluetooth::Uuid& uuid);
+extern tGATT_STATUS GATTC_Discover(uint16_t conn_id, tGATT_DISC_TYPE disc_type,
+                                   uint16_t start_handle, uint16_t end_handle);
+
 /*******************************************************************************
  *
  * Function         GATTC_Read
diff --git a/stack/include/hcimsgs.h b/stack/include/hcimsgs.h
index a909751..1d89f31 100644
--- a/stack/include/hcimsgs.h
+++ b/stack/include/hcimsgs.h
@@ -128,7 +128,7 @@
 
 /* Link Key Request Reply */
 extern void btsnd_hcic_link_key_req_reply(const RawAddress& bd_addr,
-                                          LINK_KEY link_key);
+                                          const LinkKey& link_key);
 
 #define HCIC_PARAM_SIZE_LINK_KEY_REQ_REPLY 22
 
@@ -428,8 +428,8 @@
 #define HCI_USER_PASSKEY_NEG_BD_ADDR_OFF 0
 
 /* Remote OOB Data Request Reply */
-extern void btsnd_hcic_rem_oob_reply(const RawAddress& bd_addr, uint8_t* p_c,
-                                     uint8_t* p_r);
+extern void btsnd_hcic_rem_oob_reply(const RawAddress& bd_addr,
+                                     const Octet16& c, const Octet16& r);
 
 #define HCIC_PARAM_SIZE_REM_OOB_REPLY 38
 
@@ -667,7 +667,6 @@
  *             message size
  ******************************************************************************/
 #define HCIC_BLE_RAND_DI_SIZE 8
-#define HCIC_BLE_ENCRYT_KEY_SIZE 16
 #define HCIC_BLE_IRK_SIZE 16
 
 #define HCIC_PARAM_SIZE_SET_USED_FEAT_CMD 8
@@ -787,11 +786,9 @@
 
 extern void btsnd_hcic_ble_start_enc(uint16_t handle,
                                      uint8_t rand[HCIC_BLE_RAND_DI_SIZE],
-                                     uint16_t ediv,
-                                     uint8_t ltk[HCIC_BLE_ENCRYT_KEY_SIZE]);
+                                     uint16_t ediv, const Octet16& ltk);
 
-extern void btsnd_hcic_ble_ltk_req_reply(uint16_t handle,
-                                         uint8_t ltk[HCIC_BLE_ENCRYT_KEY_SIZE]);
+extern void btsnd_hcic_ble_ltk_req_reply(uint16_t handle, const Octet16& ltk);
 
 extern void btsnd_hcic_ble_ltk_req_neg_reply(uint16_t handle);
 
@@ -827,9 +824,10 @@
                                            uint16_t tx_octets,
                                            uint16_t tx_time);
 
-extern void btsnd_hcic_ble_add_device_resolving_list(
-    uint8_t addr_type_peer, const RawAddress& bda_peer,
-    uint8_t irk_peer[HCIC_BLE_IRK_SIZE], uint8_t irk_local[HCIC_BLE_IRK_SIZE]);
+extern void btsnd_hcic_ble_add_device_resolving_list(uint8_t addr_type_peer,
+                                                     const RawAddress& bda_peer,
+                                                     const Octet16& irk_peer,
+                                                     const Octet16& irk_local);
 
 struct scanning_phy_cfg {
   uint8_t scan_type;
@@ -864,10 +862,6 @@
                                            uint8_t initiating_phys,
                                            EXT_CONN_PHY_CFG* phy_cfg);
 
-extern void btsnd_hcic_ble_add_device_resolving_list(
-    uint8_t addr_type_peer, const RawAddress& bda_peer,
-    uint8_t irk_peer[HCIC_BLE_IRK_SIZE], uint8_t irk_local[HCIC_BLE_IRK_SIZE]);
-
 extern void btsnd_hcic_ble_rm_device_resolving_list(uint8_t addr_type_peer,
                                                     const RawAddress& bda_peer);
 
diff --git a/stack/include/smp_api.h b/stack/include/smp_api.h
index 816407d..8a66aae 100644
--- a/stack/include/smp_api.h
+++ b/stack/include/smp_api.h
@@ -176,24 +176,6 @@
 
 /*******************************************************************************
  *
- * Function         SMP_Encrypt
- *
- * Description      Encrypt the data with the specified key.
- *
- * Parameters:      key                 - Pointer to key key[0] conatins the MSB
- *                  key_len             - key length
- *                  plain_text          - Pointer to data to be encrypted
- *                                        plain_text[0] conatins the MSB
- *                  pt_len              - plain text length
- *                  p_out               - pointer to the encrypted outputs
- *
- *  Returns         Boolean - true: encryption is successful
- ******************************************************************************/
-extern bool SMP_Encrypt(uint8_t* key, uint8_t key_len, uint8_t* plain_text,
-                        uint8_t pt_len, tSMP_ENC* p_out);
-
-/*******************************************************************************
- *
  * Function         SMP_KeypressNotification
  *
  * Description      Notify SM about Keypress Notification.
@@ -227,16 +209,4 @@
 // Proceed to send LTK, DIV and ER to master if bonding the devices.
 extern void smp_link_encrypted(const RawAddress& bda, uint8_t encr_enable);
 
-//
-// The AES-CMAC Generation Function with tlen implemented.
-// |key| - CMAC key in little endian order, expect SRK when used by SMP.
-// |input| - text to be signed in little endian byte order.
-// |length| - length of the input in byte.
-// |tlen| - lenth of mac desired
-// |p_signature| - data pointer to where signed data to be stored, tlen long.
-// Returns false if out of resources, true in other cases.
-//
-bool aes_cipher_msg_auth_code(BT_OCTET16 key, uint8_t* input, uint16_t length,
-                              uint16_t tlen, uint8_t* p_signature);
-
 #endif /* SMP_API_H */
diff --git a/stack/include/smp_api_types.h b/stack/include/smp_api_types.h
index f317993..cee1204 100644
--- a/stack/include/smp_api_types.h
+++ b/stack/include/smp_api_types.h
@@ -220,8 +220,8 @@
 /* the data associated with the info sent to the peer via OOB interface */
 typedef struct {
   bool present;
-  BT_OCTET16 randomizer;
-  BT_OCTET16 commitment;
+  Octet16 randomizer;
+  Octet16 commitment;
 
   tBLE_BD_ADDR addr_sent_to;
   BT_OCTET32 private_key_used; /* is used to calculate: */
@@ -234,8 +234,8 @@
 /* the data associated with the info received from the peer via OOB interface */
 typedef struct {
   bool present;
-  BT_OCTET16 randomizer;
-  BT_OCTET16 commitment;
+  Octet16 randomizer;
+  Octet16 commitment;
   tBLE_BD_ADDR addr_rcvd_from;
 } tSMP_PEER_OOB_DATA;
 
@@ -257,7 +257,7 @@
   uint8_t status;
   uint8_t param_len;
   uint16_t opcode;
-  uint8_t param_buf[BT_OCTET16_LEN];
+  uint8_t param_buf[OCTET16_LEN];
 } tSMP_ENC;
 
 /* Security Manager events - Called by the stack when Security Manager related
diff --git a/stack/l2cap/l2c_ble.cc b/stack/l2cap/l2c_ble.cc
index c82d3cf..528bdee 100644
--- a/stack/l2cap/l2c_ble.cc
+++ b/stack/l2cap/l2c_ble.cc
@@ -853,8 +853,12 @@
 bool l2cble_init_direct_conn(tL2C_LCB* p_lcb) {
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(p_lcb->remote_bd_addr);
   tBTM_BLE_CB* p_cb = &btm_cb.ble_ctr_cb;
-  uint16_t scan_int;
-  uint16_t scan_win;
+  uint16_t scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF)
+                          ? BTM_BLE_SCAN_FAST_INT
+                          : p_cb->scan_int;
+  uint16_t scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF)
+                          ? BTM_BLE_SCAN_FAST_WIN
+                          : p_cb->scan_win;
   RawAddress peer_addr;
   uint8_t peer_addr_type = BLE_ADDR_PUBLIC;
   uint8_t own_addr_type = BLE_ADDR_PUBLIC;
@@ -865,13 +869,6 @@
     return (false);
   }
 
-  scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF)
-                 ? BTM_BLE_SCAN_FAST_INT
-                 : p_cb->scan_int;
-  scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF)
-                 ? BTM_BLE_SCAN_FAST_WIN
-                 : p_cb->scan_win;
-
   peer_addr_type = p_lcb->ble_addr_type;
   peer_addr = p_lcb->remote_bd_addr;
 
diff --git a/stack/l2cap/l2c_utils.cc b/stack/l2cap/l2c_utils.cc
index cc16090..f43f4b4 100644
--- a/stack/l2cap/l2c_utils.cc
+++ b/stack/l2cap/l2c_utils.cc
@@ -2681,28 +2681,34 @@
 
   /* Tell all registered fixed channels about the connection */
   for (int xx = 0; xx < L2CAP_NUM_FIXED_CHNLS; xx++) {
+    uint16_t channel_id = xx + L2CAP_FIRST_FIXED_CHNL;
+
+    /* See BT Spec Ver 5.0 | Vol 3, Part A 2.1 table 2.1 and 2.2 */
+
     /* skip sending LE fix channel callbacks on BR/EDR links */
     if (p_lcb->transport == BT_TRANSPORT_BR_EDR &&
-        xx + L2CAP_FIRST_FIXED_CHNL >= L2CAP_ATT_CID &&
-        xx + L2CAP_FIRST_FIXED_CHNL <= L2CAP_SMP_CID)
+        channel_id >= L2CAP_ATT_CID && channel_id <= L2CAP_SMP_CID)
       continue;
-    if (l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb != NULL) {
-      if (p_lcb->peer_chnl_mask[(xx + L2CAP_FIRST_FIXED_CHNL) / 8] &
-          (1 << ((xx + L2CAP_FIRST_FIXED_CHNL) % 8))) {
-        if (p_lcb->p_fixed_ccbs[xx])
-          p_lcb->p_fixed_ccbs[xx]->chnl_state = CST_OPEN;
-        (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(xx + L2CAP_FIRST_FIXED_CHNL,
-                                                 p_lcb->remote_bd_addr, true, 0,
-                                                 p_lcb->transport);
-      } else {
-        (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(
-            xx + L2CAP_FIRST_FIXED_CHNL, p_lcb->remote_bd_addr, false,
-            p_lcb->disc_reason, p_lcb->transport);
 
-        if (p_lcb->p_fixed_ccbs[xx]) {
-          l2cu_release_ccb(p_lcb->p_fixed_ccbs[xx]);
-          p_lcb->p_fixed_ccbs[xx] = NULL;
-        }
+    /* skip sending BR fix channel callbacks on LE links */
+    if (p_lcb->transport == BT_TRANSPORT_LE && channel_id == L2CAP_SMP_BR_CID)
+      continue;
+
+    if (!l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb) continue;
+
+    if (p_lcb->peer_chnl_mask[(channel_id) / 8] & (1 << ((channel_id) % 8))) {
+      if (p_lcb->p_fixed_ccbs[xx])
+        p_lcb->p_fixed_ccbs[xx]->chnl_state = CST_OPEN;
+      (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(
+          channel_id, p_lcb->remote_bd_addr, true, 0, p_lcb->transport);
+    } else {
+      (*l2cb.fixed_reg[xx].pL2CA_FixedConn_Cb)(
+          channel_id, p_lcb->remote_bd_addr, false, p_lcb->disc_reason,
+          p_lcb->transport);
+
+      if (p_lcb->p_fixed_ccbs[xx]) {
+        l2cu_release_ccb(p_lcb->p_fixed_ccbs[xx]);
+        p_lcb->p_fixed_ccbs[xx] = NULL;
       }
     }
   }
diff --git a/stack/smp/crypto_toolbox.h b/stack/smp/crypto_toolbox.h
new file mode 100644
index 0000000..d3fceff
--- /dev/null
+++ b/stack/smp/crypto_toolbox.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "stack/include/bt_types.h"
+
+/* Functions below implement cryptographic toolbox, as described in BT Spec
+ * Ver 5.0 | Vol 3, Part H CRYPTOGRAPHIC TOOLBOX. Please see the spec for
+ * description.
+ *
+ * Example of usage is avaliable in cryptographic_toolbox_test.cc */
+
+extern Octet16 smp_calculate_f4(uint8_t* u, uint8_t* v, const Octet16& x,
+                                uint8_t z);
+extern uint32_t smp_calculate_g2(uint8_t* u, uint8_t* v, const Octet16& x,
+                                 const Octet16& y);
+extern void smp_calculate_f5(uint8_t* w, const Octet16& n1, const Octet16& n2,
+                             uint8_t* a1, uint8_t* a2, Octet16* mac_key,
+                             Octet16* ltk);
+extern Octet16 smp_calculate_f6(const Octet16& w, const Octet16& n1,
+                                const Octet16& n2, const Octet16& r,
+                                uint8_t* iocap, uint8_t* a1, uint8_t* a2);
+extern Octet16 smp_calculate_h6(const Octet16& w, std::array<uint8_t, 4> keyid);
+extern Octet16 smp_calculate_h7(const Octet16& salt, const Octet16& w);
+extern Octet16 smp_calculate_ltk_to_link_key(const Octet16& ltk, bool use_h7);
+extern Octet16 smp_calculate_link_key_to_ltk(const Octet16& link_key,
+                                             bool use_h7);
diff --git a/stack/smp/smp_act.cc b/stack/smp/smp_act.cc
index 2c657a5..f7bb30d 100644
--- a/stack/smp/smp_act.cc
+++ b/stack/smp/smp_act.cc
@@ -350,7 +350,7 @@
   smp_send_cmd(SMP_OPCODE_MASTER_ID, p_cb);
 
   /* save the DIV and key size information when acting as slave device */
-  memcpy(le_key.ltk, p_cb->ltk, BT_OCTET16_LEN);
+  le_key.ltk = p_cb->ltk;
   le_key.div = p_cb->div;
   le_key.key_size = p_cb->loc_enc_size;
   le_key.sec_level = p_cb->sec_level;
@@ -385,10 +385,7 @@
   smp_key_distribution_by_transport(p_cb, NULL);
 }
 
-/*******************************************************************************
- * Function     smp_send_csrk_info
- * Description  send CSRK command.
- ******************************************************************************/
+/**  send CSRK command. */
 void smp_send_csrk_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   tBTM_LE_LCSRK_KEYS key;
   SMP_TRACE_DEBUG("%s", __func__);
@@ -398,7 +395,7 @@
     key.div = p_cb->div;
     key.sec_level = p_cb->sec_level;
     key.counter = 0; /* initialize the local counter */
-    memcpy(key.csrk, p_cb->csrk, BT_OCTET16_LEN);
+    key.csrk = p_cb->csrk;
     btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_LCSRK,
                         (tBTM_LE_KEY_VALUE*)&key, true);
   }
@@ -412,8 +409,11 @@
  ******************************************************************************/
 void smp_send_ltk_reply(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   SMP_TRACE_DEBUG("%s", __func__);
+
+  Octet16 stk;
+  memcpy(stk.data(), p_data->key.p_data, stk.size());
   /* send stk as LTK response */
-  btm_ble_ltk_request_reply(p_cb->pairing_bda, true, p_data->key.p_data);
+  btm_ble_ltk_request_reply(p_cb->pairing_bda, true, stk);
 }
 
 /*******************************************************************************
@@ -585,10 +585,7 @@
   }
 }
 
-/*******************************************************************************
- * Function     smp_proc_confirm
- * Description  process pairing confirm from peer device
- ******************************************************************************/
+/** process pairing confirm from peer device */
 void smp_proc_confirm(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   uint8_t* p = p_data->p_data;
 
@@ -603,16 +600,13 @@
 
   if (p != NULL) {
     /* save the SConfirm for comparison later */
-    STREAM_TO_ARRAY(p_cb->rconfirm, p, BT_OCTET16_LEN);
+    STREAM_TO_ARRAY(p_cb->rconfirm.data(), p, OCTET16_LEN);
   }
 
   p_cb->flags |= SMP_PAIR_FLAGS_CMD_CONFIRM;
 }
 
-/*******************************************************************************
- * Function     smp_proc_init
- * Description  process pairing initializer from peer device
- ******************************************************************************/
+/** process pairing initializer from peer device */
 void smp_proc_init(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   uint8_t* p = p_data->p_data;
 
@@ -626,7 +620,7 @@
   }
 
   /* save the SRand for comparison */
-  STREAM_TO_ARRAY(p_cb->rrand, p, BT_OCTET16_LEN);
+  STREAM_TO_ARRAY(p_cb->rrand.data(), p, OCTET16_LEN);
 }
 
 /*******************************************************************************
@@ -646,7 +640,7 @@
   }
 
   /* save the SRand for comparison */
-  STREAM_TO_ARRAY(p_cb->rrand, p, BT_OCTET16_LEN);
+  STREAM_TO_ARRAY(p_cb->rrand.data(), p, OCTET16_LEN);
 }
 
 /*******************************************************************************
@@ -695,7 +689,7 @@
   p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_COMM;
 
   if (p != NULL) {
-    STREAM_TO_ARRAY(p_cb->remote_commitment, p, BT_OCTET16_LEN);
+    STREAM_TO_ARRAY(p_cb->remote_commitment.data(), p, OCTET16_LEN);
   }
 }
 
@@ -716,7 +710,7 @@
   }
 
   if (p != NULL) {
-    STREAM_TO_ARRAY(p_cb->remote_dhkey_check, p, BT_OCTET16_LEN);
+    STREAM_TO_ARRAY(p_cb->remote_dhkey_check.data(), p, OCTET16_LEN);
   }
 
   p_cb->flags |= SMP_PAIR_FLAG_HAVE_PEER_DHK_CHK;
@@ -895,22 +889,17 @@
   }
 }
 
-/*******************************************************************************
- * Function     smp_proc_enc_info
- * Description  process encryption information from peer device
- ******************************************************************************/
+/** process encryption information from peer device */
 void smp_proc_enc_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   uint8_t* p = p_data->p_data;
 
   SMP_TRACE_DEBUG("%s", __func__);
-  STREAM_TO_ARRAY(p_cb->ltk, p, BT_OCTET16_LEN);
+  STREAM_TO_ARRAY(p_cb->ltk.data(), p, OCTET16_LEN);
 
   smp_key_distribution(p_cb, NULL);
 }
-/*******************************************************************************
- * Function     smp_proc_master_id
- * Description  process master ID from slave device
- ******************************************************************************/
+
+/** process master ID from slave device */
 void smp_proc_master_id(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   uint8_t* p = p_data->p_data;
   tBTM_LE_PENC_KEYS le_key;
@@ -922,7 +911,7 @@
   STREAM_TO_ARRAY(le_key.rand, p, BT_OCTET8_LEN);
 
   /* store the encryption keys from peer device */
-  memcpy(le_key.ltk, p_cb->ltk, BT_OCTET16_LEN);
+  le_key.ltk = p_cb->ltk;
   le_key.sec_level = p_cb->sec_level;
   le_key.key_size = p_cb->loc_enc_size;
 
@@ -934,22 +923,16 @@
   smp_key_distribution(p_cb, NULL);
 }
 
-/*******************************************************************************
- * Function     smp_proc_enc_info
- * Description  process identity information from peer device
- ******************************************************************************/
+/** process identity information from peer device */
 void smp_proc_id_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   uint8_t* p = p_data->p_data;
 
   SMP_TRACE_DEBUG("%s", __func__);
-  STREAM_TO_ARRAY(p_cb->tk, p, BT_OCTET16_LEN); /* reuse TK for IRK */
+  STREAM_TO_ARRAY(p_cb->tk.data(), p, OCTET16_LEN); /* reuse TK for IRK */
   smp_key_distribution_by_transport(p_cb, NULL);
 }
 
-/*******************************************************************************
- * Function     smp_proc_id_addr
- * Description  process identity address from peer device
- ******************************************************************************/
+/** process identity address from peer device */
 void smp_proc_id_addr(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   uint8_t* p = p_data->p_data;
   tBTM_LE_PID_KEYS pid_key;
@@ -959,7 +942,7 @@
 
   STREAM_TO_UINT8(pid_key.addr_type, p);
   STREAM_TO_BDADDR(pid_key.static_addr, p);
-  memcpy(pid_key.irk, p_cb->tk, BT_OCTET16_LEN);
+  pid_key.irk = p_cb->tk;
 
   /* to use as BD_ADDR for lk derived from ltk */
   p_cb->id_addr_rcvd = true;
@@ -974,10 +957,7 @@
   smp_key_distribution_by_transport(p_cb, NULL);
 }
 
-/*******************************************************************************
- * Function     smp_proc_srk_info
- * Description  process security information from peer device
- ******************************************************************************/
+/* process security information from peer device */
 void smp_proc_srk_info(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   tBTM_LE_PCSRK_KEYS le_key;
 
@@ -988,7 +968,7 @@
   le_key.sec_level = p_cb->sec_level;
 
   /* get peer CSRK */
-  maybe_non_aligned_memcpy(le_key.csrk, p_data->p_data, BT_OCTET16_LEN);
+  maybe_non_aligned_memcpy(le_key.csrk.data(), p_data->p_data, OCTET16_LEN);
 
   /* initialize the peer counter */
   le_key.counter = 0;
@@ -1006,7 +986,7 @@
  ******************************************************************************/
 void smp_proc_compare(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   SMP_TRACE_DEBUG("%s", __func__);
-  if (!memcmp(p_cb->rconfirm, p_data->key.p_data, BT_OCTET16_LEN)) {
+  if (!memcmp(p_cb->rconfirm.data(), p_data->key.p_data, OCTET16_LEN)) {
     /* compare the max encryption key size, and save the smaller one for the
      * link */
     if (p_cb->peer_enc_size < p_cb->loc_enc_size)
@@ -1056,10 +1036,12 @@
   tBTM_STATUS cmd;
 
   SMP_TRACE_DEBUG("%s", __func__);
-  if (p_data != NULL)
-    cmd = btm_ble_start_encrypt(p_cb->pairing_bda, true, p_data->key.p_data);
-  else
+  if (p_data != NULL) {
+    cmd = btm_ble_start_encrypt(p_cb->pairing_bda, true,
+                                (Octet16*)p_data->key.p_data);
+  } else {
     cmd = btm_ble_start_encrypt(p_cb->pairing_bda, false, NULL);
+  }
 
   if (cmd != BTM_CMD_STARTED && cmd != BTM_BUSY) {
     tSMP_INT_DATA smp_int_data;
@@ -1244,10 +1226,10 @@
 
         tSMP_KEY key;
         key.key_type = SMP_KEY_TYPE_TK;
-        key.p_data = p_cb->tk;
+        key.p_data = p_cb->tk.data();
         smp_int_data.key = key;
 
-        memset(p_cb->tk, 0, BT_OCTET16_LEN);
+        p_cb->tk = {0};
         /* TK, ready  */
         int_evt = SMP_KEY_READY_EVT;
       }
@@ -1448,7 +1430,7 @@
   switch (p_cb->selected_association_model) {
     case SMP_MODEL_SEC_CONN_JUSTWORKS:
     case SMP_MODEL_SEC_CONN_NUM_COMP:
-      memset(p_cb->local_random, 0, BT_OCTET16_LEN);
+      p_cb->local_random = {0};
       smp_start_nonce_generation(p_cb);
       break;
     case SMP_MODEL_SEC_CONN_PASSKEY_ENT:
@@ -1638,10 +1620,10 @@
  *              received from the peer DHKey check value.
  ******************************************************************************/
 void smp_match_dhkey_checks(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
-
   SMP_TRACE_DEBUG("%s", __func__);
 
-  if (memcmp(p_data->key.p_data, p_cb->remote_dhkey_check, BT_OCTET16_LEN)) {
+  if (memcmp(p_data->key.p_data, p_cb->remote_dhkey_check.data(),
+             OCTET16_LEN)) {
     SMP_TRACE_WARNING("dhkey chcks do no match");
     tSMP_INT_DATA smp_int_data;
     smp_int_data.status = SMP_DHKEY_CHK_FAIL;
@@ -1727,10 +1709,10 @@
   uint8_t* p = NULL;
 
   SMP_TRACE_DEBUG("%s", __func__);
-  p = p_cb->local_random;
+  p = p_cb->local_random.data();
   UINT32_TO_STREAM(p, p_data->passkey);
 
-  p = p_cb->peer_random;
+  p = p_cb->peer_random.data();
   UINT32_TO_STREAM(p, p_data->passkey);
 
   p_cb->round = 0;
@@ -1747,21 +1729,18 @@
 
   tSMP_SC_OOB_DATA* p_sc_oob_data = &p_cb->sc_oob_data;
   if (p_sc_oob_data->loc_oob_data.present) {
-    memcpy(p_cb->local_random, p_sc_oob_data->loc_oob_data.randomizer,
-           sizeof(p_cb->local_random));
+    p_cb->local_random = p_sc_oob_data->loc_oob_data.randomizer;
   } else {
     SMP_TRACE_EVENT("%s: local OOB randomizer is absent", __func__);
-    memset(p_cb->local_random, 0, sizeof(p_cb->local_random));
+    p_cb->local_random = {0};
   }
 
   if (!p_sc_oob_data->peer_oob_data.present) {
     SMP_TRACE_EVENT("%s: peer OOB data is absent", __func__);
-    memset(p_cb->peer_random, 0, sizeof(p_cb->peer_random));
+    p_cb->peer_random = {0};
   } else {
-    memcpy(p_cb->peer_random, p_sc_oob_data->peer_oob_data.randomizer,
-           sizeof(p_cb->peer_random));
-    memcpy(p_cb->remote_commitment, p_sc_oob_data->peer_oob_data.commitment,
-           sizeof(p_cb->remote_commitment));
+    p_cb->peer_random = p_sc_oob_data->peer_oob_data.randomizer;
+    p_cb->remote_commitment = p_sc_oob_data->peer_oob_data.commitment;
 
     /* check commitment */
     if (!smp_check_commitment(p_cb)) {
@@ -1777,7 +1756,7 @@
       SMP_TRACE_EVENT(
           "%s: peer didn't receive local OOB data, set local randomizer to 0",
           __func__);
-      memset(p_cb->local_random, 0, sizeof(p_cb->local_random));
+      p_cb->local_random = {0};
     }
   }
 
@@ -1809,12 +1788,12 @@
  ******************************************************************************/
 void smp_set_local_oob_random_commitment(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   SMP_TRACE_DEBUG("%s", __func__);
-  memcpy(p_cb->sc_oob_data.loc_oob_data.randomizer, p_cb->rand, BT_OCTET16_LEN);
+  p_cb->sc_oob_data.loc_oob_data.randomizer = p_cb->rand;
 
-  smp_calculate_f4(p_cb->sc_oob_data.loc_oob_data.publ_key_used.x,
-                   p_cb->sc_oob_data.loc_oob_data.publ_key_used.x,
-                   p_cb->sc_oob_data.loc_oob_data.randomizer, 0,
-                   p_cb->sc_oob_data.loc_oob_data.commitment);
+  p_cb->sc_oob_data.loc_oob_data.commitment =
+      crypto_toolbox::f4(p_cb->sc_oob_data.loc_oob_data.publ_key_used.x,
+                         p_cb->sc_oob_data.loc_oob_data.publ_key_used.x,
+                         p_cb->sc_oob_data.loc_oob_data.randomizer, 0);
 
 #if (SMP_DEBUG == TRUE)
   uint8_t* p_print = NULL;
@@ -1832,9 +1811,9 @@
   smp_debug_print_nbyte_little_endian(p_print, "publ_key_used.y",
                                       BT_OCTET32_LEN);
   p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.randomizer;
-  smp_debug_print_nbyte_little_endian(p_print, "randomizer", BT_OCTET16_LEN);
+  smp_debug_print_nbyte_little_endian(p_print, "randomizer", OCTET16_LEN);
   p_print = (uint8_t*)&p_cb->sc_oob_data.loc_oob_data.commitment;
-  smp_debug_print_nbyte_little_endian(p_print, "commitment", BT_OCTET16_LEN);
+  smp_debug_print_nbyte_little_endian(p_print, "commitment", OCTET16_LEN);
   SMP_TRACE_DEBUG("");
 #endif
 
diff --git a/stack/smp/smp_api.cc b/stack/smp/smp_api.cc
index d096da5..baa41cf 100644
--- a/stack/smp/smp_api.cc
+++ b/stack/smp/smp_api.cc
@@ -328,7 +328,7 @@
     smp_int_data.passkey = passkey;
     smp_sm_event(&smp_cb, SMP_SC_KEY_READY_EVT, &smp_int_data);
   } else {
-    smp_convert_string_to_tk(p_cb->tk, passkey);
+    smp_convert_string_to_tk(&p_cb->tk, passkey);
   }
 
   return;
@@ -406,12 +406,12 @@
     smp_int_data.status = SMP_OOB_FAIL;
     smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
   } else {
-    if (len > BT_OCTET16_LEN) len = BT_OCTET16_LEN;
+    if (len > OCTET16_LEN) len = OCTET16_LEN;
 
-    memcpy(p_cb->tk, p_data, len);
+    memcpy(p_cb->tk.data(), p_data, len);
 
     key.key_type = SMP_KEY_TYPE_TK;
-    key.p_data = p_cb->tk;
+    key.p_data = p_cb->tk.data();
 
     tSMP_INT_DATA smp_int_data;
     smp_int_data.key = key;
@@ -484,31 +484,6 @@
 
 /*******************************************************************************
  *
- * Function         SMP_Encrypt
- *
- * Description      This function is called to encrypt the data with the
- *                  specified key
- *
- * Parameters:      key                 - Pointer to key key[0] conatins the MSB
- *                  key_len             - key length
- *                  plain_text          - Pointer to data to be encrypted
- *                                        plain_text[0] conatins the MSB
- *                  pt_len              - plain text length
- *                  p_out                - output of the encrypted texts
- *
- *  Returns         Boolean - request is successful
- ******************************************************************************/
-bool SMP_Encrypt(uint8_t* key, uint8_t key_len, uint8_t* plain_text,
-                 uint8_t pt_len, tSMP_ENC* p_out)
-
-{
-  bool status = false;
-  status = smp_encrypt_data(key, key_len, plain_text, pt_len, p_out);
-  return status;
-}
-
-/*******************************************************************************
- *
  * Function         SMP_KeypressNotification
  *
  * Description      This function is called to notify Security Manager about
diff --git a/stack/smp/smp_cmac.cc b/stack/smp/smp_cmac.cc
deleted file mode 100644
index 42f91a0..0000000
--- a/stack/smp/smp_cmac.cc
+++ /dev/null
@@ -1,313 +0,0 @@
-/******************************************************************************
- *
- *  Copyright 2008-2012 Broadcom Corporation
- *
- *  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.
- *
- ******************************************************************************/
-
-/******************************************************************************
- *
- *  This file contains the implementation of the AES128 CMAC algorithm.
- *
- ******************************************************************************/
-
-#include "bt_target.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include "btm_ble_api.h"
-#include "hcimsgs.h"
-#include "smp_int.h"
-
-typedef struct {
-  uint8_t* text;
-  uint16_t len;
-  uint16_t round;
-} tCMAC_CB;
-
-tCMAC_CB cmac_cb;
-
-/* Rb for AES-128 as block cipher, LSB as [0] */
-BT_OCTET16 const_Rb = {0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-
-void print128(BT_OCTET16 x, const uint8_t* key_name) {
-#if (SMP_DEBUG == TRUE && SMP_DEBUG_VERBOSE == TRUE)
-  uint8_t* p = (uint8_t*)x;
-  uint8_t i;
-
-  SMP_TRACE_WARNING("%s(MSB ~ LSB) = ", key_name);
-
-  for (i = 0; i < 4; i++) {
-    SMP_TRACE_WARNING("%02x %02x %02x %02x", p[BT_OCTET16_LEN - i * 4 - 1],
-                      p[BT_OCTET16_LEN - i * 4 - 2],
-                      p[BT_OCTET16_LEN - i * 4 - 3],
-                      p[BT_OCTET16_LEN - i * 4 - 4]);
-  }
-#endif
-}
-
-/*******************************************************************************
- *
- * Function         padding
- *
- * Description      utility function to padding the given text to be a 128 bits
- *                  data. The parameter dest is input and output parameter, it
- *                  must point to a BT_OCTET16_LEN memory space; where include
- *                  length bytes valid data.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void padding(BT_OCTET16 dest, uint8_t length) {
-  uint8_t i, *p = dest;
-  /* original last block */
-  for (i = length; i < BT_OCTET16_LEN; i++)
-    p[BT_OCTET16_LEN - i - 1] = (i == length) ? 0x80 : 0;
-}
-/*******************************************************************************
- *
- * Function         leftshift_onebit
- *
- * Description      utility function to left shift one bit for a 128 bits value.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void leftshift_onebit(uint8_t* input, uint8_t* output) {
-  uint8_t i, overflow = 0, next_overflow = 0;
-  SMP_TRACE_EVENT("leftshift_onebit ");
-  /* input[0] is LSB */
-  for (i = 0; i < BT_OCTET16_LEN; i++) {
-    next_overflow = (input[i] & 0x80) ? 1 : 0;
-    output[i] = (input[i] << 1) | overflow;
-    overflow = next_overflow;
-  }
-  return;
-}
-/*******************************************************************************
- *
- * Function         cmac_aes_cleanup
- *
- * Description      clean up function for AES_CMAC algorithm.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void cmac_aes_cleanup(void) {
-  osi_free(cmac_cb.text);
-  memset(&cmac_cb, 0, sizeof(tCMAC_CB));
-}
-
-/*******************************************************************************
- *
- * Function         cmac_aes_k_calculate
- *
- * Description      This function is the calculation of block cipher using
- *                  AES-128.
- *
- * Returns          void
- *
- ******************************************************************************/
-static bool cmac_aes_k_calculate(BT_OCTET16 key, uint8_t* p_signature,
-                                 uint16_t tlen) {
-  tSMP_ENC output;
-  uint8_t i = 1, err = 0;
-  uint8_t x[16] = {0};
-  uint8_t* p_mac;
-
-  SMP_TRACE_EVENT("cmac_aes_k_calculate ");
-
-  while (i <= cmac_cb.round) {
-    smp_xor_128(&cmac_cb.text[(cmac_cb.round - i) * BT_OCTET16_LEN],
-                x); /* Mi' := Mi (+) X  */
-
-    if (!SMP_Encrypt(key, BT_OCTET16_LEN,
-                     &cmac_cb.text[(cmac_cb.round - i) * BT_OCTET16_LEN],
-                     BT_OCTET16_LEN, &output)) {
-      err = 1;
-      break;
-    }
-
-    memcpy(x, output.param_buf, BT_OCTET16_LEN);
-    i++;
-  }
-
-  if (!err) {
-    p_mac = output.param_buf + (BT_OCTET16_LEN - tlen);
-    memcpy(p_signature, p_mac, tlen);
-
-    SMP_TRACE_DEBUG("tlen = %d p_mac = %d", tlen, p_mac);
-    SMP_TRACE_DEBUG(
-        "p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = "
-        "0x%02x",
-        *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3));
-    SMP_TRACE_DEBUG(
-        "p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = "
-        "0x%02x",
-        *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7));
-
-    return true;
-
-  } else
-    return false;
-}
-/*******************************************************************************
- *
- * Function         cmac_prepare_last_block
- *
- * Description      This function proceeed to prepare the last block of message
- *                  Mn depending on the size of the message.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void cmac_prepare_last_block(BT_OCTET16 k1, BT_OCTET16 k2) {
-  //    uint8_t     x[16] = {0};
-  bool flag;
-
-  SMP_TRACE_EVENT("cmac_prepare_last_block ");
-  /* last block is a complete block set flag to 1 */
-  flag =
-      ((cmac_cb.len % BT_OCTET16_LEN) == 0 && cmac_cb.len != 0) ? true : false;
-
-  SMP_TRACE_WARNING("flag = %d round = %d", flag, cmac_cb.round);
-
-  if (flag) { /* last block is complete block */
-    smp_xor_128(&cmac_cb.text[0], k1);
-  } else /* padding then xor with k2 */
-  {
-    padding(&cmac_cb.text[0], (uint8_t)(cmac_cb.len % 16));
-
-    smp_xor_128(&cmac_cb.text[0], k2);
-  }
-}
-/*******************************************************************************
- *
- * Function         cmac_subkey_cont
- *
- * Description      This is the callback function when CIPHk(0[128]) is
- *                  completed.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void cmac_subkey_cont(tSMP_ENC* p) {
-  uint8_t k1[BT_OCTET16_LEN], k2[BT_OCTET16_LEN];
-  uint8_t* pp = p->param_buf;
-  SMP_TRACE_EVENT("cmac_subkey_cont ");
-  print128(pp, (const uint8_t*)"K1 before shift");
-
-  /* If MSB(L) = 0, then K1 = L << 1 */
-  if ((pp[BT_OCTET16_LEN - 1] & 0x80) != 0) {
-    /* Else K1 = ( L << 1 ) (+) Rb */
-    leftshift_onebit(pp, k1);
-    smp_xor_128(k1, const_Rb);
-  } else {
-    leftshift_onebit(pp, k1);
-  }
-
-  if ((k1[BT_OCTET16_LEN - 1] & 0x80) != 0) {
-    /* K2 =  (K1 << 1) (+) Rb */
-    leftshift_onebit(k1, k2);
-    smp_xor_128(k2, const_Rb);
-  } else {
-    /* If MSB(K1) = 0, then K2 = K1 << 1 */
-    leftshift_onebit(k1, k2);
-  }
-
-  print128(k1, (const uint8_t*)"K1");
-  print128(k2, (const uint8_t*)"K2");
-
-  cmac_prepare_last_block(k1, k2);
-}
-/*******************************************************************************
- *
- * Function         cmac_generate_subkey
- *
- * Description      This is the function to generate the two subkeys.
- *
- * Parameters       key - CMAC key, expect SRK when used by SMP.
- *
- * Returns          void
- *
- ******************************************************************************/
-static bool cmac_generate_subkey(BT_OCTET16 key) {
-  BT_OCTET16 z = {0};
-  bool ret = true;
-  tSMP_ENC output;
-  SMP_TRACE_EVENT(" cmac_generate_subkey");
-
-  if (SMP_Encrypt(key, BT_OCTET16_LEN, z, BT_OCTET16_LEN, &output)) {
-    cmac_subkey_cont(&output);
-    ;
-  } else
-    ret = false;
-
-  return ret;
-}
-/*******************************************************************************
- *
- * Function         aes_cipher_msg_auth_code
- *
- * Description      This is the AES-CMAC Generation Function with tlen
- *                  implemented.
- *
- * Parameters       key - CMAC key in little endian order, expect SRK when used
- *                        by SMP.
- *                  input - text to be signed in little endian byte order.
- *                  length - length of the input in byte.
- *                  tlen - lenth of mac desired
- *                  p_signature - data pointer to where signed data to be
- *                                stored, tlen long.
- *
- * Returns          false if out of resources, true in other cases.
- *
- ******************************************************************************/
-bool aes_cipher_msg_auth_code(BT_OCTET16 key, uint8_t* input, uint16_t length,
-                              uint16_t tlen, uint8_t* p_signature) {
-  uint16_t len, diff;
-  uint16_t n = (length + BT_OCTET16_LEN - 1) /
-               BT_OCTET16_LEN; /* n is number of rounds */
-  bool ret = false;
-
-  SMP_TRACE_EVENT("%s", __func__);
-
-  if (n == 0) n = 1;
-  len = n * BT_OCTET16_LEN;
-
-  SMP_TRACE_WARNING("AES128_CMAC started, allocate buffer size = %d", len);
-  /* allocate a memory space of multiple of 16 bytes to hold text  */
-  cmac_cb.text = (uint8_t*)osi_calloc(len);
-  cmac_cb.round = n;
-  diff = len - length;
-
-  if (input != NULL && length > 0) {
-    memcpy(&cmac_cb.text[diff], input, (int)length);
-    cmac_cb.len = length;
-  } else {
-    cmac_cb.len = 0;
-  }
-
-  /* prepare calculation for subkey s and last block of data */
-  if (cmac_generate_subkey(key)) {
-    /* start calculation */
-    ret = cmac_aes_k_calculate(key, p_signature, tlen);
-  }
-  /* clean up */
-  cmac_aes_cleanup();
-
-  return ret;
-}
diff --git a/stack/smp/smp_int.h b/stack/smp/smp_int.h
index c3ad35d..c6fdf71 100644
--- a/stack/smp/smp_int.h
+++ b/stack/smp/smp_int.h
@@ -28,6 +28,7 @@
 #include "btm_ble_api.h"
 #include "btu.h"
 #include "smp_api.h"
+#include "stack/crypto_toolbox/crypto_toolbox.h"
 
 /* Legacy mode */
 #define SMP_MODEL_ENCRYPTION_ONLY 0 /* Just Works model */
@@ -244,10 +245,6 @@
 /* check if authentication requirement need MITM protection */
 #define SMP_NO_MITM_REQUIRED(x) (((x)&SMP_AUTH_YN_BIT) == 0)
 
-#define SMP_ENCRYT_KEY_SIZE 16
-#define SMP_ENCRYT_DATA_SIZE 16
-#define SMP_ECNCRPYT_STATUS HCI_SUCCESS
-
 typedef struct {
   RawAddress bd_addr;
   BT_HDR* p_copy;
@@ -273,18 +270,18 @@
   uint8_t cb_evt;
   tSMP_SEC_LEVEL sec_level;
   bool connect_initialized;
-  BT_OCTET16 confirm;
-  BT_OCTET16 rconfirm;
-  BT_OCTET16 rrand; /* for SC this is peer nonce */
-  BT_OCTET16 rand;  /* for SC this is local nonce */
+  Octet16 confirm;
+  Octet16 rconfirm;
+  Octet16 rrand; /* for SC this is peer nonce */
+  Octet16 rand;  /* for SC this is local nonce */
   BT_OCTET32 private_key;
   BT_OCTET32 dhkey;
-  BT_OCTET16 commitment;
-  BT_OCTET16 remote_commitment;
-  BT_OCTET16 local_random; /* local randomizer - passkey or OOB randomizer */
-  BT_OCTET16 peer_random;  /* peer randomizer - passkey or OOB randomizer */
-  BT_OCTET16 dhkey_check;
-  BT_OCTET16 remote_dhkey_check;
+  Octet16 commitment;
+  Octet16 remote_commitment;
+  Octet16 local_random; /* local randomizer - passkey or OOB randomizer */
+  Octet16 peer_random;  /* peer randomizer - passkey or OOB randomizer */
+  Octet16 dhkey_check;
+  Octet16 remote_dhkey_check;
   tSMP_PUBLIC_KEY loc_publ_key;
   tSMP_PUBLIC_KEY peer_publ_key;
   tSMP_OOB_DATA_TYPE req_oob_type;
@@ -307,7 +304,7 @@
   uint8_t
       round; /* authentication stage 1 round for passkey association model */
   uint32_t number_to_display;
-  BT_OCTET16 mac_key;
+  Octet16 mac_key;
   uint8_t peer_enc_size;
   uint8_t loc_enc_size;
   uint8_t peer_i_key;
@@ -315,10 +312,10 @@
   uint8_t local_i_key;
   uint8_t local_r_key;
 
-  BT_OCTET16 tk;
-  BT_OCTET16 ltk;
+  Octet16 tk;
+  Octet16 ltk;
   uint16_t div;
-  BT_OCTET16 csrk; /* storage for local CSRK */
+  Octet16 csrk; /* storage for local CSRK */
   uint16_t ediv;
   BT_OCTET8 enc_rand;
   uint8_t addr_type;
@@ -345,29 +342,6 @@
 extern void smp_sm_event(tSMP_CB* p_cb, tSMP_EVENT event,
                          tSMP_INT_DATA* p_data);
 
-extern void smp_proc_sec_request(tSMP_CB* p_cb, tSMP_INT_DATA* p_data);
-extern void smp_set_fail_nc(bool enable);
-extern void smp_set_fail_conf(bool enable);
-extern void smp_set_passk_entry_fail(bool enable);
-extern void smp_set_oob_fail(bool enable);
-extern void smp_set_peer_sc_notif(bool enable);
-extern void smp_aes_cmac_rfc4493_chk(uint8_t* key, uint8_t* msg,
-                                     uint8_t msg_len, uint8_t mac_len,
-                                     uint8_t* mac);
-extern void smp_f4_calc_chk(uint8_t* U, uint8_t* V, uint8_t* X, uint8_t* Z,
-                            uint8_t* mac);
-extern void smp_g2_calc_chk(uint8_t* U, uint8_t* V, uint8_t* X, uint8_t* Y);
-extern void smp_h6_calc_chk(uint8_t* key, uint8_t* key_id, uint8_t* mac);
-extern void smp_f5_key_calc_chk(uint8_t* w, uint8_t* mac);
-extern void smp_f5_mackey_or_ltk_calc_chk(uint8_t* t, uint8_t* counter,
-                                          uint8_t* key_id, uint8_t* n1,
-                                          uint8_t* n2, uint8_t* a1, uint8_t* a2,
-                                          uint8_t* length, uint8_t* mac);
-extern void smp_f5_calc_chk(uint8_t* w, uint8_t* n1, uint8_t* n2, uint8_t* a1,
-                            uint8_t* a2, uint8_t* mac_key, uint8_t* ltk);
-extern void smp_f6_calc_chk(uint8_t* w, uint8_t* n1, uint8_t* n2, uint8_t* r,
-                            uint8_t* iocap, uint8_t* a1, uint8_t* a2,
-                            uint8_t* mac);
 extern tSMP_STATE smp_get_state(void);
 extern void smp_set_state(tSMP_STATE state);
 
@@ -470,13 +444,10 @@
 extern void smp_cb_cleanup(tSMP_CB* p_cb);
 extern void smp_reset_control_value(tSMP_CB* p_cb);
 extern void smp_proc_pairing_cmpl(tSMP_CB* p_cb);
-extern void smp_convert_string_to_tk(BT_OCTET16 tk, uint32_t passkey);
-extern void smp_mask_enc_key(uint8_t loc_enc_size, uint8_t* p_data);
+extern void smp_convert_string_to_tk(Octet16* tk, uint32_t passkey);
+extern void smp_mask_enc_key(uint8_t loc_enc_size, Octet16* p_data);
 extern void smp_rsp_timeout(void* data);
 extern void smp_delayed_auth_complete_timeout(void* data);
-extern void smp_xor_128(BT_OCTET16 a, BT_OCTET16 b);
-extern bool smp_encrypt_data(uint8_t* key, uint8_t key_len, uint8_t* plain_text,
-                             uint8_t pt_len, tSMP_ENC* p_out);
 extern bool smp_command_has_invalid_parameters(tSMP_CB* p_cb);
 extern void smp_reject_unexpected_pairing_command(const RawAddress& bd_addr);
 extern tSMP_ASSO_MODEL smp_select_association_model(tSMP_CB* p_cb);
@@ -488,7 +459,7 @@
 extern void smp_collect_peer_ble_address(uint8_t* le_addr, tSMP_CB* p_cb);
 extern bool smp_check_commitment(tSMP_CB* p_cb);
 extern void smp_save_secure_connections_long_term_key(tSMP_CB* p_cb);
-extern bool smp_calculate_f5_mackey_and_long_term_key(tSMP_CB* p_cb);
+extern void smp_calculate_f5_mackey_and_long_term_key(tSMP_CB* p_cb);
 extern void smp_remove_fixed_channel(tSMP_CB* p_cb);
 extern bool smp_request_oob_data(tSMP_CB* p_cb);
 
@@ -504,7 +475,7 @@
 extern void smp_use_oob_private_key(tSMP_CB* p_cb, tSMP_INT_DATA* p_data);
 extern void smp_compute_dhkey(tSMP_CB* p_cb);
 extern void smp_calculate_local_commitment(tSMP_CB* p_cb);
-extern void smp_calculate_peer_commitment(tSMP_CB* p_cb, BT_OCTET16 output_buf);
+extern Octet16 smp_calculate_peer_commitment(tSMP_CB* p_cb);
 extern void smp_calculate_numeric_comparison_display_number(
     tSMP_CB* p_cb, tSMP_INT_DATA* p_data);
 extern void smp_calculate_local_dhkey_check(tSMP_CB* p_cb,
@@ -514,31 +485,21 @@
 extern void smp_start_nonce_generation(tSMP_CB* p_cb);
 extern bool smp_calculate_link_key_from_long_term_key(tSMP_CB* p_cb);
 extern bool smp_calculate_long_term_key_from_link_key(tSMP_CB* p_cb);
-extern void smp_calculate_f4(uint8_t* u, uint8_t* v, uint8_t* x, uint8_t z,
-                             uint8_t* c);
-extern uint32_t smp_calculate_g2(uint8_t* u, uint8_t* v, uint8_t* x,
-                                 uint8_t* y);
-extern bool smp_calculate_f5(uint8_t* w, uint8_t* n1, uint8_t* n2, uint8_t* a1,
-                             uint8_t* a2, uint8_t* mac_key, uint8_t* ltk);
-extern bool smp_calculate_f5_mackey_or_long_term_key(
-    uint8_t* t, uint8_t* counter, uint8_t* key_id, uint8_t* n1, uint8_t* n2,
-    uint8_t* a1, uint8_t* a2, uint8_t* length, uint8_t* mac);
-extern bool smp_calculate_f5_key(uint8_t* w, uint8_t* t);
-extern bool smp_calculate_f6(uint8_t* w, uint8_t* n1, uint8_t* n2, uint8_t* r,
-                             uint8_t* iocap, uint8_t* a1, uint8_t* a2,
-                             uint8_t* f3);
-extern bool smp_calculate_h6(uint8_t* w, uint8_t* keyid, uint8_t* h2);
-extern bool smp_calculate_h7(uint8_t* salt, uint8_t* w, uint8_t* h2);
+
 #if (SMP_DEBUG == TRUE)
 extern void smp_debug_print_nbyte_little_endian(uint8_t* p,
                                                 const char* key_name,
                                                 uint8_t len);
+
+inline void smp_debug_print_nbyte_little_endian(const Octet16& p,
+                                                const char* key_name,
+                                                uint8_t len) {
+  smp_debug_print_nbyte_little_endian(const_cast<uint8_t*>(p.data()), key_name,
+                                      len);
+}
 #endif
 
-/* smp_cmac.cc */
-extern bool aes_cipher_msg_auth_code(BT_OCTET16 key, uint8_t* input,
-                                     uint16_t length, uint16_t tlen,
-                                     uint8_t* p_signature);
-extern void print128(BT_OCTET16 x, const uint8_t* key_name);
+extern void print128(const Octet16& x, const uint8_t* key_name);
+extern void smp_xor_128(Octet16* a, const Octet16& b);
 
 #endif /* SMP_INT_H */
diff --git a/stack/smp/smp_keys.cc b/stack/smp/smp_keys.cc
index 6be1da4..da8d0f4 100644
--- a/stack/smp/smp_keys.cc
+++ b/stack/smp/smp_keys.cc
@@ -28,7 +28,6 @@
 #endif
 #include <base/bind.h>
 #include <string.h>
-#include "aes.h"
 #include "bt_utils.h"
 #include "btm_ble_api.h"
 #include "btm_ble_int.h"
@@ -38,16 +37,19 @@
 #include "osi/include/osi.h"
 #include "p_256_ecc_pp.h"
 #include "smp_int.h"
+#include "stack/crypto_toolbox/crypto_toolbox.h"
+
+#include <algorithm>
 
 using base::Bind;
+using crypto_toolbox::aes_128;
 
 #ifndef SMP_MAX_ENC_REPEAT
 #define SMP_MAX_ENC_REPEAT 3
 #endif
 
-static void smp_process_stk(tSMP_CB* p_cb, tSMP_ENC* p);
-static bool smp_calculate_legacy_short_term_key(tSMP_CB* p_cb,
-                                                tSMP_ENC* output);
+static void smp_process_stk(tSMP_CB* p_cb, Octet16* p);
+static Octet16 smp_calculate_legacy_short_term_key(tSMP_CB* p_cb);
 static void smp_process_private_key(tSMP_CB* p_cb);
 
 #define SMP_PASSKEY_MASK 0xfff00000
@@ -75,6 +77,13 @@
 #endif
 }
 
+inline void smp_debug_print_nbyte_little_endian(const Octet16& p,
+                                                const char* key_name,
+                                                uint8_t len) {
+  smp_debug_print_nbyte_little_endian(const_cast<uint8_t*>(p.data()), key_name,
+                                      len);
+}
+
 void smp_debug_print_nbyte_big_endian(uint8_t* p, const char* key_name,
                                       uint8_t len) {
 #if (SMP_DEBUG == TRUE)
@@ -98,80 +107,9 @@
 #endif
 }
 
-/*******************************************************************************
- *
- * Function         smp_encrypt_data
- *
- * Description      This function is called to encrypt data.
- *                  It uses AES-128 encryption algorithm.
- *                  Plain_text is encrypted using key, the result is at p_out.
- *
- * Returns          void
- *
- ******************************************************************************/
-bool smp_encrypt_data(uint8_t* key, uint8_t key_len, uint8_t* plain_text,
-                      uint8_t pt_len, tSMP_ENC* p_out) {
-  aes_context ctx;
-  uint8_t* p_start = NULL;
-  uint8_t* p = NULL;
-  uint8_t* p_rev_data = NULL;   /* input data in big endilan format */
-  uint8_t* p_rev_key = NULL;    /* input key in big endilan format */
-  uint8_t* p_rev_output = NULL; /* encrypted output in big endilan format */
-
-  SMP_TRACE_DEBUG("%s", __func__);
-  if ((p_out == NULL) || (key_len != SMP_ENCRYT_KEY_SIZE)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    return false;
-  }
-
-  p_start = (uint8_t*)osi_calloc(SMP_ENCRYT_DATA_SIZE * 4);
-
-  if (pt_len > SMP_ENCRYT_DATA_SIZE) pt_len = SMP_ENCRYT_DATA_SIZE;
-
-  p = p_start;
-  ARRAY_TO_STREAM(p, plain_text, pt_len);          /* byte 0 to byte 15 */
-  p_rev_data = p = p_start + SMP_ENCRYT_DATA_SIZE; /* start at byte 16 */
-  REVERSE_ARRAY_TO_STREAM(p, p_start,
-                          SMP_ENCRYT_DATA_SIZE);        /* byte 16 to byte 31 */
-  p_rev_key = p;                                        /* start at byte 32 */
-  REVERSE_ARRAY_TO_STREAM(p, key, SMP_ENCRYT_KEY_SIZE); /* byte 32 to byte 47 */
-
-#if (SMP_DEBUG == TRUE && SMP_DEBUG_VERBOSE == TRUE)
-  smp_debug_print_nbyte_little_endian(key, "Key", SMP_ENCRYT_KEY_SIZE);
-  smp_debug_print_nbyte_little_endian(p_start, "Plain text",
-                                      SMP_ENCRYT_DATA_SIZE);
-#endif
-  p_rev_output = p;
-  aes_set_key(p_rev_key, SMP_ENCRYT_KEY_SIZE, &ctx);
-  aes_encrypt(p_rev_data, p, &ctx); /* outputs in byte 48 to byte 63 */
-
-  p = p_out->param_buf;
-  REVERSE_ARRAY_TO_STREAM(p, p_rev_output, SMP_ENCRYT_DATA_SIZE);
-#if (SMP_DEBUG == TRUE && SMP_DEBUG_VERBOSE == TRUE)
-  smp_debug_print_nbyte_little_endian(p_out->param_buf, "Encrypted text",
-                                      SMP_ENCRYT_KEY_SIZE);
-#endif
-
-  p_out->param_len = SMP_ENCRYT_KEY_SIZE;
-  p_out->status = HCI_SUCCESS;
-  p_out->opcode = HCI_BLE_ENCRYPT;
-
-  osi_free(p_start);
-
-  return true;
-}
-
-/*******************************************************************************
- *
- * Function         smp_proc_passkey
- *
- * Description      This function is called to process a passkey.
- *
- * Returns          void
- *
- ******************************************************************************/
+/** This function is called to process a passkey. */
 void smp_proc_passkey(tSMP_CB* p_cb, BT_OCTET8 rand) {
-  uint8_t* tt = p_cb->tk;
+  uint8_t* tt = p_cb->tk.data();
   uint32_t passkey; /* 19655 test number; */
   uint8_t* pp = rand;
 
@@ -183,7 +121,7 @@
   while (passkey > BTM_MAX_PASSKEY_VAL) passkey >>= 1;
 
   /* save the TK */
-  memset(p_cb->tk, 0, BT_OCTET16_LEN);
+  p_cb->tk = {0};
   UINT32_TO_STREAM(tt, passkey);
 
   if (p_cb->p_callback) {
@@ -200,7 +138,7 @@
   } else {
     tSMP_KEY key;
     key.key_type = SMP_KEY_TYPE_TK;
-    key.p_data = p_cb->tk;
+    key.p_data = p_cb->tk.data();
     tSMP_INT_DATA smp_int_data;
     smp_int_data.key = key;
     smp_sm_event(p_cb, SMP_KEY_READY_EVT, &smp_int_data);
@@ -234,22 +172,15 @@
  *
  ******************************************************************************/
 void smp_generate_stk(tSMP_CB* p_cb, UNUSED_ATTR tSMP_INT_DATA* p_data) {
-  tSMP_ENC output;
+  Octet16 output;
 
   SMP_TRACE_DEBUG("%s", __func__);
 
   if (p_cb->le_secure_connections_mode_is_used) {
     SMP_TRACE_WARNING("FOR LE SC LTK IS USED INSTEAD OF STK");
-    output.param_len = SMP_ENCRYT_KEY_SIZE;
-    output.status = HCI_SUCCESS;
-    output.opcode = HCI_BLE_ENCRYPT;
-    memcpy(output.param_buf, p_cb->ltk, SMP_ENCRYT_DATA_SIZE);
-  } else if (!smp_calculate_legacy_short_term_key(p_cb, &output)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    tSMP_INT_DATA smp_int_data;
-    smp_int_data.status = SMP_PAIR_FAIL_UNKNOWN;
-    smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
-    return;
+    output = p_cb->ltk;
+  } else {
+    output = smp_calculate_legacy_short_term_key(p_cb);
   }
 
   smp_process_stk(p_cb, &output);
@@ -259,33 +190,20 @@
  * This function is called to calculate CSRK
  */
 void smp_compute_csrk(uint16_t div, tSMP_CB* p_cb) {
-  BT_OCTET16 er;
   uint8_t buffer[4]; /* for (r || DIV)  r=1*/
   uint16_t r = 1;
   uint8_t* p = buffer;
-  tSMP_ENC output;
 
   p_cb->div = div;
 
   SMP_TRACE_DEBUG("%s: div=%x", __func__, p_cb->div);
-  BTM_GetDeviceEncRoot(er);
+  const Octet16& er = BTM_GetDeviceEncRoot();
   /* CSRK = d1(ER, DIV, 1) */
   UINT16_TO_STREAM(p, p_cb->div);
   UINT16_TO_STREAM(p, r);
 
-  if (!SMP_Encrypt(er, BT_OCTET16_LEN, buffer, 4, &output)) {
-    SMP_TRACE_ERROR("smp_generate_csrk failed");
-    tSMP_INT_DATA smp_int_data;
-    smp_int_data.status = SMP_PAIR_FAIL_UNKNOWN;
-    if (p_cb->smp_over_br) {
-      smp_br_state_machine_event(p_cb, SMP_BR_AUTH_CMPL_EVT, &smp_int_data);
-    } else {
-      smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
-    }
-  } else {
-    memcpy((void*)p_cb->csrk, output.param_buf, BT_OCTET16_LEN);
-    smp_send_csrk_info(p_cb, NULL);
-  }
+  p_cb->csrk = aes_128(er, buffer, 4);
+  smp_send_csrk_info(p_cb, NULL);
 }
 
 /**
@@ -349,22 +267,16 @@
   *p_data = p;
 }
 
-/*******************************************************************************
- *
- * Function         smp_gen_p1_4_confirm
- *
- * Description      Generate Confirm/Compare Step1:
+/** Generate Confirm/Compare Step1:
  *                  p1 = (MSB) pres || preq || rat' || iat' (LSB)
  *                  Fill in values LSB first thus
  *                  p1 = iat' || rat' || preq || pres
- *
- * Returns          void
- *
- ******************************************************************************/
-void smp_gen_p1_4_confirm(tSMP_CB* p_cb, tBLE_ADDR_TYPE remote_bd_addr_type,
-                          BT_OCTET16 p1) {
+ */
+Octet16 smp_gen_p1_4_confirm(tSMP_CB* p_cb,
+                             tBLE_ADDR_TYPE remote_bd_addr_type) {
   SMP_TRACE_DEBUG("%s", __func__);
-  uint8_t* p = (uint8_t*)p1;
+  Octet16 p1;
+  uint8_t* p = p1.data();
   if (p_cb->role == HCI_ROLE_MASTER) {
     /* iat': initiator's (local) address type */
     UINT8_TO_STREAM(p, p_cb->addr_type);
@@ -384,28 +296,23 @@
     /* pres : Pairing Response (local) command */
     smp_concatenate_local(p_cb, &p, SMP_OPCODE_PAIRING_RSP);
   }
-  smp_debug_print_nbyte_little_endian((uint8_t*)p1,
-                                      "p1 = iat' || rat' || preq || pres", 16);
+  smp_debug_print_nbyte_little_endian(p1, "p1 = iat' || rat' || preq || pres",
+                                      16);
+
+  return p1;
 }
 
-/*******************************************************************************
- *
- * Function         smp_gen_p2_4_confirm
- *
- * Description      Generate Confirm/Compare Step2:
+/** Generate Confirm/Compare Step2:
  *                  p2 = (MSB) padding || ia || ra (LSB)
  *                  Fill values LSB first and thus:
  *                  p2 = ra || ia || padding
- *
- * Returns          void
- *
- ******************************************************************************/
-void smp_gen_p2_4_confirm(tSMP_CB* p_cb, const RawAddress& remote_bda,
-                          BT_OCTET16 p2) {
+ */
+Octet16 smp_gen_p2_4_confirm(tSMP_CB* p_cb, const RawAddress& remote_bda) {
   SMP_TRACE_DEBUG("%s", __func__);
-  uint8_t* p = (uint8_t*)p2;
+  Octet16 p2{0};
+  uint8_t* p = p2.data();
   /* 32-bit Padding */
-  memset(p, 0, sizeof(BT_OCTET16));
+  memset(p, 0, OCTET16_LEN);
   if (p_cb->role == HCI_ROLE_MASTER) {
     /* ra : Responder's (remote) address */
     BDADDR_TO_STREAM(p, remote_bda);
@@ -418,6 +325,7 @@
     BDADDR_TO_STREAM(p, remote_bda);
   }
   smp_debug_print_nbyte_little_endian(p2, "p2 = ra || ia || padding", 16);
+  return p2;
 }
 
 /*******************************************************************************
@@ -429,8 +337,8 @@
  * Returns          tSMP_STATUS status of confirmation calculation
  *
  ******************************************************************************/
-tSMP_STATUS smp_calculate_comfirm(tSMP_CB* p_cb, BT_OCTET16 rand,
-                                  tSMP_ENC* output) {
+tSMP_STATUS smp_calculate_comfirm(tSMP_CB* p_cb, const Octet16& rand,
+                                  Octet16* output) {
   SMP_TRACE_DEBUG("%s", __func__);
   RawAddress remote_bda;
   tBLE_ADDR_TYPE remote_bd_addr_type = 0;
@@ -443,31 +351,21 @@
   /* get local connection specific bluetooth address */
   BTM_ReadConnectionAddr(p_cb->pairing_bda, p_cb->local_bda, &p_cb->addr_type);
   /* generate p1 = pres || preq || rat' || iat' */
-  BT_OCTET16 p1;
-  smp_gen_p1_4_confirm(p_cb, remote_bd_addr_type, p1);
+  Octet16 p1 = smp_gen_p1_4_confirm(p_cb, remote_bd_addr_type);
   /* p1' = rand XOR p1 */
-  smp_xor_128(p1, rand);
-  smp_debug_print_nbyte_little_endian((uint8_t*)p1, "p1' = p1 XOR r", 16);
+  smp_xor_128(&p1, rand);
+  smp_debug_print_nbyte_little_endian(p1, "p1' = p1 XOR r", 16);
   /* calculate e1 = e(k, p1'), where k = TK */
-  smp_debug_print_nbyte_little_endian(p_cb->tk, "TK", 16);
-  memset(output, 0, sizeof(tSMP_ENC));
-  if (!SMP_Encrypt(p_cb->tk, BT_OCTET16_LEN, p1, BT_OCTET16_LEN, output)) {
-    SMP_TRACE_ERROR("%s: failed encryption at e1 = e(k, p1')");
-    return SMP_PAIR_FAIL_UNKNOWN;
-  }
-  smp_debug_print_nbyte_little_endian(output->param_buf, "e1 = e(k, p1')", 16);
+  smp_debug_print_nbyte_little_endian(p_cb->tk.data(), "TK", 16);
+  Octet16 e1 = aes_128(p_cb->tk, p1);
+  smp_debug_print_nbyte_little_endian(e1.data(), "e1 = e(k, p1')", 16);
   /* generate p2 = padding || ia || ra */
-  BT_OCTET16 p2;
-  smp_gen_p2_4_confirm(p_cb, remote_bda, p2);
+  Octet16 p2 = smp_gen_p2_4_confirm(p_cb, remote_bda);
   /* calculate p2' = (p2 XOR e1) */
-  smp_xor_128(p2, output->param_buf);
-  smp_debug_print_nbyte_little_endian((uint8_t*)p2, "p2' = p2 XOR e1", 16);
+  smp_xor_128(&p2, e1);
+  smp_debug_print_nbyte_little_endian(p2, "p2' = p2 XOR e1", 16);
   /* calculate: c1 = e(k, p2') */
-  memset(output, 0, sizeof(tSMP_ENC));
-  if (!SMP_Encrypt(p_cb->tk, BT_OCTET16_LEN, p2, BT_OCTET16_LEN, output)) {
-    SMP_TRACE_ERROR("%s: failed encryption at e1 = e(k, p2')");
-    return SMP_PAIR_FAIL_UNKNOWN;
-  }
+  *output = aes_128(p_cb->tk, p2);
   return SMP_SUCCESS;
 }
 
@@ -484,8 +382,8 @@
  ******************************************************************************/
 static void smp_generate_confirm(tSMP_CB* p_cb) {
   SMP_TRACE_DEBUG("%s", __func__);
-  smp_debug_print_nbyte_little_endian((uint8_t*)p_cb->rand, "local_rand", 16);
-  tSMP_ENC output;
+  smp_debug_print_nbyte_little_endian(p_cb->rand.data(), "local_rand", 16);
+  Octet16 output;
   tSMP_STATUS status = smp_calculate_comfirm(p_cb, p_cb->rand, &output);
   if (status != SMP_SUCCESS) {
     tSMP_INT_DATA smp_int_data;
@@ -494,11 +392,11 @@
     return;
   }
   tSMP_KEY key;
-  memcpy(p_cb->confirm, output.param_buf, BT_OCTET16_LEN);
+  p_cb->confirm = output;
   smp_debug_print_nbyte_little_endian(p_cb->confirm, "Local Confirm generated",
                                       16);
   key.key_type = SMP_KEY_TYPE_CFM;
-  key.p_data = output.param_buf;
+  key.p_data = output.data();
   tSMP_INT_DATA smp_int_data;
   smp_int_data.key = key;
   smp_sm_event(p_cb, SMP_KEY_READY_EVT, &smp_int_data);
@@ -521,7 +419,7 @@
   /* generate MRand or SRand */
   btsnd_hcic_ble_rand(Bind(
       [](tSMP_CB* p_cb, BT_OCTET8 rand) {
-        memcpy((void*)p_cb->rand, rand, 8);
+        memcpy(p_cb->rand.data(), rand, 8);
 
         /* generate 64 MSB of MRand or SRand */
         btsnd_hcic_ble_rand(Bind(
@@ -548,8 +446,8 @@
  ******************************************************************************/
 void smp_generate_compare(tSMP_CB* p_cb, UNUSED_ATTR tSMP_INT_DATA* p_data) {
   SMP_TRACE_DEBUG("smp_generate_compare ");
-  smp_debug_print_nbyte_little_endian((uint8_t*)p_cb->rrand, "peer rand", 16);
-  tSMP_ENC output;
+  smp_debug_print_nbyte_little_endian(p_cb->rrand, "peer rand", 16);
+  Octet16 output;
   tSMP_STATUS status = smp_calculate_comfirm(p_cb, p_cb->rrand, &output);
   if (status != SMP_SUCCESS) {
     tSMP_INT_DATA smp_int_data;
@@ -558,48 +456,38 @@
     return;
   }
   tSMP_KEY key;
-  smp_debug_print_nbyte_little_endian(output.param_buf,
-                                      "Remote Confirm generated", 16);
+  smp_debug_print_nbyte_little_endian(output.data(), "Remote Confirm generated",
+                                      16);
   key.key_type = SMP_KEY_TYPE_CMP;
-  key.p_data = output.param_buf;
+  key.p_data = output.data();
   tSMP_INT_DATA smp_int_data;
   smp_int_data.key = key;
   smp_sm_event(p_cb, SMP_KEY_READY_EVT, &smp_int_data);
 }
 
-/*******************************************************************************
- *
- * Function         smp_process_stk
- *
- * Description      This function is called when STK is generated
- *                  proceed to send the encrypt the link using STK.
- *
- * Returns          void
- *
- ******************************************************************************/
-static void smp_process_stk(tSMP_CB* p_cb, tSMP_ENC* p) {
+/** This function is called when STK is generated proceed to send the encrypt
+ * the link using STK. */
+static void smp_process_stk(tSMP_CB* p_cb, Octet16* p) {
   tSMP_KEY key;
 
   SMP_TRACE_DEBUG("smp_process_stk ");
 #if (SMP_DEBUG == TRUE)
   SMP_TRACE_ERROR("STK Generated");
 #endif
-  smp_mask_enc_key(p_cb->loc_enc_size, p->param_buf);
+  smp_mask_enc_key(p_cb->loc_enc_size, p);
 
   key.key_type = SMP_KEY_TYPE_STK;
-  key.p_data = p->param_buf;
+  key.p_data = p->data();
 
   tSMP_INT_DATA smp_int_data;
   smp_int_data.key = key;
   smp_sm_event(p_cb, SMP_KEY_READY_EVT, &smp_int_data);
 }
 
-/**
- * This function is to calculate EDIV = Y xor DIV
- */
-static void smp_process_ediv(tSMP_CB* p_cb, tSMP_ENC* p) {
+/** This function calculates EDIV = Y xor DIV */
+static void smp_process_ediv(tSMP_CB* p_cb, Octet16& p) {
   tSMP_KEY key;
-  uint8_t* pp = p->param_buf;
+  uint8_t* pp = p.data();
   uint16_t y;
 
   SMP_TRACE_DEBUG("smp_process_ediv ");
@@ -610,7 +498,7 @@
   /* send LTK ready */
   SMP_TRACE_ERROR("LTK ready");
   key.key_type = SMP_KEY_TYPE_LTK;
-  key.p_data = p->param_buf;
+  key.p_data = p.data();
 
   tSMP_INT_DATA smp_int_data;
   smp_int_data.key = key;
@@ -623,19 +511,11 @@
 static void smp_generate_y(tSMP_CB* p_cb, BT_OCTET8 rand) {
   SMP_TRACE_DEBUG("%s ", __func__);
 
-  BT_OCTET16 dhk;
-  BTM_GetDeviceDHK(dhk);
+  const Octet16& dhk = BTM_GetDeviceDHK();
 
   memcpy(p_cb->enc_rand, rand, BT_OCTET8_LEN);
-  tSMP_ENC output;
-  if (!SMP_Encrypt(dhk, BT_OCTET16_LEN, rand, BT_OCTET8_LEN, &output)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    tSMP_INT_DATA smp_int_data;
-    smp_int_data.status = SMP_PAIR_FAIL_UNKNOWN;
-    smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
-  } else {
-    smp_process_ediv(p_cb, &output);
-  }
+  Octet16 output = aes_128(dhk, rand, BT_OCTET8_LEN);
+  smp_process_ediv(p_cb, output);
 }
 
 /**
@@ -645,25 +525,16 @@
   p_cb->div = div;
 
   SMP_TRACE_DEBUG("%s", __func__);
-  BT_OCTET16 er;
-  BTM_GetDeviceEncRoot(er);
+  const Octet16& er = BTM_GetDeviceEncRoot();
 
-  tSMP_ENC output;
   /* LTK = d1(ER, DIV, 0)= e(ER, DIV)*/
-  if (!SMP_Encrypt(er, BT_OCTET16_LEN, (uint8_t*)&p_cb->div, sizeof(uint16_t),
-                   &output)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    tSMP_INT_DATA smp_int_data;
-    smp_int_data.status = SMP_PAIR_FAIL_UNKNOWN;
-    smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
-  } else {
-    /* mask the LTK */
-    smp_mask_enc_key(p_cb->loc_enc_size, output.param_buf);
-    memcpy((void*)p_cb->ltk, output.param_buf, BT_OCTET16_LEN);
+  Octet16 ltk = aes_128(er, (uint8_t*)&p_cb->div, sizeof(uint16_t));
+  /* mask the LTK */
+  smp_mask_enc_key(p_cb->loc_enc_size, &ltk);
+  p_cb->ltk = ltk;
 
-    /* generate EDIV and rand now */
-    btsnd_hcic_ble_rand(Bind(&smp_generate_y, p_cb));
-  }
+  /* generate EDIV and rand now */
+  btsnd_hcic_ble_rand(Bind(&smp_generate_y, p_cb));
 }
 
 /*******************************************************************************
@@ -710,36 +581,21 @@
   }
 }
 
-/*******************************************************************************
- *
- * Function         smp_calculate_legacy_short_term_key
- *
- * Description      The function calculates legacy STK.
- *
- * Returns          false if out of resources, true in other cases.
- *
- ******************************************************************************/
-bool smp_calculate_legacy_short_term_key(tSMP_CB* p_cb, tSMP_ENC* output) {
+/* The function calculates legacy STK */
+Octet16 smp_calculate_legacy_short_term_key(tSMP_CB* p_cb) {
   SMP_TRACE_DEBUG("%s", __func__);
 
-  BT_OCTET16 ptext;
-  uint8_t* p = ptext;
-  memset(p, 0, BT_OCTET16_LEN);
+  Octet16 text{0};
   if (p_cb->role == HCI_ROLE_MASTER) {
-    memcpy(p, p_cb->rand, BT_OCTET8_LEN);
-    memcpy(&p[BT_OCTET8_LEN], p_cb->rrand, BT_OCTET8_LEN);
+    memcpy(text.data(), p_cb->rand.data(), BT_OCTET8_LEN);
+    memcpy(text.data() + BT_OCTET8_LEN, p_cb->rrand.data(), BT_OCTET8_LEN);
   } else {
-    memcpy(p, p_cb->rrand, BT_OCTET8_LEN);
-    memcpy(&p[BT_OCTET8_LEN], p_cb->rand, BT_OCTET8_LEN);
+    memcpy(text.data(), p_cb->rrand.data(), BT_OCTET8_LEN);
+    memcpy(text.data() + BT_OCTET8_LEN, p_cb->rand.data(), BT_OCTET8_LEN);
   }
 
   /* generate STK = Etk(rand|rrand)*/
-  bool encrypted =
-      SMP_Encrypt(p_cb->tk, BT_OCTET16_LEN, ptext, BT_OCTET16_LEN, output);
-  if (!encrypted) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-  }
-  return encrypted;
+  return aes_128(p_cb->tk, text);
 }
 
 /*******************************************************************************
@@ -889,15 +745,7 @@
                                       BT_OCTET32_LEN);
 }
 
-/*******************************************************************************
- *
- * Function         smp_calculate_local_commitment
- *
- * Description      The function calculates and saves local commmitment in CB.
- *
- * Returns          void
- *
- ******************************************************************************/
+/** The function calculates and saves local commmitment in CB. */
 void smp_calculate_local_commitment(tSMP_CB* p_cb) {
   uint8_t random_input;
 
@@ -910,21 +758,22 @@
         SMP_TRACE_WARNING(
             "local commitment calc on master is not expected "
             "for Just Works/Numeric Comparison models");
-      smp_calculate_f4(p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, p_cb->rand,
-                       0, p_cb->commitment);
+      p_cb->commitment = crypto_toolbox::f4(
+          p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, p_cb->rand, 0);
       break;
     case SMP_MODEL_SEC_CONN_PASSKEY_ENT:
     case SMP_MODEL_SEC_CONN_PASSKEY_DISP:
       random_input =
-          smp_calculate_random_input(p_cb->local_random, p_cb->round);
-      smp_calculate_f4(p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, p_cb->rand,
-                       random_input, p_cb->commitment);
+          smp_calculate_random_input(p_cb->local_random.data(), p_cb->round);
+      p_cb->commitment =
+          crypto_toolbox::f4(p_cb->loc_publ_key.x, p_cb->peer_publ_key.x,
+                             p_cb->rand, random_input);
       break;
     case SMP_MODEL_SEC_CONN_OOB:
       SMP_TRACE_WARNING(
           "local commitment calc is expected for OOB model BEFORE pairing");
-      smp_calculate_f4(p_cb->loc_publ_key.x, p_cb->loc_publ_key.x,
-                       p_cb->local_random, 0, p_cb->commitment);
+      p_cb->commitment = crypto_toolbox::f4(
+          p_cb->loc_publ_key.x, p_cb->loc_publ_key.x, p_cb->local_random, 0);
       break;
     default:
       SMP_TRACE_ERROR("Association Model = %d is not used in LE SC",
@@ -935,21 +784,12 @@
   SMP_TRACE_EVENT("local commitment calculation is completed");
 }
 
-/*******************************************************************************
- *
- * Function         smp_calculate_peer_commitment
- *
- * Description      The function calculates and saves peer commmitment at the
- *                  provided output buffer.
- *
- * Returns          void
- *
- ******************************************************************************/
-void smp_calculate_peer_commitment(tSMP_CB* p_cb, BT_OCTET16 output_buf) {
+/** The function calculates peer commmitment */
+Octet16 smp_calculate_peer_commitment(tSMP_CB* p_cb) {
   uint8_t ri;
 
   SMP_TRACE_DEBUG("%s", __func__);
-
+  Octet16 output;
   switch (p_cb->selected_association_model) {
     case SMP_MODEL_SEC_CONN_JUSTWORKS:
     case SMP_MODEL_SEC_CONN_NUM_COMP:
@@ -957,97 +797,27 @@
         SMP_TRACE_WARNING(
             "peer commitment calc on slave is not expected "
             "for Just Works/Numeric Comparison models");
-      smp_calculate_f4(p_cb->peer_publ_key.x, p_cb->loc_publ_key.x, p_cb->rrand,
-                       0, output_buf);
+      output = crypto_toolbox::f4(p_cb->peer_publ_key.x, p_cb->loc_publ_key.x,
+                                  p_cb->rrand, 0);
       break;
     case SMP_MODEL_SEC_CONN_PASSKEY_ENT:
     case SMP_MODEL_SEC_CONN_PASSKEY_DISP:
-      ri = smp_calculate_random_input(p_cb->peer_random, p_cb->round);
-      smp_calculate_f4(p_cb->peer_publ_key.x, p_cb->loc_publ_key.x, p_cb->rrand,
-                       ri, output_buf);
+      ri = smp_calculate_random_input(p_cb->peer_random.data(), p_cb->round);
+      output = crypto_toolbox::f4(p_cb->peer_publ_key.x, p_cb->loc_publ_key.x,
+                                  p_cb->rrand, ri);
       break;
     case SMP_MODEL_SEC_CONN_OOB:
-      smp_calculate_f4(p_cb->peer_publ_key.x, p_cb->peer_publ_key.x,
-                       p_cb->peer_random, 0, output_buf);
+      output = crypto_toolbox::f4(p_cb->peer_publ_key.x, p_cb->peer_publ_key.x,
+                                  p_cb->peer_random, 0);
       break;
     default:
       SMP_TRACE_ERROR("Association Model = %d is not used in LE SC",
                       p_cb->selected_association_model);
-      return;
+      return output;
   }
 
   SMP_TRACE_EVENT("peer commitment calculation is completed");
-}
-
-/*******************************************************************************
- *
- * Function         smp_calculate_f4
- *
- * Description      The function calculates
- *                  C = f4(U, V, X, Z) = AES-CMAC (U||V||Z)
- *                                               X
- *                  where
- *                  input:  U is 256 bit,
- *                          V is 256 bit,
- *                          X is 128 bit,
- *                          Z is 8 bit,
- *                  output: C is 128 bit.
- *
- * Returns          void
- *
- * Note             The LSB is the first octet, the MSB is the last octet of
- *                  the AES-CMAC input/output stream.
- *
- ******************************************************************************/
-void smp_calculate_f4(uint8_t* u, uint8_t* v, uint8_t* x, uint8_t z,
-                      uint8_t* c) {
-  uint8_t msg_len = BT_OCTET32_LEN /* U size */ + BT_OCTET32_LEN /* V size */ +
-                    1 /* Z size */;
-  uint8_t msg[BT_OCTET32_LEN + BT_OCTET32_LEN + 1];
-  uint8_t key[BT_OCTET16_LEN];
-  uint8_t cmac[BT_OCTET16_LEN];
-  uint8_t* p = NULL;
-#if (SMP_DEBUG == TRUE)
-  uint8_t* p_prnt = NULL;
-#endif
-
-  SMP_TRACE_DEBUG("%s", __func__);
-
-#if (SMP_DEBUG == TRUE)
-  p_prnt = u;
-  smp_debug_print_nbyte_little_endian(p_prnt, "U", BT_OCTET32_LEN);
-  p_prnt = v;
-  smp_debug_print_nbyte_little_endian(p_prnt, "V", BT_OCTET32_LEN);
-  p_prnt = x;
-  smp_debug_print_nbyte_little_endian(p_prnt, "X", BT_OCTET16_LEN);
-  p_prnt = &z;
-  smp_debug_print_nbyte_little_endian(p_prnt, "Z", 1);
-#endif
-
-  p = msg;
-  UINT8_TO_STREAM(p, z);
-  ARRAY_TO_STREAM(p, v, BT_OCTET32_LEN);
-  ARRAY_TO_STREAM(p, u, BT_OCTET32_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = msg;
-  smp_debug_print_nbyte_little_endian(p_prnt, "M", msg_len);
-#endif
-
-  p = key;
-  ARRAY_TO_STREAM(p, x, BT_OCTET16_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = key;
-  smp_debug_print_nbyte_little_endian(p_prnt, "K", BT_OCTET16_LEN);
-#endif
-
-  aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = cmac;
-  smp_debug_print_nbyte_little_endian(p_prnt, "AES_CMAC", BT_OCTET16_LEN);
-#endif
-
-  p = c;
-  ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN);
+  return output;
 }
 
 /*******************************************************************************
@@ -1065,10 +835,10 @@
   SMP_TRACE_DEBUG("%s", __func__);
 
   if (p_cb->role == HCI_ROLE_MASTER) {
-    p_cb->number_to_display = smp_calculate_g2(
+    p_cb->number_to_display = crypto_toolbox::g2(
         p_cb->loc_publ_key.x, p_cb->peer_publ_key.x, p_cb->rand, p_cb->rrand);
   } else {
-    p_cb->number_to_display = smp_calculate_g2(
+    p_cb->number_to_display = crypto_toolbox::g2(
         p_cb->peer_publ_key.x, p_cb->loc_publ_key.x, p_cb->rrand, p_cb->rand);
   }
 
@@ -1089,398 +859,6 @@
   return;
 }
 
-/*******************************************************************************
- *
- * Function         smp_calculate_g2
- *
- * Description      The function calculates
- *                  g2(U, V, X, Y) = AES-CMAC (U||V||Y) mod 2**32 mod 10**6
- *                                           X
- *                  and
- *                  Vres = g2(U, V, X, Y) mod 10**6
- *                  where
- *                  input:  U     is 256 bit,
- *                          V     is 256 bit,
- *                          X     is 128 bit,
- *                          Y     is 128 bit,
- *
- * Returns          Vres.
- *                  Expected value has to be in the range [0 - 999999] i.e.
- *                        [0 - 0xF423F].
- *                  Vres = 1000000 means that the calculation fails.
- *
- * Note             The LSB is the first octet, the MSB is the last octet of
- *                  the AES-CMAC input/output stream.
- *
- ******************************************************************************/
-uint32_t smp_calculate_g2(uint8_t* u, uint8_t* v, uint8_t* x, uint8_t* y) {
-  uint8_t msg_len = BT_OCTET32_LEN /* U size */ + BT_OCTET32_LEN /* V size */
-                    + BT_OCTET16_LEN /* Y size */;
-  uint8_t msg[BT_OCTET32_LEN + BT_OCTET32_LEN + BT_OCTET16_LEN];
-  uint8_t key[BT_OCTET16_LEN];
-  uint8_t cmac[BT_OCTET16_LEN];
-  uint8_t* p = NULL;
-  uint32_t vres;
-#if (SMP_DEBUG == TRUE)
-  uint8_t* p_prnt = NULL;
-#endif
-
-  SMP_TRACE_DEBUG("%s", __func__);
-
-  p = msg;
-  ARRAY_TO_STREAM(p, y, BT_OCTET16_LEN);
-  ARRAY_TO_STREAM(p, v, BT_OCTET32_LEN);
-  ARRAY_TO_STREAM(p, u, BT_OCTET32_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = u;
-  smp_debug_print_nbyte_little_endian(p_prnt, "U", BT_OCTET32_LEN);
-  p_prnt = v;
-  smp_debug_print_nbyte_little_endian(p_prnt, "V", BT_OCTET32_LEN);
-  p_prnt = x;
-  smp_debug_print_nbyte_little_endian(p_prnt, "X", BT_OCTET16_LEN);
-  p_prnt = y;
-  smp_debug_print_nbyte_little_endian(p_prnt, "Y", BT_OCTET16_LEN);
-#endif
-
-  p = key;
-  ARRAY_TO_STREAM(p, x, BT_OCTET16_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = key;
-  smp_debug_print_nbyte_little_endian(p_prnt, "K", BT_OCTET16_LEN);
-#endif
-
-  if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    return (BTM_MAX_PASSKEY_VAL + 1);
-  }
-
-#if (SMP_DEBUG == TRUE)
-  p_prnt = cmac;
-  smp_debug_print_nbyte_little_endian(p_prnt, "AES-CMAC", BT_OCTET16_LEN);
-#endif
-
-  /* vres = cmac mod 2**32 mod 10**6 */
-  p = &cmac[0];
-  STREAM_TO_UINT32(vres, p);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = (uint8_t*)&vres;
-  smp_debug_print_nbyte_little_endian(p_prnt, "cmac mod 2**32", 4);
-#endif
-
-  while (vres > BTM_MAX_PASSKEY_VAL) vres -= (BTM_MAX_PASSKEY_VAL + 1);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = (uint8_t*)&vres;
-  smp_debug_print_nbyte_little_endian(p_prnt, "cmac mod 2**32 mod 10**6", 4);
-#endif
-
-  SMP_TRACE_ERROR("Value for numeric comparison = %d", vres);
-  return vres;
-}
-
-/*******************************************************************************
- *
- * Function         smp_calculate_f5
- *
- * Description      The function provides two AES-CMAC that are supposed to be
- *                    used as
- *                  - MacKey (used in pairing DHKey check calculation);
- *                  - LTK (used to ecrypt the link after completion of Phase 2
- *                    and on reconnection, to derive BR/EDR LK).
- *                  The function inputs are W, N1, N2, A1, A2.
- *                  F5 rules:
- *                  - the value used as key in MacKey/LTK (T) is calculated
- *                    (function smp_calculate_f5_key(...));
- *                    The formula is:
- *                          T = AES-CMAC    (W)
- *                                      salt
- *                    where salt is internal parameter of
- *                    smp_calculate_f5_key(...).
- *                  - MacKey and LTK are calculated as AES-MAC values received
- *                    with the key T calculated in the previous step and the
- *                    plaintext message built from the external parameters N1,
- *                    N2, A1, A2 and the internal parameters counter, keyID,
- *                    length.
- *                    The function smp_calculate_f5_mackey_or_long_term_key(...)
- *                    is used in the calculations.
- *                    The same formula is used in calculation of MacKey and LTK
- *                    and the same parameter values except the value of the
- *                    internal parameter counter:
- *                    - in MacKey calculations the value is 0;
- *                    - in LTK calculations the value is 1.
- *                      MacKey  =
- *                       AES-CMAC (Counter=0||keyID||N1||N2||A1||A2||Length=256)
- *                               T
- *                      LTK     =
- *                       AES-CMAC (Counter=1||keyID||N1||N2||A1||A2||Length=256)
- *                               T
- *                  The parameters are
- *                  input:
- *                          W       is 256 bits,
- *                          N1      is 128 bits,
- *                          N2      is 128 bits,
- *                          A1 is 56 bit,
- *                          A2 is 56 bit.
- *                  internal:
- *                          Counter is 8 bits,  its value is 0 for MacKey,
- *                                                          1 for LTK;
- *                          KeyId   is 32 bits, its value is
- *                                              0x62746c65 (MSB~LSB);
- *                          Length  is 16 bits, its value is 0x0100
- *                                              (MSB~LSB).
- *                  output:
- *                          MacKey  is 128 bits;
- *                          LTK     is 128 bits
- *
- * Returns          false if out of resources, true in other cases.
- *
- * Note             The LSB is the first octet, the MSB is the last octet of
- *                  the AES-CMAC input/output stream.
- *
- ******************************************************************************/
-bool smp_calculate_f5(uint8_t* w, uint8_t* n1, uint8_t* n2, uint8_t* a1,
-                      uint8_t* a2, uint8_t* mac_key, uint8_t* ltk) {
-  BT_OCTET16 t; /* AES-CMAC output in smp_calculate_f5_key(...), key in */
-                /* smp_calculate_f5_mackey_or_long_term_key(...) */
-#if (SMP_DEBUG == TRUE)
-  uint8_t* p_prnt = NULL;
-#endif
-  /* internal parameters: */
-
-  /*
-      counter is 0 for MacKey,
-              is 1 for LTK
-  */
-  uint8_t counter_mac_key[1] = {0};
-  uint8_t counter_ltk[1] = {1};
-  /*
-      keyID   62746c65
-  */
-  uint8_t key_id[4] = {0x65, 0x6c, 0x74, 0x62};
-  /*
-      length  0100
-  */
-  uint8_t length[2] = {0x00, 0x01};
-
-  SMP_TRACE_DEBUG("%s", __func__);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = w;
-  smp_debug_print_nbyte_little_endian(p_prnt, "W", BT_OCTET32_LEN);
-  p_prnt = n1;
-  smp_debug_print_nbyte_little_endian(p_prnt, "N1", BT_OCTET16_LEN);
-  p_prnt = n2;
-  smp_debug_print_nbyte_little_endian(p_prnt, "N2", BT_OCTET16_LEN);
-  p_prnt = a1;
-  smp_debug_print_nbyte_little_endian(p_prnt, "A1", 7);
-  p_prnt = a2;
-  smp_debug_print_nbyte_little_endian(p_prnt, "A2", 7);
-#endif
-
-  if (!smp_calculate_f5_key(w, t)) {
-    SMP_TRACE_ERROR("%s failed to calc T", __func__);
-    return false;
-  }
-#if (SMP_DEBUG == TRUE)
-  p_prnt = t;
-  smp_debug_print_nbyte_little_endian(p_prnt, "T", BT_OCTET16_LEN);
-#endif
-
-  if (!smp_calculate_f5_mackey_or_long_term_key(t, counter_mac_key, key_id, n1,
-                                                n2, a1, a2, length, mac_key)) {
-    SMP_TRACE_ERROR("%s failed to calc MacKey", __func__);
-    return false;
-  }
-#if (SMP_DEBUG == TRUE)
-  p_prnt = mac_key;
-  smp_debug_print_nbyte_little_endian(p_prnt, "MacKey", BT_OCTET16_LEN);
-#endif
-
-  if (!smp_calculate_f5_mackey_or_long_term_key(t, counter_ltk, key_id, n1, n2,
-                                                a1, a2, length, ltk)) {
-    SMP_TRACE_ERROR("%s failed to calc LTK", __func__);
-    return false;
-  }
-#if (SMP_DEBUG == TRUE)
-  p_prnt = ltk;
-  smp_debug_print_nbyte_little_endian(p_prnt, "LTK", BT_OCTET16_LEN);
-#endif
-
-  return true;
-}
-
-/*******************************************************************************
- *
- * Function         smp_calculate_f5_mackey_or_long_term_key
- *
- * Description      The function calculates the value of MacKey or LTK by the
- *                  rules defined for f5 function.
- *                  At the moment exactly the same formula is used to calculate
- *                  LTK and MacKey.
- *                  The difference is the value of input parameter Counter:
- *                  - in MacKey calculations the value is 0;
- *                  - in LTK calculations the value is 1.
- *                  The formula:
- *                  mac = AES-CMAC (Counter||keyID||N1||N2||A1||A2||Length)
- *                                T
- *                  where
- *                  input:      T       is 256 bits;
- *                              Counter is 8 bits, its value is 0 for MacKey,
- *                                                              1 for LTK;
- *                              keyID   is 32 bits, its value is 0x62746c65;
- *                              N1      is 128 bits;
- *                              N2      is 128 bits;
- *                              A1      is 56 bits;
- *                              A2      is 56 bits;
- *                              Length  is 16 bits, its value is 0x0100
- *                  output:     LTK     is 128 bit.
- *
- * Returns          false if out of resources, true in other cases.
- *
- * Note             The LSB is the first octet, the MSB is the last octet of
- *                  the AES-CMAC input/output stream.
- *
- ******************************************************************************/
-bool smp_calculate_f5_mackey_or_long_term_key(uint8_t* t, uint8_t* counter,
-                                              uint8_t* key_id, uint8_t* n1,
-                                              uint8_t* n2, uint8_t* a1,
-                                              uint8_t* a2, uint8_t* length,
-                                              uint8_t* mac) {
-  uint8_t* p = NULL;
-  uint8_t cmac[BT_OCTET16_LEN];
-  uint8_t key[BT_OCTET16_LEN];
-  uint8_t msg_len = 1 /* Counter size */ + 4 /* keyID size */ +
-                    BT_OCTET16_LEN /* N1 size */ +
-                    BT_OCTET16_LEN /* N2 size */ + 7 /* A1 size*/ +
-                    7 /* A2 size*/ + 2 /* Length size */;
-  uint8_t msg[1 + 4 + BT_OCTET16_LEN + BT_OCTET16_LEN + 7 + 7 + 2];
-  bool ret = true;
-#if (SMP_DEBUG == TRUE)
-  uint8_t* p_prnt = NULL;
-#endif
-
-  SMP_TRACE_DEBUG("%s", __func__);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = t;
-  smp_debug_print_nbyte_little_endian(p_prnt, "T", BT_OCTET16_LEN);
-  p_prnt = counter;
-  smp_debug_print_nbyte_little_endian(p_prnt, "Counter", 1);
-  p_prnt = key_id;
-  smp_debug_print_nbyte_little_endian(p_prnt, "KeyID", 4);
-  p_prnt = n1;
-  smp_debug_print_nbyte_little_endian(p_prnt, "N1", BT_OCTET16_LEN);
-  p_prnt = n2;
-  smp_debug_print_nbyte_little_endian(p_prnt, "N2", BT_OCTET16_LEN);
-  p_prnt = a1;
-  smp_debug_print_nbyte_little_endian(p_prnt, "A1", 7);
-  p_prnt = a2;
-  smp_debug_print_nbyte_little_endian(p_prnt, "A2", 7);
-  p_prnt = length;
-  smp_debug_print_nbyte_little_endian(p_prnt, "Length", 2);
-#endif
-
-  p = key;
-  ARRAY_TO_STREAM(p, t, BT_OCTET16_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = key;
-  smp_debug_print_nbyte_little_endian(p_prnt, "K", BT_OCTET16_LEN);
-#endif
-  p = msg;
-  ARRAY_TO_STREAM(p, length, 2);
-  ARRAY_TO_STREAM(p, a2, 7);
-  ARRAY_TO_STREAM(p, a1, 7);
-  ARRAY_TO_STREAM(p, n2, BT_OCTET16_LEN);
-  ARRAY_TO_STREAM(p, n1, BT_OCTET16_LEN);
-  ARRAY_TO_STREAM(p, key_id, 4);
-  ARRAY_TO_STREAM(p, counter, 1);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = msg;
-  smp_debug_print_nbyte_little_endian(p_prnt, "M", msg_len);
-#endif
-
-  if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    ret = false;
-  }
-
-#if (SMP_DEBUG == TRUE)
-  p_prnt = cmac;
-  smp_debug_print_nbyte_little_endian(p_prnt, "AES-CMAC", BT_OCTET16_LEN);
-#endif
-
-  p = mac;
-  ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN);
-  return ret;
-}
-
-/*******************************************************************************
- *
- * Function         smp_calculate_f5_key
- *
- * Description      The function calculates key T used in calculation of
- *                  MacKey and LTK (f5 output is defined as MacKey || LTK).
- *                  T = AES-CMAC    (W)
- *                              salt
- *                  where
- *                  Internal:   salt    is 128 bit.
- *                  input:      W       is 256 bit.
- *                  Output:     T       is 128 bit.
- *
- * Returns          false if out of resources, true in other cases.
- *
- * Note             The LSB is the first octet, the MSB is the last octet of
- *                  the AES-CMAC input/output stream.
- *
- ******************************************************************************/
-bool smp_calculate_f5_key(uint8_t* w, uint8_t* t) {
-  uint8_t* p = NULL;
-  /* Please see 2.2.7 LE Secure Connections Key Generation Function f5 */
-  /*
-      salt:   6C88 8391 AAF5 A538 6037 0BDB 5A60 83BE
-  */
-  BT_OCTET16 salt = {0xBE, 0x83, 0x60, 0x5A, 0xDB, 0x0B, 0x37, 0x60,
-                     0x38, 0xA5, 0xF5, 0xAA, 0x91, 0x83, 0x88, 0x6C};
-#if (SMP_DEBUG == TRUE)
-  uint8_t* p_prnt = NULL;
-#endif
-
-  SMP_TRACE_DEBUG("%s", __func__);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = salt;
-  smp_debug_print_nbyte_little_endian(p_prnt, "salt", BT_OCTET16_LEN);
-  p_prnt = w;
-  smp_debug_print_nbyte_little_endian(p_prnt, "W", BT_OCTET32_LEN);
-#endif
-
-  BT_OCTET16 key;
-  BT_OCTET32 msg;
-
-  p = key;
-  ARRAY_TO_STREAM(p, salt, BT_OCTET16_LEN);
-  p = msg;
-  ARRAY_TO_STREAM(p, w, BT_OCTET32_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_prnt = key;
-  smp_debug_print_nbyte_little_endian(p_prnt, "K", BT_OCTET16_LEN);
-  p_prnt = msg;
-  smp_debug_print_nbyte_little_endian(p_prnt, "M", BT_OCTET32_LEN);
-#endif
-
-  BT_OCTET16 cmac;
-  bool ret = true;
-  if (!aes_cipher_msg_auth_code(key, msg, BT_OCTET32_LEN, BT_OCTET16_LEN,
-                                cmac)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    ret = false;
-  }
-
-#if (SMP_DEBUG == TRUE)
-  p_prnt = cmac;
-  smp_debug_print_nbyte_little_endian(p_prnt, "AES-CMAC", BT_OCTET16_LEN);
-#endif
-
-  p = t;
-  ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN);
-  return ret;
-}
 
 /*******************************************************************************
  *
@@ -1507,8 +885,8 @@
 
   smp_collect_local_ble_address(a, p_cb);
   smp_collect_peer_ble_address(b, p_cb);
-  smp_calculate_f6(p_cb->mac_key, p_cb->rand, p_cb->rrand, p_cb->peer_random,
-                   iocap, a, b, p_cb->dhkey_check);
+  p_cb->dhkey_check = crypto_toolbox::f6(p_cb->mac_key, p_cb->rand, p_cb->rrand,
+                                         p_cb->peer_random, iocap, a, b);
 
   SMP_TRACE_EVENT("local DHKey check calculation is completed");
 }
@@ -1524,8 +902,6 @@
  ******************************************************************************/
 void smp_calculate_peer_dhkey_check(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) {
   uint8_t iocap[3], a[7], b[7];
-  BT_OCTET16 param_buf;
-  bool ret;
   tSMP_KEY key;
 
   SMP_TRACE_DEBUG("%s", __func__);
@@ -1534,119 +910,21 @@
 
   smp_collect_local_ble_address(a, p_cb);
   smp_collect_peer_ble_address(b, p_cb);
-  ret = smp_calculate_f6(p_cb->mac_key, p_cb->rrand, p_cb->rand,
-                         p_cb->local_random, iocap, b, a, param_buf);
+  Octet16 param_buf = crypto_toolbox::f6(p_cb->mac_key, p_cb->rrand, p_cb->rand,
+                                         p_cb->local_random, iocap, b, a);
 
-  if (ret) {
-    SMP_TRACE_EVENT("peer DHKey check calculation is completed");
+  SMP_TRACE_EVENT("peer DHKey check calculation is completed");
 #if (SMP_DEBUG == TRUE)
-    smp_debug_print_nbyte_little_endian(param_buf, "peer DHKey check",
-                                        BT_OCTET16_LEN);
+  smp_debug_print_nbyte_little_endian(param_buf, "peer DHKey check",
+                                      OCTET16_LEN);
 #endif
-    key.key_type = SMP_KEY_TYPE_PEER_DHK_CHCK;
-    key.p_data = param_buf;
-    tSMP_INT_DATA smp_int_data;
-    smp_int_data.key = key;
-    smp_sm_event(p_cb, SMP_SC_KEY_READY_EVT, &smp_int_data);
-  } else {
-    SMP_TRACE_EVENT("peer DHKey check calculation failed");
-    tSMP_INT_DATA smp_int_data;
-    smp_int_data.status = SMP_PAIR_FAIL_UNKNOWN;
-    smp_sm_event(p_cb, SMP_AUTH_CMPL_EVT, &smp_int_data);
-  }
+  key.key_type = SMP_KEY_TYPE_PEER_DHK_CHCK;
+  key.p_data = param_buf.data();
+  tSMP_INT_DATA smp_int_data;
+  smp_int_data.key = key;
+  smp_sm_event(p_cb, SMP_SC_KEY_READY_EVT, &smp_int_data);
 }
 
-/*******************************************************************************
- *
- * Function         smp_calculate_f6
- *
- * Description      The function calculates
- *                  C = f6(W, N1, N2, R, IOcap, A1, A2) =
- *                      AES-CMAC (N1||N2||R||IOcap||A1||A2)
- *                              W
- *                  where
- *                  input:  W is 128 bit,
- *                          N1 is 128 bit,
- *                          N2 is 128 bit,
- *                          R is 128 bit,
- *                          IOcap is 24 bit,
- *                          A1 is 56 bit,
- *                          A2 is 56 bit,
- *                  output: C is 128 bit.
- *
- * Returns          false if out of resources, true in other cases.
- *
- * Note             The LSB is the first octet, the MSB is the last octet of
- *                  the AES-CMAC input/output stream.
- *
- ******************************************************************************/
-bool smp_calculate_f6(uint8_t* w, uint8_t* n1, uint8_t* n2, uint8_t* r,
-                      uint8_t* iocap, uint8_t* a1, uint8_t* a2, uint8_t* c) {
-  uint8_t* p = NULL;
-  uint8_t msg_len = BT_OCTET16_LEN /* N1 size */ +
-                    BT_OCTET16_LEN /* N2 size */ + BT_OCTET16_LEN /* R size */ +
-                    3 /* IOcap size */ + 7 /* A1 size*/
-                    + 7 /* A2 size*/;
-  uint8_t msg[BT_OCTET16_LEN + BT_OCTET16_LEN + BT_OCTET16_LEN + 3 + 7 + 7];
-#if (SMP_DEBUG == TRUE)
-  uint8_t* p_print = NULL;
-#endif
-
-  SMP_TRACE_DEBUG("%s", __func__);
-#if (SMP_DEBUG == TRUE)
-  p_print = w;
-  smp_debug_print_nbyte_little_endian(p_print, "W", BT_OCTET16_LEN);
-  p_print = n1;
-  smp_debug_print_nbyte_little_endian(p_print, "N1", BT_OCTET16_LEN);
-  p_print = n2;
-  smp_debug_print_nbyte_little_endian(p_print, "N2", BT_OCTET16_LEN);
-  p_print = r;
-  smp_debug_print_nbyte_little_endian(p_print, "R", BT_OCTET16_LEN);
-  p_print = iocap;
-  smp_debug_print_nbyte_little_endian(p_print, "IOcap", 3);
-  p_print = a1;
-  smp_debug_print_nbyte_little_endian(p_print, "A1", 7);
-  p_print = a2;
-  smp_debug_print_nbyte_little_endian(p_print, "A2", 7);
-#endif
-
-  uint8_t cmac[BT_OCTET16_LEN];
-  uint8_t key[BT_OCTET16_LEN];
-
-  p = key;
-  ARRAY_TO_STREAM(p, w, BT_OCTET16_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_print = key;
-  smp_debug_print_nbyte_little_endian(p_print, "K", BT_OCTET16_LEN);
-#endif
-
-  p = msg;
-  ARRAY_TO_STREAM(p, a2, 7);
-  ARRAY_TO_STREAM(p, a1, 7);
-  ARRAY_TO_STREAM(p, iocap, 3);
-  ARRAY_TO_STREAM(p, r, BT_OCTET16_LEN);
-  ARRAY_TO_STREAM(p, n2, BT_OCTET16_LEN);
-  ARRAY_TO_STREAM(p, n1, BT_OCTET16_LEN);
-#if (SMP_DEBUG == TRUE)
-  p_print = msg;
-  smp_debug_print_nbyte_little_endian(p_print, "M", msg_len);
-#endif
-
-  bool ret = true;
-  if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    ret = false;
-  }
-
-#if (SMP_DEBUG == TRUE)
-  p_print = cmac;
-  smp_debug_print_nbyte_little_endian(p_print, "AES-CMAC", BT_OCTET16_LEN);
-#endif
-
-  p = c;
-  ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN);
-  return ret;
-}
 
 /*******************************************************************************
  *
@@ -1662,8 +940,6 @@
   tBTM_SEC_DEV_REC* p_dev_rec;
   RawAddress bda_for_lk;
   tBLE_ADDR_TYPE conn_addr_type;
-  BT_OCTET16 salt = {0x31, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
-                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
   SMP_TRACE_DEBUG("%s", __func__);
 
@@ -1686,79 +962,44 @@
     return false;
   }
 
-  BT_OCTET16 intermediate_link_key;
-  bool ret = true;
+  Octet16 link_key =
+      crypto_toolbox::ltk_to_link_key(p_cb->ltk, p_cb->key_derivation_h7_used);
 
-  if (p_cb->key_derivation_h7_used)
-    ret = smp_calculate_h7((uint8_t*)salt, p_cb->ltk, intermediate_link_key);
-  else
-    ret = smp_calculate_h6(p_cb->ltk, (uint8_t*)"1pmt" /* reversed "tmp1" */,
-                           intermediate_link_key);
-  if (!ret) {
-    SMP_TRACE_ERROR("%s failed to derive intermediate_link_key", __func__);
-    return ret;
-  }
-
-  BT_OCTET16 link_key;
-  ret = smp_calculate_h6(intermediate_link_key,
-                         (uint8_t*)"rbel" /* reversed "lebr" */, link_key);
-  if (!ret) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-  } else {
-    uint8_t link_key_type;
-    if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
-      /* Secure Connections Only Mode */
+  uint8_t link_key_type;
+  if (btm_cb.security_mode == BTM_SEC_MODE_SC) {
+    /* Secure Connections Only Mode */
+    link_key_type = BTM_LKEY_TYPE_AUTH_COMB_P_256;
+  } else if (controller_get_interface()->supports_secure_connections()) {
+    /* both transports are SC capable */
+    if (p_cb->sec_level == SMP_SEC_AUTHENTICATED)
       link_key_type = BTM_LKEY_TYPE_AUTH_COMB_P_256;
-    } else if (controller_get_interface()->supports_secure_connections()) {
-      /* both transports are SC capable */
-      if (p_cb->sec_level == SMP_SEC_AUTHENTICATED)
-        link_key_type = BTM_LKEY_TYPE_AUTH_COMB_P_256;
-      else
-        link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB_P_256;
-    } else if (btm_cb.security_mode == BTM_SEC_MODE_SP) {
-      /* BR/EDR transport is SSP capable */
-      if (p_cb->sec_level == SMP_SEC_AUTHENTICATED)
-        link_key_type = BTM_LKEY_TYPE_AUTH_COMB;
-      else
-        link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB;
-    } else {
-      SMP_TRACE_ERROR(
-          "%s failed to update link_key. Sec Mode = %d, sm4 = 0x%02x", __func__,
-          btm_cb.security_mode, p_dev_rec->sm4);
-      return false;
-    }
-
-    link_key_type += BTM_LTK_DERIVED_LKEY_OFFSET;
-
-    uint8_t* p;
-    BT_OCTET16 notif_link_key;
-    p = notif_link_key;
-    ARRAY16_TO_STREAM(p, link_key);
-
-    btm_sec_link_key_notification(bda_for_lk, notif_link_key, link_key_type);
-
-    SMP_TRACE_EVENT("%s is completed", __func__);
+    else
+      link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB_P_256;
+  } else if (btm_cb.security_mode == BTM_SEC_MODE_SP) {
+    /* BR/EDR transport is SSP capable */
+    if (p_cb->sec_level == SMP_SEC_AUTHENTICATED)
+      link_key_type = BTM_LKEY_TYPE_AUTH_COMB;
+    else
+      link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB;
+  } else {
+    SMP_TRACE_ERROR("%s failed to update link_key. Sec Mode = %d, sm4 = 0x%02x",
+                    __func__, btm_cb.security_mode, p_dev_rec->sm4);
+    return false;
   }
 
-  return ret;
+  link_key_type += BTM_LTK_DERIVED_LKEY_OFFSET;
+
+  Octet16 notif_link_key = link_key;
+  btm_sec_link_key_notification(bda_for_lk, notif_link_key, link_key_type);
+
+  SMP_TRACE_EVENT("%s is completed", __func__);
+
+  return true;
 }
 
-/*******************************************************************************
- *
- * Function         smp_calculate_long_term_key_from_link_key
- *
- * Description      The function calculates and saves SC LTK derived from BR/EDR
- *                  link key.
- *
- * Returns          false if out of resources, true in other cases.
- *
- ******************************************************************************/
+/** The function calculates and saves SC LTK derived from BR/EDR link key. */
 bool smp_calculate_long_term_key_from_link_key(tSMP_CB* p_cb) {
-  bool ret = true;
   tBTM_SEC_DEV_REC* p_dev_rec;
-  uint8_t rev_link_key[16];
-  BT_OCTET16 salt = {0x32, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00,
-                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
   SMP_TRACE_DEBUG("%s", __func__);
 
@@ -1782,153 +1023,17 @@
     return false;
   }
 
-  uint8_t* p1;
-  uint8_t* p2;
-  p1 = rev_link_key;
-  p2 = p_dev_rec->link_key;
-  REVERSE_ARRAY_TO_STREAM(p1, p2, 16);
+  Octet16 rev_link_key;
+  std::reverse_copy(p_dev_rec->link_key.begin(), p_dev_rec->link_key.end(),
+                    rev_link_key.begin());
+  p_cb->ltk = crypto_toolbox::link_key_to_ltk(rev_link_key,
+                                              p_cb->key_derivation_h7_used);
 
-  BT_OCTET16 intermediate_long_term_key;
-  if (p_cb->key_derivation_h7_used) {
-    ret = smp_calculate_h7((uint8_t*)salt, rev_link_key,
-                           intermediate_long_term_key);
-  } else {
-    /* "tmp2" obtained from the spec */
-    ret = smp_calculate_h6(rev_link_key, (uint8_t*)"2pmt" /* reversed "tmp2" */,
-                           intermediate_long_term_key);
-  }
-
-  if (!ret) {
-    SMP_TRACE_ERROR("%s failed to derive intermediate_long_term_key", __func__);
-    return ret;
-  }
-
-  /* "brle" obtained from the spec */
-  ret = smp_calculate_h6(intermediate_long_term_key,
-                         (uint8_t*)"elrb" /* reversed "brle" */, p_cb->ltk);
-
-  if (!ret) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-  } else {
-    p_cb->sec_level = (br_link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)
-                          ? SMP_SEC_AUTHENTICATED
-                          : SMP_SEC_UNAUTHENTICATE;
-    SMP_TRACE_EVENT("%s is completed", __func__);
-  }
-
-  return ret;
-}
-
-/*******************************************************************************
- *
- * Function         smp_calculate_h6
- *
- * Description      The function calculates
- *                  C = h6(W, KeyID) = AES-CMAC (KeyID)
- *                                             W
- *                  where
- *                  input:  W is 128 bit,
- *                          KeyId is 32 bit,
- *                  output: C is 128 bit.
- *
- * Returns          false if out of resources, true in other cases.
- *
- * Note             The LSB is the first octet, the MSB is the last octet of
- *                  the AES-CMAC input/output stream.
- *
- ******************************************************************************/
-bool smp_calculate_h6(uint8_t* w, uint8_t* keyid, uint8_t* c) {
-#if (SMP_DEBUG == TRUE)
-  uint8_t* p_print = NULL;
-#endif
-
-  SMP_TRACE_DEBUG("%s", __func__);
-#if (SMP_DEBUG == TRUE)
-  p_print = w;
-  smp_debug_print_nbyte_little_endian(p_print, "W", BT_OCTET16_LEN);
-  p_print = keyid;
-  smp_debug_print_nbyte_little_endian(p_print, "keyID", 4);
-#endif
-
-  uint8_t* p = NULL;
-  uint8_t key[BT_OCTET16_LEN];
-
-  p = key;
-  ARRAY_TO_STREAM(p, w, BT_OCTET16_LEN);
-
-#if (SMP_DEBUG == TRUE)
-  p_print = key;
-  smp_debug_print_nbyte_little_endian(p_print, "K", BT_OCTET16_LEN);
-#endif
-
-  uint8_t msg_len = 4 /* KeyID size */;
-  uint8_t msg[4];
-
-  p = msg;
-  ARRAY_TO_STREAM(p, keyid, 4);
-
-#if (SMP_DEBUG == TRUE)
-  p_print = msg;
-  smp_debug_print_nbyte_little_endian(p_print, "M", msg_len);
-#endif
-
-  bool ret = true;
-  uint8_t cmac[BT_OCTET16_LEN];
-  if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    ret = false;
-  }
-
-#if (SMP_DEBUG == TRUE)
-  p_print = cmac;
-  smp_debug_print_nbyte_little_endian(p_print, "AES-CMAC", BT_OCTET16_LEN);
-#endif
-
-  p = c;
-  ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN);
-  return ret;
-}
-
-/*******************************************************************************
-**
-** Function         smp_calculate_h7
-**
-** Description      The function calculates
-**                  C = h7(SALT, W) = AES-CMAC   (W)
-**                                            SALT
-**                  where
-**                  input:  W is 128 bit,
-**                          SALT is 128 bit,
-**                  output: C is 128 bit.
-**
-** Returns          FALSE if out of resources, TRUE in other cases.
-**
-** Note             The LSB is the first octet, the MSB is the last octet of
-**                  the AES-CMAC input/output stream.
-**
-*******************************************************************************/
-bool smp_calculate_h7(uint8_t* salt, uint8_t* w, uint8_t* c) {
-  SMP_TRACE_DEBUG("%s", __FUNCTION__);
-
-  uint8_t key[BT_OCTET16_LEN];
-  uint8_t* p = key;
-  ARRAY_TO_STREAM(p, salt, BT_OCTET16_LEN);
-
-  uint8_t msg_len = BT_OCTET16_LEN /* msg size */;
-  uint8_t msg[BT_OCTET16_LEN];
-  p = msg;
-  ARRAY_TO_STREAM(p, w, BT_OCTET16_LEN);
-
-  bool ret = true;
-  uint8_t cmac[BT_OCTET16_LEN];
-  if (!aes_cipher_msg_auth_code(key, msg, msg_len, BT_OCTET16_LEN, cmac)) {
-    SMP_TRACE_ERROR("%s failed", __FUNCTION__);
-    ret = false;
-  }
-
-  p = c;
-  ARRAY_TO_STREAM(p, cmac, BT_OCTET16_LEN);
-  return ret;
+  p_cb->sec_level = (br_link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)
+                        ? SMP_SEC_AUTHENTICATED
+                        : SMP_SEC_UNAUTHENTICATE;
+  SMP_TRACE_EVENT("%s is completed", __func__);
+  return true;
 }
 
 /**
@@ -1938,10 +1043,10 @@
   SMP_TRACE_DEBUG("%s", __func__);
   btsnd_hcic_ble_rand(Bind(
       [](tSMP_CB* p_cb, BT_OCTET8 rand) {
-        memcpy((void*)p_cb->rand, rand, BT_OCTET8_LEN);
+        memcpy(p_cb->rand.data(), rand, BT_OCTET8_LEN);
         btsnd_hcic_ble_rand(Bind(
             [](tSMP_CB* p_cb, BT_OCTET8 rand) {
-              memcpy((void*)&p_cb->rand[8], rand, BT_OCTET8_LEN);
+              memcpy(p_cb->rand.data() + 8, rand, BT_OCTET8_LEN);
               SMP_TRACE_DEBUG("%s round %d", __func__, p_cb->round);
               /* notifies SM that it has new nonce. */
               smp_sm_event(p_cb, SMP_HAVE_LOC_NONCE_EVT, NULL);
diff --git a/stack/smp/smp_utils.cc b/stack/smp/smp_utils.cc
index bff5645..be98ed0 100644
--- a/stack/smp/smp_utils.cc
+++ b/stack/smp/smp_utils.cc
@@ -37,20 +37,21 @@
 #include "smp_int.h"
 
 #define SMP_PAIRING_REQ_SIZE 7
-#define SMP_CONFIRM_CMD_SIZE (BT_OCTET16_LEN + 1)
-#define SMP_RAND_CMD_SIZE (BT_OCTET16_LEN + 1)
-#define SMP_INIT_CMD_SIZE (BT_OCTET16_LEN + 1)
-#define SMP_ENC_INFO_SIZE (BT_OCTET16_LEN + 1)
+#define SMP_CONFIRM_CMD_SIZE (OCTET16_LEN + 1)
+#define SMP_RAND_CMD_SIZE (OCTET16_LEN + 1)
+#define SMP_INIT_CMD_SIZE (OCTET16_LEN + 1)
+#define SMP_ENC_INFO_SIZE (OCTET16_LEN + 1)
 #define SMP_MASTER_ID_SIZE (BT_OCTET8_LEN + 2 + 1)
-#define SMP_ID_INFO_SIZE (BT_OCTET16_LEN + 1)
+#define SMP_ID_INFO_SIZE (OCTET16_LEN + 1)
 #define SMP_ID_ADDR_SIZE (BD_ADDR_LEN + 1 + 1)
-#define SMP_SIGN_INFO_SIZE (BT_OCTET16_LEN + 1)
+#define SMP_SIGN_INFO_SIZE (OCTET16_LEN + 1)
 #define SMP_PAIR_FAIL_SIZE 2
 #define SMP_SECURITY_REQUEST_SIZE 2
 #define SMP_PAIR_PUBL_KEY_SIZE (1 /* opcode */ + (2 * BT_OCTET32_LEN))
-#define SMP_PAIR_COMMITM_SIZE (1 /* opcode */ + BT_OCTET16_LEN /*Commitment*/)
+#define SMP_PAIR_COMMITM_SIZE (1 /* opcode */ + OCTET16_LEN /*Commitment*/)
 #define SMP_PAIR_DHKEY_CHECK_SIZE \
-  (1 /* opcode */ + BT_OCTET16_LEN /*DHKey Check*/)
+  (1 /* opcode */ + OCTET16_LEN /*DHKey \
+                                                                   Check*/)
 #define SMP_PAIR_KEYPR_NOTIF_SIZE (1 /* opcode */ + 1 /*Notif Type*/)
 
 /* SMP command sizes per spec */
@@ -454,7 +455,7 @@
   p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
 
   UINT8_TO_STREAM(p, SMP_OPCODE_CONFIRM);
-  ARRAY_TO_STREAM(p, p_cb->confirm, BT_OCTET16_LEN);
+  ARRAY_TO_STREAM(p, p_cb->confirm, OCTET16_LEN);
 
   p_buf->offset = L2CAP_MIN_OFFSET;
   p_buf->len = SMP_CONFIRM_CMD_SIZE;
@@ -478,7 +479,7 @@
 
   p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
   UINT8_TO_STREAM(p, SMP_OPCODE_RAND);
-  ARRAY_TO_STREAM(p, p_cb->rand, BT_OCTET16_LEN);
+  ARRAY_TO_STREAM(p, p_cb->rand, OCTET16_LEN);
 
   p_buf->offset = L2CAP_MIN_OFFSET;
   p_buf->len = SMP_RAND_CMD_SIZE;
@@ -503,7 +504,7 @@
 
   p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
   UINT8_TO_STREAM(p, SMP_OPCODE_ENCRYPT_INFO);
-  ARRAY_TO_STREAM(p, p_cb->ltk, BT_OCTET16_LEN);
+  ARRAY_TO_STREAM(p, p_cb->ltk, OCTET16_LEN);
 
   p_buf->offset = L2CAP_MIN_OFFSET;
   p_buf->len = SMP_ENC_INFO_SIZE;
@@ -547,7 +548,6 @@
 static BT_HDR* smp_build_identity_info_cmd(UNUSED_ATTR uint8_t cmd_code,
                                            UNUSED_ATTR tSMP_CB* p_cb) {
   uint8_t* p;
-  BT_OCTET16 irk;
   BT_HDR* p_buf =
       (BT_HDR*)osi_malloc(sizeof(BT_HDR) + SMP_ID_INFO_SIZE + L2CAP_MIN_OFFSET);
 
@@ -555,10 +555,10 @@
 
   p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
 
-  BTM_GetDeviceIDRoot(irk);
+  const Octet16& irk = BTM_GetDeviceIDRoot();
 
   UINT8_TO_STREAM(p, SMP_OPCODE_IDENTITY_INFO);
-  ARRAY_TO_STREAM(p, irk, BT_OCTET16_LEN);
+  ARRAY_TO_STREAM(p, irk.data(), OCTET16_LEN);
 
   p_buf->offset = L2CAP_MIN_OFFSET;
   p_buf->len = SMP_ID_INFO_SIZE;
@@ -609,7 +609,7 @@
 
   p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
   UINT8_TO_STREAM(p, SMP_OPCODE_SIGN_INFO);
-  ARRAY_TO_STREAM(p, p_cb->csrk, BT_OCTET16_LEN);
+  ARRAY_TO_STREAM(p, p_cb->csrk, OCTET16_LEN);
 
   p_buf->offset = L2CAP_MIN_OFFSET;
   p_buf->len = SMP_SIGN_INFO_SIZE;
@@ -716,7 +716,7 @@
 
   p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
   UINT8_TO_STREAM(p, SMP_OPCODE_CONFIRM);
-  ARRAY_TO_STREAM(p, p_cb->commitment, BT_OCTET16_LEN);
+  ARRAY_TO_STREAM(p, p_cb->commitment, OCTET16_LEN);
 
   p_buf->offset = L2CAP_MIN_OFFSET;
   p_buf->len = SMP_PAIR_COMMITM_SIZE;
@@ -741,7 +741,7 @@
 
   p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET;
   UINT8_TO_STREAM(p, SMP_OPCODE_PAIR_DHKEY_CHECK);
-  ARRAY_TO_STREAM(p, p_cb->dhkey_check, BT_OCTET16_LEN);
+  ARRAY_TO_STREAM(p, p_cb->dhkey_check, OCTET16_LEN);
 
   p_buf->offset = L2CAP_MIN_OFFSET;
   p_buf->len = SMP_PAIR_DHKEY_CHECK_SIZE;
@@ -774,66 +774,42 @@
   return p_buf;
 }
 
-/*******************************************************************************
- *
- * Function         smp_convert_string_to_tk
- *
- * Description      This function is called to convert a 6 to 16 digits numeric
- *                  character string into SMP TK.
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-void smp_convert_string_to_tk(BT_OCTET16 tk, uint32_t passkey) {
-  uint8_t* p = tk;
+/** This function is called to convert a 6 to 16 digits numeric character string
+ * into SMP TK. */
+void smp_convert_string_to_tk(Octet16* tk, uint32_t passkey) {
+  uint8_t* p = tk->data();
   tSMP_KEY key;
   SMP_TRACE_EVENT("smp_convert_string_to_tk");
   UINT32_TO_STREAM(p, passkey);
 
   key.key_type = SMP_KEY_TYPE_TK;
-  key.p_data = tk;
+  key.p_data = tk->data();
 
   tSMP_INT_DATA smp_int_data;
   smp_int_data.key = key;
   smp_sm_event(&smp_cb, SMP_KEY_READY_EVT, &smp_int_data);
 }
 
-/*******************************************************************************
- *
- * Function         smp_mask_enc_key
- *
- * Description      This function is called to mask off the encryption key based
- *                  on the maximum encryption key size.
- *
- *
- * Returns          void
- *
- ******************************************************************************/
-void smp_mask_enc_key(uint8_t loc_enc_size, uint8_t* p_data) {
+/** This function is called to mask off the encryption key based on the maximum
+ * encryption key size. */
+void smp_mask_enc_key(uint8_t loc_enc_size, Octet16* p_data) {
   SMP_TRACE_EVENT("smp_mask_enc_key");
-  if (loc_enc_size < BT_OCTET16_LEN) {
-    for (; loc_enc_size < BT_OCTET16_LEN; loc_enc_size++)
-      *(p_data + loc_enc_size) = 0;
+  if (loc_enc_size < OCTET16_LEN) {
+    for (; loc_enc_size < OCTET16_LEN; loc_enc_size++)
+      (*p_data)[loc_enc_size] = 0;
   }
   return;
 }
 
-/*******************************************************************************
- *
- * Function         smp_xor_128
- *
- * Description      utility function to do an biteise exclusive-OR of two bit
- *                  strings of the length of BT_OCTET16_LEN.
- *
- * Returns          void
- *
- ******************************************************************************/
-void smp_xor_128(BT_OCTET16 a, BT_OCTET16 b) {
-  uint8_t i, *aa = a, *bb = b;
+/** utility function to do an biteise exclusive-OR of two bit strings of the
+ * length of OCTET16_LEN. Result is stored in first argument.
+ */
+void smp_xor_128(Octet16* a, const Octet16& b) {
+  CHECK(a);
+  uint8_t i, *aa = a->data();
+  const uint8_t* bb = b.data();
 
-  SMP_TRACE_EVENT("smp_xor_128");
-  for (i = 0; i < BT_OCTET16_LEN; i++) {
+  for (i = 0; i < OCTET16_LEN; i++) {
     aa[i] = aa[i] ^ bb[i];
   }
 }
@@ -1409,15 +1385,14 @@
  *
  ******************************************************************************/
 bool smp_check_commitment(tSMP_CB* p_cb) {
-  BT_OCTET16 expected;
 
   SMP_TRACE_DEBUG("%s", __func__);
 
-  smp_calculate_peer_commitment(p_cb, expected);
+  Octet16 expected = smp_calculate_peer_commitment(p_cb);
   print128(expected, (const uint8_t*)"calculated peer commitment");
   print128(p_cb->remote_commitment, (const uint8_t*)"received peer commitment");
 
-  if (memcmp(p_cb->remote_commitment, expected, BT_OCTET16_LEN)) {
+  if (memcmp(p_cb->remote_commitment.data(), expected.data(), OCTET16_LEN)) {
     SMP_TRACE_WARNING("%s: Commitment check fails", __func__);
     return false;
   }
@@ -1441,7 +1416,7 @@
   tBTM_LE_PENC_KEYS ple_key;
 
   SMP_TRACE_DEBUG("%s-Save LTK as local LTK key", __func__);
-  memcpy(lle_key.ltk, p_cb->ltk, BT_OCTET16_LEN);
+  lle_key.ltk = p_cb->ltk;
   lle_key.div = 0;
   lle_key.key_size = p_cb->loc_enc_size;
   lle_key.sec_level = p_cb->sec_level;
@@ -1451,53 +1426,39 @@
   SMP_TRACE_DEBUG("%s-Save LTK as peer LTK key", __func__);
   ple_key.ediv = 0;
   memset(ple_key.rand, 0, BT_OCTET8_LEN);
-  memcpy(ple_key.ltk, p_cb->ltk, BT_OCTET16_LEN);
+  ple_key.ltk = p_cb->ltk;
   ple_key.sec_level = p_cb->sec_level;
   ple_key.key_size = p_cb->loc_enc_size;
   btm_sec_save_le_key(p_cb->pairing_bda, BTM_LE_KEY_PENC,
                       (tBTM_LE_KEY_VALUE*)&ple_key, true);
 }
 
-/*******************************************************************************
- *
- * Function         smp_calculate_f5_mackey_and_long_term_key
- *
- * Description      The function calculates MacKey and LTK and saves them in CB.
- *                  To calculate MacKey and LTK it calls smp_calc_f5(...).
- *                  MacKey is used in dhkey calculation, LTK is used to encrypt
- *                  the link.
- *
- * Returns          false if out of resources, true otherwise.
- *
- ******************************************************************************/
-bool smp_calculate_f5_mackey_and_long_term_key(tSMP_CB* p_cb) {
+/** The function calculates MacKey and LTK and saves them in CB. To calculate
+ * MacKey and LTK it calls smp_calc_f5(...). MacKey is used in dhkey
+ * calculation, LTK is used to encrypt the link. */
+void smp_calculate_f5_mackey_and_long_term_key(tSMP_CB* p_cb) {
   uint8_t a[7];
   uint8_t b[7];
-  uint8_t* p_na;
-  uint8_t* p_nb;
+  Octet16 na;
+  Octet16 nb;
 
   SMP_TRACE_DEBUG("%s", __func__);
 
   if (p_cb->role == HCI_ROLE_MASTER) {
     smp_collect_local_ble_address(a, p_cb);
     smp_collect_peer_ble_address(b, p_cb);
-    p_na = p_cb->rand;
-    p_nb = p_cb->rrand;
+    na = p_cb->rand;
+    nb = p_cb->rrand;
   } else {
     smp_collect_local_ble_address(b, p_cb);
     smp_collect_peer_ble_address(a, p_cb);
-    p_na = p_cb->rrand;
-    p_nb = p_cb->rand;
+    na = p_cb->rrand;
+    nb = p_cb->rand;
   }
 
-  if (!smp_calculate_f5(p_cb->dhkey, p_na, p_nb, a, b, p_cb->mac_key,
-                        p_cb->ltk)) {
-    SMP_TRACE_ERROR("%s failed", __func__);
-    return false;
-  }
+  crypto_toolbox::f5(p_cb->dhkey, na, nb, a, b, &p_cb->mac_key, &p_cb->ltk);
 
   SMP_TRACE_EVENT("%s is completed", __func__);
-  return true;
 }
 
 /*******************************************************************************
@@ -1538,3 +1499,15 @@
 
   return true;
 }
+
+void print128(const Octet16& x, const uint8_t* key_name) {
+  if (VLOG_IS_ON(2) && DLOG_IS_ON(INFO)) {
+    uint8_t* p = (uint8_t*)x.data();
+
+    DVLOG(2) << key_name << " (MSB ~ LSB) = ";
+    for (int i = 0; i < 4; i++) {
+      DVLOG(2) << +p[OCTET16_LEN - i * 4 - 1] << +p[OCTET16_LEN - i * 4 - 2]
+               << +p[OCTET16_LEN - i * 4 - 3] << +p[OCTET16_LEN - i * 4 - 4];
+    }
+  }
+}
diff --git a/stack/test/ble_advertiser_test.cc b/stack/test/ble_advertiser_test.cc
index 65a30d5..6ca0ad4 100644
--- a/stack/test/ble_advertiser_test.cc
+++ b/stack/test/ble_advertiser_test.cc
@@ -47,19 +47,11 @@
 uint16_t BTM_ReadDiscoverability(uint16_t* p_window, uint16_t* p_interval) {
   return true;
 }
-bool SMP_Encrypt(uint8_t* key, uint8_t key_len, uint8_t* plain_text,
-                 uint8_t pt_len, tSMP_ENC* p_out) {
-  return true;
-}
-void BTM_GetDeviceIDRoot(BT_OCTET16 irk) {}
-void btm_ble_update_dmt_flag_bits(uint8_t* flag_value,
-                                  const uint16_t connect_mode,
-                                  const uint16_t disc_mode) {}
 void btm_acl_update_conn_addr(uint16_t conn_handle, const RawAddress& address) {
 }
-void btm_gen_resolvable_private_addr(base::Callback<void(uint8_t[8])> cb) {
-  uint8_t fake_rand[8] = {0, 0, 0, 0, 0, 0, 0, 0};
-  cb.Run(fake_rand);
+void btm_gen_resolvable_private_addr(
+    base::Callback<void(const RawAddress& rpa)> cb) {
+  cb.Run(RawAddress::kEmpty);
 }
 
 alarm_callback_t last_alarm_cb = nullptr;
diff --git a/stack/test/crypto_toolbox_test.cc b/stack/test/crypto_toolbox_test.cc
new file mode 100644
index 0000000..727668d
--- /dev/null
+++ b/stack/test/crypto_toolbox_test.cc
@@ -0,0 +1,400 @@
+/******************************************************************************
+ *
+ *  Copyright 2018 The Android Open Source Project
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at:
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ ******************************************************************************/
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "stack/crypto_toolbox/aes.h"
+#include "stack/crypto_toolbox/crypto_toolbox.h"
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <vector>
+
+using ::testing::ElementsAreArray;
+
+namespace crypto_toolbox {
+
+// BT Spec 5.0 | Vol 3, Part H D.1
+TEST(CryptoToolboxTest, bt_spec_test_d_1_test) {
+  uint8_t k[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+                 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  uint8_t m[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+  uint8_t aes_cmac_k_m[] = {0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3,
+                            0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f};
+
+  uint8_t output[16];
+  aes_context ctx;
+  aes_set_key(k, sizeof(k), &ctx);
+  aes_encrypt(m, output, &ctx); /* outputs in byte 48 to byte 63 */
+
+  EXPECT_THAT(output, ElementsAreArray(aes_cmac_k_m, OCTET16_LEN));
+
+  // useful for debugging
+  // LOG(INFO) << "k " << base::HexEncode(k, OCTET16_LEN);
+  // LOG(INFO) << "m " << base::HexEncode(m, sizeof(m));
+  // LOG(INFO) << "output " << base::HexEncode(output, OCTET16_LEN);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.1
+TEST(CryptoToolboxTest, bt_spec_example_d_1_1_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+            0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  Octet16 aes_cmac_k_m{0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
+                       0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, nullptr /* empty message */, 0);
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+
+  // useful for debugging
+  // LOG(INFO) << "k " << base::HexEncode(k.data(), k.size());
+  // LOG(INFO) << "aes_cmac(k,nullptr) "
+  //           << base::HexEncode(output.data(), output.size());
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.2
+TEST(CryptoToolboxTest, bt_spec_example_d_1_2_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+            0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  Octet16 m = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
+               0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a};
+
+  Octet16 aes_cmac_k_m{0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
+                       0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(m), std::end(m));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, m);
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+
+  // useful for debugging
+  // LOG(INFO) << "k " << base::HexEncode(k.data(), k.size());
+  // LOG(INFO) << "m " << base::HexEncode(m, sizeof(m));
+  // LOG(INFO) << "aes_cmac(k,m) "
+  //           << base::HexEncode(output.data(), output.size());
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.3
+TEST(CryptoToolboxTest, bt_spec_example_d_1_3_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+            0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  uint8_t m[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
+                 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57,
+                 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf,
+                 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11};
+
+  Octet16 aes_cmac_k_m{0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
+                       0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(m), std::end(m));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, m, sizeof(m));
+  EXPECT_EQ(output, aes_cmac_k_m);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.1.4
+TEST(CryptoToolboxTest, bt_spec_example_d_1_4_test) {
+  Octet16 k{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+            0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
+
+  uint8_t m[] = {0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
+                 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57,
+                 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf,
+                 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
+                 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f,
+                 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
+                 0xe6, 0x6c, 0x37, 0x10};
+
+  Octet16 aes_cmac_k_m{0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
+                       0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(k), std::end(k));
+  std::reverse(std::begin(m), std::end(m));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = aes_cmac(k, m, sizeof(m));
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.2
+TEST(CryptoToolboxTest, bt_spec_example_d_2_test) {
+  std::vector<uint8_t> u{0x20, 0xb0, 0x03, 0xd2, 0xf2, 0x97, 0xbe, 0x2c,
+                         0x5e, 0x2c, 0x83, 0xa7, 0xe9, 0xf9, 0xa5, 0xb9,
+                         0xef, 0xf4, 0x91, 0x11, 0xac, 0xf4, 0xfd, 0xdb,
+                         0xcc, 0x03, 0x01, 0x48, 0x0e, 0x35, 0x9d, 0xe6};
+  std::vector<uint8_t> v{0x55, 0x18, 0x8b, 0x3d, 0x32, 0xf6, 0xbb, 0x9a,
+                         0x90, 0x0a, 0xfc, 0xfb, 0xee, 0xd4, 0xe7, 0x2a,
+                         0x59, 0xcb, 0x9a, 0xc2, 0xf1, 0x9d, 0x7c, 0xfb,
+                         0x6b, 0x4f, 0xdd, 0x49, 0xf4, 0x7f, 0xc5, 0xfd};
+  Octet16 x{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e,
+            0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  uint8_t z = 0x00;
+
+  Octet16 aes_cmac_k_m{0xf2, 0xc9, 0x16, 0xf1, 0x07, 0xa9, 0xbd, 0x1c,
+                       0xf1, 0xed, 0xa1, 0xbe, 0xa9, 0x74, 0x87, 0x2d};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(u), std::end(u));
+  std::reverse(std::begin(v), std::end(v));
+  std::reverse(std::begin(x), std::end(x));
+  std::reverse(std::begin(aes_cmac_k_m), std::end(aes_cmac_k_m));
+
+  Octet16 output = f4(u.data(), v.data(), x, z);
+
+  EXPECT_EQ(output, aes_cmac_k_m);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.3
+TEST(CryptoToolboxTest, bt_spec_example_d_3_test) {
+  std::array<uint8_t, 32> dhkey_w{
+      0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05, 0x34, 0x10, 0x10,
+      0xa6, 0x0a, 0x39, 0x7d, 0x9b, 0x99, 0x79, 0x6b, 0x13, 0xb4, 0xf8,
+      0x66, 0xf1, 0x86, 0x8d, 0x34, 0xf3, 0x73, 0xbf, 0xa6, 0x98};
+  Octet16 n1{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e,
+             0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  Octet16 n2{0xa6, 0xe8, 0xe7, 0xcc, 0x25, 0xa7, 0x5f, 0x6e,
+             0x21, 0x65, 0x83, 0xf7, 0xff, 0x3d, 0xc4, 0xcf};
+  std::array<uint8_t, 7> a1{0x00, 0x56, 0x12, 0x37, 0x37, 0xbf, 0xce};
+  std::array<uint8_t, 7> a2{0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1};
+
+  Octet16 expected_ltk{0x69, 0x86, 0x79, 0x11, 0x69, 0xd7, 0xcd, 0x23,
+                       0x98, 0x05, 0x22, 0xb5, 0x94, 0x75, 0x0a, 0x38};
+  Octet16 expected_mac_key{0x29, 0x65, 0xf1, 0x76, 0xa1, 0x08, 0x4a, 0x02,
+                           0xfd, 0x3f, 0x6a, 0x20, 0xce, 0x63, 0x6e, 0x20};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(dhkey_w), std::end(dhkey_w));
+  std::reverse(std::begin(n1), std::end(n1));
+  std::reverse(std::begin(n2), std::end(n2));
+  std::reverse(std::begin(a1), std::end(a1));
+  std::reverse(std::begin(a2), std::end(a2));
+  std::reverse(std::begin(expected_ltk), std::end(expected_ltk));
+  std::reverse(std::begin(expected_mac_key), std::end(expected_mac_key));
+
+  Octet16 mac_key, ltk;
+  f5(dhkey_w.data(), n1, n2, a1.data(), a2.data(), &mac_key, &ltk);
+
+  EXPECT_EQ(mac_key, expected_mac_key);
+  EXPECT_EQ(ltk, expected_ltk);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.4
+TEST(CryptoToolboxTest, bt_spec_example_d_4_test) {
+  Octet16 n1{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e,
+             0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  Octet16 n2{0xa6, 0xe8, 0xe7, 0xcc, 0x25, 0xa7, 0x5f, 0x6e,
+             0x21, 0x65, 0x83, 0xf7, 0xff, 0x3d, 0xc4, 0xcf};
+  Octet16 r{0x12, 0xa3, 0x34, 0x3b, 0xb4, 0x53, 0xbb, 0x54,
+            0x08, 0xda, 0x42, 0xd2, 0x0c, 0x2d, 0x0f, 0xc8};
+  std::vector<uint8_t> IOcap{0x01, 0x01, 0x02};
+  std::vector<uint8_t> a1{0x00, 0x56, 0x12, 0x37, 0x37, 0xbf, 0xce};
+  std::vector<uint8_t> a2{0x00, 0xa7, 0x13, 0x70, 0x2d, 0xcf, 0xc1};
+
+  Octet16 MacKey{0x29, 0x65, 0xf1, 0x76, 0xa1, 0x08, 0x4a, 0x02,
+                 0xfd, 0x3f, 0x6a, 0x20, 0xce, 0x63, 0x6e, 0x20};
+
+  Octet16 expected_aes_cmac{0xe3, 0xc4, 0x73, 0x98, 0x9c, 0xd0, 0xe8, 0xc5,
+                            0xd2, 0x6c, 0x0b, 0x09, 0xda, 0x95, 0x8f, 0x61};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(n1), std::end(n1));
+  std::reverse(std::begin(n2), std::end(n2));
+  std::reverse(std::begin(r), std::end(r));
+  std::reverse(std::begin(IOcap), std::end(IOcap));
+  std::reverse(std::begin(a1), std::end(a1));
+  std::reverse(std::begin(a2), std::end(a2));
+  std::reverse(std::begin(MacKey), std::end(MacKey));
+  std::reverse(std::begin(expected_aes_cmac), std::end(expected_aes_cmac));
+
+  Octet16 aes_cmac = f6(MacKey, n1, n2, r, IOcap.data(), a1.data(), a2.data());
+
+  EXPECT_EQ(aes_cmac, expected_aes_cmac);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.5
+TEST(CryptoToolboxTest, bt_spec_example_d_5_test) {
+  std::array<uint8_t, 32> u{0x20, 0xb0, 0x03, 0xd2, 0xf2, 0x97, 0xbe, 0x2c,
+                            0x5e, 0x2c, 0x83, 0xa7, 0xe9, 0xf9, 0xa5, 0xb9,
+                            0xef, 0xf4, 0x91, 0x11, 0xac, 0xf4, 0xfd, 0xdb,
+                            0xcc, 0x03, 0x01, 0x48, 0x0e, 0x35, 0x9d, 0xe6};
+  std::array<uint8_t, 32> v{0x55, 0x18, 0x8b, 0x3d, 0x32, 0xf6, 0xbb, 0x9a,
+                            0x90, 0x0a, 0xfc, 0xfb, 0xee, 0xd4, 0xe7, 0x2a,
+                            0x59, 0xcb, 0x9a, 0xc2, 0xf1, 0x9d, 0x7c, 0xfb,
+                            0x6b, 0x4f, 0xdd, 0x49, 0xf4, 0x7f, 0xc5, 0xfd};
+
+  Octet16 x{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77, 0x73, 0x3e,
+            0xff, 0xff, 0xb2, 0xec, 0x71, 0x2b, 0xae, 0xab};
+  Octet16 y{0xa6, 0xe8, 0xe7, 0xcc, 0x25, 0xa7, 0x5f, 0x6e,
+            0x21, 0x65, 0x83, 0xf7, 0xff, 0x3d, 0xc4, 0xcf};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(u), std::end(u));
+  std::reverse(std::begin(v), std::end(v));
+  std::reverse(std::begin(x), std::end(x));
+  std::reverse(std::begin(y), std::end(y));
+
+  uint32_t val = g2(u.data(), v.data(), x, y);
+
+  /* the returned value is already mod 1000000, so do mod on the test result
+   * value too */
+  EXPECT_EQ(val, 0x2f9ed5baU % 1000000);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.6
+TEST(CryptoToolboxTest, bt_spec_example_d_6_test) {
+  Octet16 key{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05,
+              0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  std::array<uint8_t, 4> keyID{0x6c, 0x65, 0x62, 0x72};
+  Octet16 expected_aes_cmac{0x2d, 0x9a, 0xe1, 0x02, 0xe7, 0x6d, 0xc9, 0x1c,
+                            0xe8, 0xd3, 0xa9, 0xe2, 0x80, 0xb1, 0x63, 0x99};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(key), std::end(key));
+  std::reverse(std::begin(keyID), std::end(keyID));
+  std::reverse(std::begin(expected_aes_cmac), std::end(expected_aes_cmac));
+
+  Octet16 aes_cmac = h6(key, keyID);
+  EXPECT_EQ(aes_cmac, expected_aes_cmac);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.7
+TEST(CryptoToolboxTest, bt_spec_example_d_7_test) {
+  Octet16 IRK{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05,
+              0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  Octet16 prand{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x81, 0x94};
+  Octet16 expected_aes_128{0x15, 0x9d, 0x5f, 0xb7, 0x2e, 0xbe, 0x23, 0x11,
+                           0xa4, 0x8c, 0x1b, 0xdc, 0xc4, 0x0d, 0xfb, 0xaa};
+  std::array<uint8_t, 3> expected_ah{0x0d, 0xfb, 0xaa};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(IRK), std::end(IRK));
+  std::reverse(std::begin(prand), std::end(prand));
+  std::reverse(std::begin(expected_aes_128), std::end(expected_aes_128));
+  std::reverse(std::begin(expected_ah), std::end(expected_ah));
+
+  Octet16 result = aes_128(IRK, prand.data(), 3);
+  EXPECT_EQ(expected_aes_128, result);
+
+  // little/big endian 24 bits
+  EXPECT_EQ(result[0], expected_ah[0]);
+  EXPECT_EQ(result[1], expected_ah[1]);
+  EXPECT_EQ(result[2], expected_ah[2]);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.8
+TEST(CryptoToolboxTest, bt_spec_example_d_8_test) {
+  Octet16 Key{0xec, 0x02, 0x34, 0xa3, 0x57, 0xc8, 0xad, 0x05,
+              0x34, 0x10, 0x10, 0xa6, 0x0a, 0x39, 0x7d, 0x9b};
+  Octet16 SALT{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x74, 0x6D, 0x70, 0x31};
+  Octet16 expected_aes_cmac{0xfb, 0x17, 0x35, 0x97, 0xc6, 0xa3, 0xc0, 0xec,
+                            0xd2, 0x99, 0x8c, 0x2a, 0x75, 0xa5, 0x70, 0x11};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(Key), std::end(Key));
+  std::reverse(std::begin(SALT), std::end(SALT));
+  std::reverse(std::begin(expected_aes_cmac), std::end(expected_aes_cmac));
+
+  Octet16 aes_cmac = h7(SALT, Key);
+  EXPECT_EQ(expected_aes_cmac, aes_cmac);
+}
+
+extern Octet16 smp_calculate_ltk_to_link_key(const Octet16& ltk, bool use_h7);
+
+// BT Spec 5.0 | Vol 3, Part H D.9
+TEST(CryptoToolboxTest, bt_spec_example_d_9_test) {
+  Octet16 LTK{0x36, 0x8d, 0xf9, 0xbc, 0xe3, 0x26, 0x4b, 0x58,
+              0xbd, 0x06, 0x6c, 0x33, 0x33, 0x4f, 0xbf, 0x64};
+  Octet16 expected_link_key{0x28, 0x7a, 0xd3, 0x79, 0xdc, 0xa4, 0x02, 0x53,
+                            0x0a, 0x39, 0xf1, 0xf4, 0x30, 0x47, 0xb8, 0x35};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(LTK), std::end(LTK));
+  std::reverse(std::begin(expected_link_key), std::end(expected_link_key));
+
+  Octet16 link_key = ltk_to_link_key(LTK, true);
+  EXPECT_EQ(expected_link_key, link_key);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.10
+TEST(CryptoToolboxTest, bt_spec_example_d_10_test) {
+  Octet16 LTK{0x36, 0x8d, 0xf9, 0xbc, 0xe3, 0x26, 0x4b, 0x58,
+              0xbd, 0x06, 0x6c, 0x33, 0x33, 0x4f, 0xbf, 0x64};
+  Octet16 expected_link_key{0xbc, 0x1c, 0xa4, 0xef, 0x63, 0x3f, 0xc1, 0xbd,
+                            0x0d, 0x82, 0x30, 0xaf, 0xee, 0x38, 0x8f, 0xb0};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(LTK), std::end(LTK));
+  std::reverse(std::begin(expected_link_key), std::end(expected_link_key));
+
+  Octet16 link_key = ltk_to_link_key(LTK, false);
+  EXPECT_EQ(expected_link_key, link_key);
+}
+
+// // BT Spec 5.0 | Vol 3, Part H D.11
+TEST(CryptoToolboxTest, bt_spec_example_d_11_test) {
+  Octet16 link_key{0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x09, 0x08,
+                   0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+  Octet16 expected_ltk{0xe8, 0x5e, 0x09, 0xeb, 0x5e, 0xcc, 0xb3, 0xe2,
+                       0x69, 0x41, 0x8a, 0x13, 0x32, 0x11, 0xbc, 0x79};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(link_key), std::end(link_key));
+  std::reverse(std::begin(expected_ltk), std::end(expected_ltk));
+
+  Octet16 ltk = link_key_to_ltk(link_key, true);
+  EXPECT_EQ(expected_ltk, ltk);
+}
+
+// BT Spec 5.0 | Vol 3, Part H D.12
+TEST(CryptoToolboxTest, bt_spec_example_d_12_test) {
+  Octet16 link_key{0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x09, 0x08,
+                   0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00};
+  Octet16 expected_ltk{0xa8, 0x13, 0xfb, 0x72, 0xf1, 0xa3, 0xdf, 0xa1,
+                       0x8a, 0x2c, 0x9a, 0x43, 0xf1, 0x0d, 0x0a, 0x30};
+
+  // algorithm expect all input to be in little endian format, so reverse
+  std::reverse(std::begin(link_key), std::end(link_key));
+  std::reverse(std::begin(expected_ltk), std::end(expected_ltk));
+
+  Octet16 ltk = link_key_to_ltk(link_key, false);
+  EXPECT_EQ(expected_ltk, ltk);
+}
+
+}  // namespace crypto_toolbox
diff --git a/stack/test/stack_smp_test.cc b/stack/test/stack_smp_test.cc
index 9070764..2427814 100644
--- a/stack/test/stack_smp_test.cc
+++ b/stack/test/stack_smp_test.cc
@@ -80,70 +80,58 @@
   va_end(args);
 }
 
-extern void smp_gen_p1_4_confirm(tSMP_CB* p_cb,
-                                 tBLE_ADDR_TYPE remote_bd_addr_type,
-                                 BT_OCTET16 p1);
+extern Octet16 smp_gen_p1_4_confirm(tSMP_CB* p_cb,
+                                    tBLE_ADDR_TYPE remote_bd_addr_type);
 
-extern void smp_gen_p2_4_confirm(tSMP_CB* p_cb, const RawAddress& remote_bda,
-                                 BT_OCTET16 p2);
+extern Octet16 smp_gen_p2_4_confirm(tSMP_CB* p_cb,
+                                    const RawAddress& remote_bda);
 
-extern tSMP_STATUS smp_calculate_comfirm(tSMP_CB* p_cb, BT_OCTET16 rand,
-                                         tSMP_ENC* output);
+extern tSMP_STATUS smp_calculate_comfirm(tSMP_CB* p_cb, const Octet16& rand,
+                                         Octet16* output);
 
 namespace testing {
 
-void dump_uint128(BT_OCTET16 a, char* buffer) {
-  for (unsigned int i = 0; i < sizeof(BT_OCTET16); ++i) {
+void dump_uint128(const Octet16& a, char* buffer) {
+  for (unsigned int i = 0; i < OCTET16_LEN; ++i) {
     snprintf(buffer, 3, "%02x", a[i]);
     buffer += 2;
   }
   *buffer = '\0';
 }
 
-void dump_uint128_reverse(BT_OCTET16 a, char* buffer) {
-  for (int i = (int)(sizeof(BT_OCTET16) - 1); i >= 0; --i) {
+void dump_uint128_reverse(const Octet16& a, char* buffer) {
+  for (int i = (int)(OCTET16_LEN - 1); i >= 0; --i) {
     snprintf(buffer, 3, "%02x", a[i]);
     buffer += 2;
   }
   *buffer = '\0';
 }
 
-void print_uint128(BT_OCTET16 a) {
-  for (unsigned int i = 0; i < sizeof(BT_OCTET16); ++i) {
+void print_uint128(const Octet16& a) {
+  for (unsigned int i = 0; i < OCTET16_LEN; ++i) {
     printf("%02x", a[i]);
   }
   printf("\n");
 }
 
-void parse_uint128(const char* input, BT_OCTET16 output) {
-  memset(output, 0, sizeof(BT_OCTET16));
-  for (unsigned int count = 0; count < sizeof(BT_OCTET16); count++) {
+Octet16 parse_uint128(const char* input) {
+  Octet16 output{0};
+  for (unsigned int count = 0; count < OCTET16_LEN; count++) {
     sscanf(input, "%2hhx", &output[count]);
     input += 2;
   }
-}
-
-void reverse_array_inplace(BT_OCTET16 a) {
-  uint8_t tmp;
-  uint8_t* a_end = a + sizeof(BT_OCTET16) - 1;
-  while (a_end > a) {
-    tmp = *a_end;
-    *a_end = *a;
-    *a = tmp;
-    ++a;
-    --a_end;
-  }
+  return output;
 }
 
 class SmpCalculateConfirmTest : public Test {
  protected:
   tSMP_CB p_cb_;
   // Set random to 0x5783D52156AD6F0E6388274EC6702EE0
-  BT_OCTET16 rand_ = {0x57, 0x83, 0xD5, 0x21, 0x56, 0xAD, 0x6F, 0x0E,
-                      0x63, 0x88, 0x27, 0x4E, 0xC6, 0x70, 0x2E, 0xE0};
+  Octet16 rand_{0x57, 0x83, 0xD5, 0x21, 0x56, 0xAD, 0x6F, 0x0E,
+                0x63, 0x88, 0x27, 0x4E, 0xC6, 0x70, 0x2E, 0xE0};
 
   void SetUp() {
-    memset(p_cb_.tk, 0, sizeof(p_cb_.tk));
+    p_cb_.tk = {0};
     // Set pairing request packet to 0x070710000001(01)
     p_cb_.local_io_capability = 0x01;
     p_cb_.loc_oob_flag = 0x00;
@@ -160,7 +148,7 @@
     p_cb_.peer_r_key = 0x05;
     // Set role to master
     p_cb_.role = HCI_ROLE_MASTER;
-    reverse_array_inplace(rand_);
+    std::reverse(rand_.begin(), rand_.end());
   }
   void TearDown() {}
 
@@ -169,59 +157,54 @@
 
 // Test smp_gen_p2_4_confirm function implementation
 TEST_F(SmpCalculateConfirmTest, test_smp_gen_p2_4_confirm_as_master) {
-  BT_OCTET16 p2;
   RawAddress remote_bda;
   tBLE_ADDR_TYPE remote_bd_addr_type = 0;
   BTM_ReadRemoteConnectionAddr(p_cb_.pairing_bda, remote_bda,
                                &remote_bd_addr_type);
   BTM_ReadConnectionAddr(p_cb_.pairing_bda, p_cb_.local_bda, &p_cb_.addr_type);
-  smp_gen_p2_4_confirm(&p_cb_, remote_bda, p2);
+  Octet16 p2 = smp_gen_p2_4_confirm(&p_cb_, remote_bda);
   // Correct p2 is 0x00000000a1a2a3a4a5a6b1b2b3b4b5b6
   const char expected_p2_str[] = "00000000a1a2a3a4a5a6b1b2b3b4b5b6";
-  char p2_str[2 * sizeof(BT_OCTET16) + 1];
+  char p2_str[2 * OCTET16_LEN + 1];
   dump_uint128_reverse(p2, p2_str);
   ASSERT_THAT(p2_str, StrEq(expected_p2_str));
 }
 
-// Test smp_gen_p1_4_confirm and SMP_Encrypt function implementation
-TEST_F(SmpCalculateConfirmTest, test_SMP_Encrypt_as_master) {
-  BT_OCTET16 p1;
+// Test smp_gen_p1_4_confirm and aes_128 function implementation
+TEST_F(SmpCalculateConfirmTest, test_aes_128_as_master) {
   RawAddress remote_bda;
   tBLE_ADDR_TYPE remote_bd_addr_type = 0;
   BTM_ReadRemoteConnectionAddr(p_cb_.pairing_bda, remote_bda,
                                &remote_bd_addr_type);
   BTM_ReadConnectionAddr(p_cb_.pairing_bda, p_cb_.local_bda, &p_cb_.addr_type);
-  smp_gen_p1_4_confirm(&p_cb_, remote_bd_addr_type, p1);
+  Octet16 p1 = smp_gen_p1_4_confirm(&p_cb_, remote_bd_addr_type);
   // Correct p1 is 0x05000800000302070710000001010001
   const char expected_p1_str[] = "05000800000302070710000001010001";
-  char p1_str[2 * sizeof(BT_OCTET16) + 1];
+  char p1_str[2 * OCTET16_LEN + 1];
   dump_uint128_reverse(p1, p1_str);
   ASSERT_THAT(p1_str, StrEq(expected_p1_str));
-  smp_xor_128(p1, rand_);
+  smp_xor_128(&p1, rand_);
   // Correct p1 xor r is 0x5283dd2156ae6d096498274ec7712ee1
   const char expected_p1_xor_r_str[] = "5283dd2156ae6d096498274ec7712ee1";
-  char p1_xor_r_str[2 * sizeof(BT_OCTET16) + 1];
+  char p1_xor_r_str[2 * OCTET16_LEN + 1];
   dump_uint128_reverse(p1, p1_xor_r_str);
   ASSERT_THAT(p1_xor_r_str, StrEq(expected_p1_xor_r_str));
-  tSMP_ENC output;
-  memset(&output, 0, sizeof(tSMP_ENC));
-  ASSERT_TRUE(
-      SMP_Encrypt(p_cb_.tk, BT_OCTET16_LEN, p1, BT_OCTET16_LEN, &output));
+  Octet16 output = crypto_toolbox::aes_128(p_cb_.tk, p1.data(), OCTET16_LEN);
   const char expected_p1_prime_str[] = "02c7aa2a9857ac866ff91232df0e3c95";
-  char p1_prime_str[2 * sizeof(BT_OCTET16) + 1];
-  dump_uint128_reverse(output.param_buf, p1_prime_str);
+  char p1_prime_str[2 * OCTET16_LEN + 1];
+  dump_uint128_reverse(output, p1_prime_str);
   ASSERT_THAT(p1_prime_str, StrEq(expected_p1_prime_str));
 }
 
 // Test smp_calculate_comfirm function implementation
 TEST_F(SmpCalculateConfirmTest, test_smp_calculate_comfirm_as_master) {
-  tSMP_ENC output;
+  Octet16 output;
   tSMP_STATUS status = smp_calculate_comfirm(&p_cb_, rand_, &output);
   EXPECT_EQ(status, SMP_SUCCESS);
   // Correct MConfirm is 0x1e1e3fef878988ead2a74dc5bef13b86
   const char expected_confirm_str[] = "1e1e3fef878988ead2a74dc5bef13b86";
-  char confirm_str[2 * sizeof(BT_OCTET16) + 1];
-  dump_uint128_reverse(output.param_buf, confirm_str);
+  char confirm_str[2 * OCTET16_LEN + 1];
+  dump_uint128_reverse(output, confirm_str);
   ASSERT_THAT(confirm_str, StrEq(expected_confirm_str));
 }
 }  // namespace testing
diff --git a/tools/scripts/dump_metrics_ascii.py b/tools/scripts/dump_metrics_ascii.py
new file mode 100755
index 0000000..c24d299
--- /dev/null
+++ b/tools/scripts/dump_metrics_ascii.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+# Copyright 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import base64
+import logging
+import os
+import subprocess
+import sys
+import tempfile
+from distutils.spawn import find_executable
+import google.protobuf.text_format as text_format
+from importlib import import_module
+
+def compile_proto(proto_path, output_dir):
+    """Invoke Protocol Compiler to generate python from given source .proto."""
+    # Find compiler path
+    protoc = None
+    if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']):
+        protoc = os.environ['PROTOC']
+    if not protoc:
+        protoc = find_executable('protoc')
+    if not protoc:
+        logging.error(
+            "Cannot find Protobuf compiler (>=3.0.0), please install"
+            "protobuf-compiler package. Prefer copying from <top>/prebuilts/tools"
+        )
+        logging.error("    prebuilts/tools/linux-x86_64/protoc/bin/protoc")
+        logging.error("If prebuilts are not available, use apt-get:")
+        logging.error("    sudo apt-get install protobuf-compiler")
+        return None
+    # Validate input proto path
+    if not os.path.exists(proto_path):
+        logging.error('Can\'t find required file: %s\n' % proto_path)
+        return None
+    # Validate output py-proto path
+    if not os.path.exists(output_dir):
+        os.mkdirs(output_dir)
+    elif not os.path.isdir(output_dir):
+        logging.error("Output path is not a valid directory: %s" %
+                      (output_dir))
+        return None
+    input_dir = os.path.dirname(proto_path)
+    output_filename = os.path.basename(proto_path).replace('.proto', '_pb2.py')
+    output_path = os.path.join(output_dir, output_filename)
+    protoc_command = [
+        protoc, '-I=%s' % (input_dir), '--python_out=%s' % (output_dir),
+        proto_path
+    ]
+    if subprocess.call(protoc_command, stderr=subprocess.STDOUT) != 0:
+        logging.error("Fail to compile proto")
+        return None
+    output_module_name = os.path.splitext(output_filename)[0]
+    return output_module_name
+
+
+def compile_import_proto(output_dir, proto_path):
+    """
+    Compile protobuf from PROTO_PATH and put the result in OUTPUT_DIR.
+    Return the imported module to caller.
+    :param output_dir: To store generated python proto library
+    :param proto_path: Path to the .proto file that needs to be compiled
+    :return: python proto module
+    """
+    output_module_name = compile_proto(proto_path, output_dir)
+    if not output_module_name:
+        return None
+    sys.path.append(output_dir)
+    output_module = None
+    try:
+        output_module = import_module(output_module_name)
+    except ImportError:
+        logging.error("Cannot import generated py-proto %s" %
+                      (output_module_name))
+    return output_module
+
+
+def parse_proto_to_ascii(binary_proto_msg):
+    """
+    Parse binary protobuf message to human readable ascii string
+    :param binary_proto_msg:
+    :return: ascii string of the message
+    """
+    return text_format.MessageToString(binary_proto_msg)
+
+def dump_metrics():
+    os.system('adb wait-for-device')
+    p = subprocess.Popen("adb shell dumpsys bluetooth_manager --proto-bin",
+        shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+        stdin=subprocess.PIPE)
+    return p.communicate()
+
+def get_bluetooth_metrics(proto_native_str_64, bluetooth_proto_module):
+    bluetooth_log = bluetooth_proto_module.BluetoothLog()
+    proto_native_str = base64.b64decode(proto_native_str_64)
+    bluetooth_log.MergeFromString(proto_native_str)
+    return bluetooth_log
+
+def main():
+    root = logging.getLogger()
+    root.setLevel(logging.DEBUG)
+    log_handler = logging.StreamHandler(sys.stderr)
+    log_handler.setLevel(logging.DEBUG)
+    formatter = logging.Formatter(
+        "%(asctime)s %(levelname)s %(message)s")
+    log_handler.setFormatter(formatter)
+    root.addHandler(log_handler)
+    if len(sys.argv) < 2:
+        logging.error("Not enough arguments. Need at least 2")
+        logging.error("Usage: " + sys.argv[0] + " <path_to_metric_proto>")
+        sys.exit(1)
+    if sys.argv[1] == "-h":
+        logging.info("Usage: " + sys.argv[0] + " <path_to_metric_proto>")
+        logging.info("Requires Protobuf compiler, protoc, version >=3.0.0")
+        sys.exit(0)
+    bluetooth_proto_module = compile_import_proto(tempfile.gettempdir(),
+        sys.argv[1])
+    if not bluetooth_proto_module:
+        logging.error("Cannot compile " + sys.argv[1])
+        sys.exit(1)
+    stdout, stderr = dump_metrics()
+    stdout = stdout.strip()
+    stderr = stderr.strip()
+    bluetooth_log = get_bluetooth_metrics(stdout, bluetooth_proto_module)
+    bluetooth_log_ascii = parse_proto_to_ascii(bluetooth_log)
+    print(bluetooth_log_ascii)
+
+if __name__ == "__main__":
+    main()
diff --git a/vendor_libs/linux/sepolicy/file_contexts b/vendor_libs/linux/sepolicy/file_contexts
deleted file mode 100644
index 20b69c1..0000000
--- a/vendor_libs/linux/sepolicy/file_contexts
+++ /dev/null
@@ -1,2 +0,0 @@
-/sys/class/rfkill/rfkill[0-9]/state    u:object_r:sysfs_bluetooth_writable:s0
-/(vendor|system/vendor)/bin/hw/android\.hardware\.bluetooth@1\.0-service\.btlinux    u:object_r:hal_bluetooth_btlinux_exec:s0
diff --git a/vendor_libs/linux/sepolicy/hal_bluetooth_btlinux.te b/vendor_libs/linux/sepolicy/hal_bluetooth_btlinux.te
deleted file mode 100644
index 22d9cf0..0000000
--- a/vendor_libs/linux/sepolicy/hal_bluetooth_btlinux.te
+++ /dev/null
@@ -1,8 +0,0 @@
-type hal_bluetooth_btlinux, domain;
-type hal_bluetooth_btlinux_exec, exec_type, file_type, vendor_file_type;
-
-hal_server_domain(hal_bluetooth_btlinux, hal_bluetooth)
-init_daemon_domain(hal_bluetooth_btlinux)
-
-allow hal_bluetooth_btlinux self:socket { create bind read write };
-allow hal_bluetooth_btlinux self:bluetooth_socket { create bind read write };