diff --git a/system/bta/Android.bp b/system/bta/Android.bp
index 18a6cc1260e477da0ccbc8b3bb04307fea66d546..eb9f953751d937e629fd2dab402a3590a4c881da 100644
--- a/system/bta/Android.bp
+++ b/system/bta/Android.bp
@@ -295,6 +295,7 @@ cc_test {
     ],
     static_libs: [
         "libbluetooth-types",
+        "libbtdevice",
         "libbt-common",
         "libbt-protos-lite",
         "libbtcore",
diff --git a/system/bta/ag/bta_ag_act.cc b/system/bta/ag/bta_ag_act.cc
index fef362082c5da90d5ce9405c9bbcb77382afa066..2612f48bcc07e139903b6143bf3feed4e73537d5 100644
--- a/system/bta/ag/bta_ag_act.cc
+++ b/system/bta/ag/bta_ag_act.cc
@@ -22,19 +22,20 @@
  *
  ******************************************************************************/
 
+#include <base/logging.h>
+
 #include <cstdint>
 #include <cstring>
 
 #include "bta/ag/bta_ag_int.h"
 #include "bta/include/bta_dm_api.h"
 #include "btif/include/btif_config.h"
+#include "device/include/device_iot_config.h"
 #include "osi/include/osi.h"  // UNUSED_ATTR
 #include "stack/include/l2c_api.h"
 #include "stack/include/port_api.h"
 #include "types/raw_address.h"
 
-#include <base/logging.h>
-
 /*****************************************************************************
  *  Constants
  ****************************************************************************/
@@ -222,6 +223,10 @@ void bta_ag_disc_int_res(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) {
 
       /* send ourselves sdp ok event */
       event = BTA_AG_DISC_OK_EVT;
+
+      DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(
+          p_scb->peer_addr, IOT_CONF_KEY_HFP_VERSION, p_scb->peer_version,
+          IOT_CONF_BYTE_NUM_2);
     }
   }
 
@@ -269,6 +274,9 @@ void bta_ag_disc_acp_res(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) {
       data.disc_result.status == SDP_DB_FULL) {
     /* get attributes */
     bta_ag_sdp_find_attr(p_scb, bta_ag_svc_mask[p_scb->conn_service]);
+    DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(
+        p_scb->peer_addr, IOT_CONF_KEY_HFP_VERSION, p_scb->peer_version,
+        IOT_CONF_BYTE_NUM_2);
   }
 
   /* free discovery db */
@@ -520,6 +528,7 @@ void bta_ag_rfc_acp_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) {
 
   /* get bd addr of peer */
   uint16_t lcid = 0;
+  uint16_t hfp_version = 0;
   RawAddress dev_addr = RawAddress::kEmpty;
   int status = PORT_CheckConnection(data.rfc.port_handle, &dev_addr, &lcid);
   if (status != PORT_SUCCESS) {
@@ -583,6 +592,16 @@ void bta_ag_rfc_acp_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) {
   bta_ag_close_servers(
       p_scb, (p_scb->reg_services & ~bta_ag_svc_mask[p_scb->conn_service]));
 
+  size_t version_value_size = sizeof(hfp_version);
+  bool get_version =
+      btif_config_get_bin(p_scb->peer_addr.ToString(), HFP_VERSION_CONFIG_KEY,
+                          (uint8_t*)&hfp_version, &version_value_size);
+
+  if (p_scb->conn_service == BTA_AG_HFP && get_version) {
+    DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(p_scb->peer_addr,
+                                              IOT_CONF_KEY_HFP_VERSION,
+                                              hfp_version, IOT_CONF_BYTE_NUM_2);
+  }
   /* do service discovery to get features */
   bta_ag_do_disc(p_scb, bta_ag_svc_mask[p_scb->conn_service]);
 
diff --git a/system/bta/av/bta_av_aact.cc b/system/bta/av/bta_av_aact.cc
index 0e1d8746c1605f3edeefa96466c825e26752af14..83aca43b861c58823e176a9b47c86bb17a968f6b 100644
--- a/system/bta/av/bta_av_aact.cc
+++ b/system/bta/av/bta_av_aact.cc
@@ -40,6 +40,7 @@
 #include "btif/include/btif_av_co.h"
 #include "btif/include/btif_config.h"
 #include "btif/include/btif_storage.h"
+#include "device/include/device_iot_config.h"
 #include "device/include/interop.h"
 #include "main/shim/dumpsys.h"
 #include "osi/include/allocator.h"
@@ -533,6 +534,10 @@ static void bta_av_a2dp_sdp_cback(bool found, tA2DP_Service* p_service,
   }
   if (found && (p_service != NULL)) {
     p_scb->SetAvdtpVersion(p_service->avdt_version);
+    DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(
+        p_scb->PeerAddress(), IOT_CONF_KEY_A2DP_VERSION,
+        p_service->avdt_version, IOT_CONF_BYTE_NUM_2);
+
     if (p_service->avdt_version != 0) {
       if (btif_config_set_bin(p_scb->PeerAddress().ToString(),
                               AVDTP_VERSION_CONFIG_KEY,
@@ -970,10 +975,17 @@ void bta_av_config_ind(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* p_data) {
     p_info->seid = p_data->str_msg.msg.config_ind.int_seid;
 
     /* Sep type of Peer will be oppsite role to our local sep */
-    if (local_sep == AVDT_TSEP_SRC)
+    if (local_sep == AVDT_TSEP_SRC) {
       p_info->tsep = AVDT_TSEP_SNK;
-    else if (local_sep == AVDT_TSEP_SNK)
+      DEVICE_IOT_CONFIG_ADDR_SET_INT(p_scb->PeerAddress(),
+                                     IOT_CONF_KEY_A2DP_ROLE,
+                                     IOT_CONF_VAL_A2DP_ROLE_SINK);
+    } else if (local_sep == AVDT_TSEP_SNK) {
       p_info->tsep = AVDT_TSEP_SRC;
+      DEVICE_IOT_CONFIG_ADDR_SET_INT(p_scb->PeerAddress(),
+                                     IOT_CONF_KEY_A2DP_ROLE,
+                                     IOT_CONF_VAL_A2DP_ROLE_SOURCE);
+    }
 
     p_scb->role |= BTA_AV_ROLE_AD_ACP;
     p_scb->cur_psc_mask = p_evt_cfg->psc_mask;
diff --git a/system/bta/av/bta_av_act.cc b/system/bta/av/bta_av_act.cc
index ec6d3b7bc357f81b739b500a2e216416bb30749f..2eb93c612cf0bd6ffcaa047368fbccc99f7012df 100644
--- a/system/bta/av/bta_av_act.cc
+++ b/system/bta/av/bta_av_act.cc
@@ -32,6 +32,7 @@
 #include "bta/include/bta_ar_api.h"
 #include "bta/include/utl.h"
 #include "btif/avrcp/avrcp_service.h"
+#include "device/include/device_iot_config.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"  // UNUSED_ATTR
@@ -323,6 +324,9 @@ uint8_t bta_av_rc_create(tBTA_AV_CB* p_cb, uint8_t role, uint8_t shdl,
     tBTA_AV_SCB* p_scb = p_cb->p_scb[shdl - 1];
     bda = p_scb->PeerAddress();
     status = BTA_AV_RC_ROLE_INT;
+    DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(p_scb->PeerAddress(),
+                                       IOT_CONF_KEY_AVRCP_CONN_COUNT);
+
   } else {
     p_rcb = bta_av_get_rcb_by_shdl(shdl);
     if (p_rcb != NULL) {
@@ -341,8 +345,10 @@ uint8_t bta_av_rc_create(tBTA_AV_CB* p_cb, uint8_t role, uint8_t shdl,
   ccb.control = p_cb->features & (BTA_AV_FEAT_RCTG | BTA_AV_FEAT_RCCT |
                                   BTA_AV_FEAT_METADATA | AVRC_CT_PASSIVE);
 
-  if (AVRC_Open(&rc_handle, &ccb, bda) != AVRC_SUCCESS)
+  if (AVRC_Open(&rc_handle, &ccb, bda) != AVRC_SUCCESS) {
+    DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(bda, IOT_CONF_KEY_AVRCP_CONN_FAIL_COUNT);
     return BTA_AV_RC_HANDLE_NONE;
+  }
 
   i = rc_handle;
   p_rcb = &p_cb->rcb[i];
@@ -1650,6 +1656,39 @@ static void bta_av_accept_signalling_timer_cback(void* data) {
   }
 }
 
+static void bta_av_store_peer_rc_version() {
+  tBTA_AV_CB* p_cb = &bta_av_cb;
+  tSDP_DISC_REC* p_rec = NULL;
+  uint16_t peer_rc_version = 0; /*Assuming Default peer version as 1.3*/
+
+  if ((p_rec = SDP_FindServiceInDb(
+           p_cb->p_disc_db, UUID_SERVCLASS_AV_REMOTE_CONTROL, NULL)) != NULL) {
+    if ((SDP_FindAttributeInRec(p_rec, ATTR_ID_BT_PROFILE_DESC_LIST)) != NULL) {
+      /* get profile version (if failure, version parameter is not updated) */
+      SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_AV_REMOTE_CONTROL,
+                                  &peer_rc_version);
+    }
+    if (peer_rc_version != 0)
+      DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(
+          p_rec->remote_bd_addr, IOT_CONF_KEY_AVRCP_CTRL_VERSION,
+          peer_rc_version, IOT_CONF_BYTE_NUM_2);
+  }
+
+  peer_rc_version = 0;
+  if ((p_rec = SDP_FindServiceInDb(
+           p_cb->p_disc_db, UUID_SERVCLASS_AV_REM_CTRL_TARGET, NULL)) != NULL) {
+    if ((SDP_FindAttributeInRec(p_rec, ATTR_ID_BT_PROFILE_DESC_LIST)) != NULL) {
+      /* get profile version (if failure, version parameter is not updated) */
+      SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_AV_REMOTE_CONTROL,
+                                  &peer_rc_version);
+    }
+    if (peer_rc_version != 0)
+      DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(
+          p_rec->remote_bd_addr, IOT_CONF_KEY_AVRCP_TG_VERSION, peer_rc_version,
+          IOT_CONF_BYTE_NUM_2);
+  }
+}
+
 /*******************************************************************************
  *
  * Function         bta_av_check_peer_features
@@ -1986,6 +2025,8 @@ void bta_av_rc_disc_done(UNUSED_ATTR tBTA_AV_DATA* p_data) {
     }
   }
 
+  bta_av_store_peer_rc_version();
+
   p_cb->disc = 0;
   osi_free_and_reset((void**)&p_cb->p_disc_db);
 
@@ -2036,6 +2077,10 @@ void bta_av_rc_disc_done(UNUSED_ATTR tBTA_AV_DATA* p_data) {
         bta_av_data.rc_open = rc_open;
         (*p_cb->p_cback)(BTA_AV_RC_OPEN_EVT, &bta_av_data);
       }
+      if (peer_features != 0)
+        DEVICE_IOT_CONFIG_ADDR_SET_HEX(p_scb->PeerAddress(),
+                                       IOT_CONF_KEY_AVRCP_FEATURES,
+                                       peer_features, IOT_CONF_BYTE_NUM_2);
     }
   } else {
     tBTA_AV_RC_FEAT rc_feat;
@@ -2057,6 +2102,11 @@ void bta_av_rc_disc_done(UNUSED_ATTR tBTA_AV_DATA* p_data) {
     bta_av_feat.rc_feat = rc_feat;
     (*p_cb->p_cback)(BTA_AV_RC_FEAT_EVT, &bta_av_feat);
 
+    if (peer_features != 0)
+      DEVICE_IOT_CONFIG_ADDR_SET_HEX(rc_feat.peer_addr,
+                                     IOT_CONF_KEY_AVRCP_FEATURES, peer_features,
+                                     IOT_CONF_BYTE_NUM_2);
+
     // Send PSM data
     APPL_TRACE_DEBUG("%s: Send PSM data", __func__);
     tBTA_AV_RC_PSM rc_psm;
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index cfb87b39644f61aedd04e308c68ff6eb1b423092..69a334ba44092f8b82a049167b685adf7ee4d693 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -59,7 +59,7 @@
 #include "stack/include/bt_octets.h"
 #include "stack/include/bt_types.h"
 #include "stack/include/btm_client_interface.h"
-#include "stack/include/btu.h"  // do_in_main_thread
+#include "stack/include/btu.h"       // do_in_main_thread
 #include "stack/include/srvc_api.h"  // DIS_ReadDISInfo
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 6fca3df1b059eb8356b1477f224a5cf348042389..81e517744b88a075575b3b5f62081a9010fffd3b 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -115,6 +115,7 @@ cc_library_static {
         "src/btif_hf_client.cc",
         "src/btif_hh.cc",
         "src/btif_hd.cc",
+        "src/btif_iot_config.cc",
         "src/btif_le_audio.cc",
         "src/btif_le_audio_broadcaster.cc",
         "src/btif_pan.cc",
@@ -186,6 +187,7 @@ cc_library_static {
         "src/btif_gatt_server.cc",
         "src/btif_gatt_test.cc",
         "src/btif_gatt_util.cc",
+        "src/btif_iot_config.cc",
         "src/btif_metrics_logging.cc",
         "src/btif_profile_queue.cc",
         "src/btif_sdp.cc",
@@ -658,6 +660,7 @@ cc_test {
           "libbt-sbc-encoder",
           "libbtif",
           "libbtif-core",
+          "libbt-stack-core",
           "libbtdevice",
           "lib-bt-packets",
           "lib-bt-packets-avrcp",
diff --git a/system/btif/BUILD.gn b/system/btif/BUILD.gn
index 814c911dde85b7ac913122d4bca25110dbaa9882..625157e45abd2759bfec73c02c21711afb241674 100644
--- a/system/btif/BUILD.gn
+++ b/system/btif/BUILD.gn
@@ -71,6 +71,7 @@ static_library("btif") {
     "src/btif_hf.cc",
     "src/btif_hf_client.cc",
     "src/btif_hh.cc",
+    "src/btif_iot_config.cc",
     "src/btif_keystore.cc",
     "src/btif_le_audio.cc",
     "src/btif_metrics_logging.cc",
diff --git a/system/btif/co/bta_av_co.cc b/system/btif/co/bta_av_co.cc
index 653b201a35e09d8d125888c68414d50198597c69..426bdc7e519835f915ba1818414dade2b12acb85 100644
--- a/system/btif/co/bta_av_co.cc
+++ b/system/btif/co/bta_av_co.cc
@@ -23,6 +23,8 @@
  *
  ******************************************************************************/
 
+#include <base/logging.h>
+
 #include <mutex>
 #include <vector>
 
@@ -31,6 +33,7 @@
 #include "bta/include/bta_av_ci.h"
 #include "btif/include/btif_a2dp_source.h"
 #include "btif/include/btif_av.h"
+#include "device/include/device_iot_config.h"
 #include "include/hardware/bt_av.h"
 #include "osi/include/osi.h"  // UNUSED_ATTR
 #include "stack/include/a2dp_codec_api.h"
@@ -40,8 +43,6 @@
 #include "types/bluetooth/uuid.h"
 #include "types/raw_address.h"
 
-#include <base/logging.h>
-
 // Macro to retrieve the number of elements in a statically allocated array
 #define BTA_AV_CO_NUM_ELEMENTS(__a) (sizeof(__a) / sizeof((__a)[0]))
 
@@ -910,6 +911,8 @@ void BtaAvCo::ProcessDiscoveryResult(tBTA_AV_HNDL bta_av_handle,
   }
 }
 
+static void bta_av_co_store_peer_codectype(const BtaAvCoPeer* p_peer);
+
 tA2DP_STATUS BtaAvCo::ProcessSourceGetConfig(
     tBTA_AV_HNDL bta_av_handle, const RawAddress& peer_address,
     uint8_t* p_codec_info, uint8_t* p_sep_info_idx, uint8_t seid,
@@ -968,6 +971,8 @@ tA2DP_STATUS BtaAvCo::ProcessSourceGetConfig(
                    __func__, ADDRESS_TO_LOGGABLE_CSTR(p_peer->addr),
                    p_peer->acceptor ? "acceptor" : "initiator");
 
+  bta_av_co_store_peer_codectype(p_peer);
+
   // Select the Source codec
   const BtaAvCoSep* p_sink = nullptr;
   if (p_peer->acceptor) {
@@ -2115,6 +2120,19 @@ void bta_av_co_audio_disc_res(tBTA_AV_HNDL bta_av_handle,
                                       num_sinks, num_sources, uuid_local);
 }
 
+static void bta_av_co_store_peer_codectype(const BtaAvCoPeer* p_peer) {
+  int index, peer_codec_type = 0;
+  const BtaAvCoSep* p_sink;
+  APPL_TRACE_DEBUG("%s", __func__);
+  for (index = 0; index < p_peer->num_sup_sinks; index++) {
+    p_sink = &p_peer->sinks[index];
+    peer_codec_type |= A2DP_IotGetPeerSinkCodecType(p_sink->codec_caps);
+  }
+
+  DEVICE_IOT_CONFIG_ADDR_SET_HEX(p_peer->addr, IOT_CONF_KEY_A2DP_CODECTYPE,
+                                 peer_codec_type, IOT_CONF_BYTE_NUM_1);
+}
+
 tA2DP_STATUS bta_av_co_audio_getconfig(tBTA_AV_HNDL bta_av_handle,
                                        const RawAddress& peer_address,
                                        uint8_t* p_codec_info,
diff --git a/system/btif/src/bluetooth.cc b/system/btif/src/bluetooth.cc
index b303c3d095c5370ea0cf097c18a9c016902f576d..1ed5550544df8b938170997f4d7314ddf516356c 100644
--- a/system/btif/src/bluetooth.cc
+++ b/system/btif/src/bluetooth.cc
@@ -82,6 +82,7 @@
 #include "common/metric_id_allocator.h"
 #include "common/metrics.h"
 #include "common/os_utils.h"
+#include "device/include/device_iot_config.h"
 #include "device/include/interop.h"
 #include "gd/common/init_flags.h"
 #include "gd/os/parameter_provider.h"
@@ -760,6 +761,7 @@ static void dump(int fd, const char** arguments) {
   stack_debug_avdtp_api_dump(fd);
   bluetooth::avrcp::AvrcpService::DebugDump(fd);
   btif_debug_config_dump(fd);
+  device_debug_iot_config_dump(fd);
   BTA_HfClientDumpStatistics(fd);
   wakelock_debug_dump(fd);
   osi_allocator_debug_dump(fd);
@@ -940,7 +942,18 @@ static int set_os_callouts(bt_os_callouts_t* callouts) {
 
 static int config_clear(void) {
   LOG_INFO("%s", __func__);
-  return btif_config_clear() ? BT_STATUS_SUCCESS : BT_STATUS_FAIL;
+  int ret = BT_STATUS_SUCCESS;
+  if (!btif_config_clear()) {
+    LOG_ERROR("Failed to clear btif config");
+    ret = BT_STATUS_FAIL;
+  }
+
+  if (!device_iot_config_clear()) {
+    LOG_ERROR("Failed to clear device iot config");
+    ret = BT_STATUS_FAIL;
+  }
+
+  return ret;
 }
 
 static bluetooth::avrcp::ServiceInterface* get_avrcp_service(void) {
diff --git a/system/btif/src/btif_av.cc b/system/btif/src/btif_av.cc
index e3c65b7c53c0820ee9c738ca69a3b2bebdfda8bb..fad4b2766dd6e537ab1d2151e750a06e863d51c5 100644
--- a/system/btif/src/btif_av.cc
+++ b/system/btif/src/btif_av.cc
@@ -48,6 +48,7 @@
 #include "btif_metrics_logging.h"
 #include "common/metrics.h"
 #include "common/state_machine.h"
+#include "device/include/device_iot_config.h"
 #include "hardware/bt_av.h"
 #include "include/hardware/bt_rc.h"
 #include "main/shim/dumpsys.h"
@@ -1502,6 +1503,18 @@ bool BtifAvStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
       BTA_AvOpen(peer_.PeerAddress(), peer_.BtaHandle(), true,
                  peer_.LocalUuidServiceClass());
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpening);
+      if (event == BTIF_AV_CONNECT_REQ_EVT) {
+        DEVICE_IOT_CONFIG_ADDR_SET_INT(
+            peer_.PeerAddress(), IOT_CONF_KEY_A2DP_ROLE,
+            (peer_.LocalUuidServiceClass() == UUID_SERVCLASS_AUDIO_SOURCE)
+                ? IOT_CONF_VAL_A2DP_ROLE_SINK
+                : IOT_CONF_VAL_A2DP_ROLE_SOURCE);
+        DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
+                                           IOT_CONF_KEY_A2DP_CONN_COUNT);
+      } else if (event == BTA_AV_PENDING_EVT) {
+        DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
+                                           IOT_CONF_KEY_A2DP_CONN_COUNT);
+      }
     } break;
     case BTIF_AV_AVRCP_OPEN_EVT:
     case BTA_AV_RC_OPEN_EVT: {
@@ -1626,6 +1639,8 @@ bool BtifAvStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
                                      BTAV_CONNECTION_STATE_DISCONNECTED,
                                      bt_status_t::BT_STATUS_FAIL, status);
         peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+        DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
+                                           IOT_CONF_KEY_A2DP_CONN_FAIL_COUNT);
       }
       btif_queue_advance();
     } break;
@@ -1771,6 +1786,8 @@ bool BtifAvStateMachine::StateOpening::ProcessEvent(uint32_t event,
           if (peer_handle != BTRC_HANDLE_NONE) {
             BTA_AvCloseRc(peer_handle);
           }
+          DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
+                                             IOT_CONF_KEY_A2DP_CONN_FAIL_COUNT);
         }
         av_state = BtifAvStateMachine::kStateIdle;
         // Report the connection state to the application
@@ -1855,6 +1872,8 @@ bool BtifAvStateMachine::StateOpening::ProcessEvent(uint32_t event,
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
       log_counter_metrics_btif(
           android::bluetooth::CodePathCounterKeyEnum::A2DP_CONNECTION_CLOSE, 1);
+      DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
+                                         IOT_CONF_KEY_A2DP_CONN_FAIL_COUNT);
       if (peer_.SelfInitiatedConnection()) {
         btif_queue_advance();
       }
@@ -1866,6 +1885,8 @@ bool BtifAvStateMachine::StateOpening::ProcessEvent(uint32_t event,
                                    BTAV_CONNECTION_STATE_DISCONNECTED,
                                    bt_status_t::BT_STATUS_FAIL, BTA_AV_FAIL);
       peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateIdle);
+      DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
+                                         IOT_CONF_KEY_A2DP_CONN_FAIL_COUNT);
       log_counter_metrics_btif(android::bluetooth::CodePathCounterKeyEnum::
                                    A2DP_CONNECTION_DISCONNECTED,
                                1);
diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc
index 7803b9550a132b2638d9c8beef1008eb0701e976..50634ec756461e3ed7ccdf582b374d8b99d32ac4 100644
--- a/system/btif/src/btif_core.cc
+++ b/system/btif/src/btif_core.cc
@@ -50,6 +50,7 @@
 #include "btif/include/stack_manager.h"
 #include "common/message_loop_thread.h"
 #include "device/include/controller.h"
+#include "device/include/device_iot_config.h"
 #include "osi/include/allocator.h"
 #include "osi/include/future.h"
 #include "osi/include/log.h"
@@ -271,6 +272,10 @@ void btif_enable_bluetooth_evt() {
 
   std::string bdstr = local_bd_addr.ToString();
 
+  // save bd addr to iot conf file
+  device_iot_config_set_str(IOT_CONF_KEY_SECTION_ADAPTER, IOT_CONF_KEY_ADDRESS,
+                            bdstr);
+
   char val[PROPERTY_VALUE_MAX] = "";
   int val_size = PROPERTY_VALUE_MAX;
   if (!btif_config_get_str("Adapter", "Address", val, &val_size) ||
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 921ecaea6f5591b928ced8fac2c03c4e4935e231..d2f65b4a3d71cd71279bbb6c02a606c68da0e014 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -70,6 +70,7 @@
 #include "common/lru.h"
 #include "common/metrics.h"
 #include "device/include/controller.h"
+#include "device/include/device_iot_config.h"
 #include "device/include/interop.h"
 #include "gd/common/lru_cache.h"
 #include "internal_include/stack_config.h"
@@ -278,6 +279,8 @@ static void btif_stats_add_bond_event(const RawAddress& bd_addr,
  *  Externs
  *****************************************************************************/
 extern bt_status_t btif_sdp_execute_service(bool b_enable);
+extern void btif_iot_update_remote_info(tBTA_DM_AUTH_CMPL* p_auth_cmpl,
+                                        bool is_ble, bool is_ssp);
 
 /******************************************************************************
  *  Functions
@@ -1044,6 +1047,9 @@ static void btif_dm_auth_cmpl_evt(tBTA_DM_AUTH_CMPL* p_auth_cmpl) {
   }
 
   if (p_auth_cmpl->success) {
+    // save remote info to iot conf file
+    btif_iot_update_remote_info(p_auth_cmpl, false, pairing_cb.is_ssp);
+
     // We could have received a new link key without going through the pairing
     // flow.  If so, we don't want to perform SDP or any other operations on the
     // authenticated device. Also, make sure that the link key is not derived
diff --git a/system/btif/src/btif_hf.cc b/system/btif/src/btif_hf.cc
index 9aa2d3e30cdd0fe6669a15222fec9707f2810235..cae174113188fec3ebfc6c91f6d9ceba4b22f64a 100644
--- a/system/btif/src/btif_hf.cc
+++ b/system/btif/src/btif_hf.cc
@@ -40,6 +40,7 @@
 #include "btif/include/btif_profile_queue.h"
 #include "btif/include/btif_util.h"
 #include "common/metrics.h"
+#include "device/include/device_iot_config.h"
 #include "include/hardware/bluetooth_headset_callbacks.h"
 #include "include/hardware/bluetooth_headset_interface.h"
 #include "include/hardware/bt_hf.h"
@@ -388,6 +389,14 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) {
       if (p_data->open.status == BTA_AG_SUCCESS) {
         // In case this is an incoming connection
         btif_hf_cb[idx].connected_bda = p_data->open.bd_addr;
+        if (btif_hf_cb[idx].state != BTHF_CONNECTION_STATE_CONNECTING) {
+          DEVICE_IOT_CONFIG_ADDR_SET_INT(btif_hf_cb[idx].connected_bda,
+                                         IOT_CONF_KEY_HFP_ROLE,
+                                         IOT_CONF_VAL_HFP_ROLE_CLIENT);
+          DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(btif_hf_cb[idx].connected_bda,
+                                             IOT_CONF_KEY_HFP_SLC_CONN_COUNT);
+        }
+
         btif_hf_cb[idx].state = BTHF_CONNECTION_STATE_CONNECTED;
         btif_hf_cb[idx].peer_feat = 0;
         clear_phone_state_multihf(&btif_hf_cb[idx]);
@@ -414,6 +423,8 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) {
                                      HFP_SELF_INITIATED_AG_FAILED,
                                  1);
         btif_queue_advance();
+        DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(
+            connected_bda, IOT_CONF_KEY_HFP_SLC_CONN_FAIL_COUNT);
       }
       break;
     case BTA_AG_CLOSE_EVT: {
@@ -439,10 +450,22 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) {
             android::bluetooth::CodePathCounterKeyEnum::HFP_SLC_SETUP_FAILED,
             1);
         btif_queue_advance();
+        DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(
+            btif_hf_cb[idx].connected_bda,
+            IOT_CONF_KEY_HFP_SLC_CONN_FAIL_COUNT);
       }
       break;
     }
     case BTA_AG_CONN_EVT:
+      DEVICE_IOT_CONFIG_ADDR_SET_HEX(
+          btif_hf_cb[idx].connected_bda, IOT_CONF_KEY_HFP_CODECTYPE,
+          p_data->conn.peer_codec == 0x03 ? IOT_CONF_VAL_HFP_CODECTYPE_CVSDMSBC
+                                          : IOT_CONF_VAL_HFP_CODECTYPE_CVSD,
+          IOT_CONF_BYTE_NUM_1);
+      DEVICE_IOT_CONFIG_ADDR_SET_HEX(
+          btif_hf_cb[idx].connected_bda, IOT_CONF_KEY_HFP_FEATURES,
+          p_data->conn.peer_feat, IOT_CONF_BYTE_NUM_2);
+
       LOG_DEBUG("SLC connected event:%s idx:%d", dump_hf_event(event), idx);
       btif_hf_cb[idx].peer_feat = p_data->conn.peer_feat;
       btif_hf_cb[idx].state = BTHF_CONNECTION_STATE_SLC_CONNECTED;
@@ -461,6 +484,10 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) {
 
     case BTA_AG_AUDIO_CLOSE_EVT:
       LOG_DEBUG("Audio close event:%s", dump_hf_event(event));
+
+      DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(btif_hf_cb[idx].connected_bda,
+                                         IOT_CONF_KEY_HFP_SCO_CONN_FAIL_COUNT);
+
       bt_hf_callbacks->AudioStateCallback(BTHF_AUDIO_STATE_DISCONNECTED,
                                           &btif_hf_cb[idx].connected_bda);
       break;
@@ -702,6 +729,11 @@ static bt_status_t connect_int(RawAddress* bd_addr, uint16_t uuid) {
   hf_cb->is_initiator = true;
   hf_cb->peer_feat = 0;
   BTA_AgOpen(hf_cb->handle, hf_cb->connected_bda);
+
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(hf_cb->connected_bda, IOT_CONF_KEY_HFP_ROLE,
+                                 IOT_CONF_VAL_HFP_ROLE_CLIENT);
+  DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(hf_cb->connected_bda,
+                                     IOT_CONF_KEY_HFP_SLC_CONN_COUNT);
   return BT_STATUS_SUCCESS;
 }
 
@@ -854,6 +886,9 @@ bt_status_t HeadsetInterface::ConnectAudio(RawAddress* bd_addr,
                               BTHF_AUDIO_STATE_CONNECTING,
                               &btif_hf_cb[idx].connected_bda));
   BTA_AgAudioOpen(btif_hf_cb[idx].handle, force_cvsd);
+
+  DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(*bd_addr, IOT_CONF_KEY_HFP_SCO_CONN_COUNT);
+
   return BT_STATUS_SUCCESS;
 }
 
@@ -1157,6 +1192,7 @@ bt_status_t HeadsetInterface::PhoneStateChange(
     LOG_WARN("Invalid index %d for %s", idx, PRIVATE_ADDRESS(raw_address));
     return BT_STATUS_FAIL;
   }
+
   const btif_hf_cb_t& control_block = btif_hf_cb[idx];
   if (!IsSlcConnected(bd_addr)) {
     LOG(WARNING) << ": SLC not connected for " << *bd_addr;
@@ -1425,6 +1461,10 @@ bt_status_t HeadsetInterface::PhoneStateChange(
   }
 
   UpdateCallStates(&btif_hf_cb[idx], num_active, num_held, call_setup_state);
+
+  DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(btif_hf_cb[idx].connected_bda,
+                                     IOT_CONF_KEY_HFP_SCO_CONN_COUNT);
+
   return status;
 }
 
diff --git a/system/btif/src/btif_iot_config.cc b/system/btif/src/btif_iot_config.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a44794040fd3790b0427212ed7b16ed5da99a100
--- /dev/null
+++ b/system/btif/src/btif_iot_config.cc
@@ -0,0 +1,149 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2018 The Linux Foundation
+ *
+ *  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 "bt_target.h"
+#include "bta_api.h"
+#include "btif_storage.h"
+#include "device/include/device_iot_config.h"
+#include "stack/include/btm_ble_api.h"
+
+/*******************************************************************************
+ *  Constants & Macros
+ ******************************************************************************/
+#define COD_UNCLASSIFIED ((0x1F) << 8)
+
+/*******************************************************************************
+ *
+ * Function         btif_iot_save_pair_type
+ *
+ * Description      Store remote pair type to iot conf file
+ *
+ * Returns          void
+ *
+ *******************************************************************************/
+static void btif_iot_save_pair_type(const RawAddress& bdaddr, bool is_ble,
+                                    bool is_ssp) {
+  if (is_ssp) {
+    if (!is_ble)
+      DEVICE_IOT_CONFIG_ADDR_SET_INT(bdaddr, IOT_CONF_KEY_PAIRTYPE,
+                                     IOT_CONF_VAL_PAIR_TYPE_SSP);
+    else
+      DEVICE_IOT_CONFIG_ADDR_SET_INT(bdaddr, IOT_CONF_KEY_LE_PAIRTYPE,
+                                     IOT_CONF_VAL_LE_PAIRTYPE_SECURE);
+  } else {
+    if (!is_ble)
+      DEVICE_IOT_CONFIG_ADDR_SET_INT(bdaddr, IOT_CONF_KEY_PAIRTYPE,
+                                     IOT_CONF_VAL_PAIR_TYPE_LEGACY);
+    else
+      DEVICE_IOT_CONFIG_ADDR_SET_INT(bdaddr, IOT_CONF_KEY_LE_PAIRTYPE,
+                                     IOT_CONF_VAL_LE_PAIRTYPE_LEGACY);
+  }
+}
+
+/*******************************************************************************
+ *
+ * Function         btif_iot_update_remote_info
+ *
+ * Description      Store remote dev info to iot conf file
+ *
+ * Returns          void
+ *
+ *******************************************************************************/
+void btif_iot_update_remote_info(tBTA_DM_AUTH_CMPL* p_auth_cmpl, bool is_ble,
+                                 bool is_ssp) {
+  int name_length = 0;
+  char value[1024];
+  BD_NAME bd_name;
+  int num_properties = 0;
+  bt_property_t properties[2];
+  uint32_t cod = 0;
+  uint8_t lmp_ver = 0;
+  uint16_t lmp_subver = 0;
+  uint16_t mfct_set = 0;
+  tBTM_STATUS btm_status;
+
+  // save remote name to iot conf file
+  if (strlen((const char*)p_auth_cmpl->bd_name)) {
+    name_length = strlen((char*)p_auth_cmpl->bd_name) > BTM_MAX_LOC_BD_NAME_LEN
+                      ? BTM_MAX_LOC_BD_NAME_LEN
+                      : strlen((char*)p_auth_cmpl->bd_name) + 1;
+    strncpy(value, (char*)p_auth_cmpl->bd_name, name_length);
+    DEVICE_IOT_CONFIG_ADDR_SET_STR(p_auth_cmpl->bd_addr,
+                                   IOT_CONF_KEY_REMOTE_NAME, value);
+  } else {
+    if (BTM_GetRemoteDeviceName(p_auth_cmpl->bd_addr, bd_name)) {
+      DEVICE_IOT_CONFIG_ADDR_SET_STR(p_auth_cmpl->bd_addr,
+                                     IOT_CONF_KEY_REMOTE_NAME, (char*)bd_name);
+    }
+  }
+
+  // save remote dev class to iot conf file
+  // Try to retrieve cod from storage
+  BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
+                             BT_PROPERTY_CLASS_OF_DEVICE, sizeof(cod), &cod);
+  if (btif_storage_get_remote_device_property(&p_auth_cmpl->bd_addr,
+                                              &properties[num_properties]) ==
+      BT_STATUS_SUCCESS)
+    BTIF_TRACE_DEBUG("%s cod retrieved from storage is 0x%06x", __func__, cod);
+  if (cod == 0) {
+    BTIF_TRACE_DEBUG("%s cod is 0, set as unclassified", __func__);
+    cod = COD_UNCLASSIFIED;
+  }
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_auth_cmpl->bd_addr, IOT_CONF_KEY_DEVCLASS,
+                                 (int)cod);
+  num_properties++;
+
+  // save remote dev type to iot conf file
+  bt_device_type_t dev_type;
+  uint8_t remote_dev_type;
+  BTIF_STORAGE_FILL_PROPERTY(&properties[num_properties],
+                             BT_PROPERTY_TYPE_OF_DEVICE, sizeof(uint8_t),
+                             &remote_dev_type);
+  if (btif_storage_get_remote_device_property(&p_auth_cmpl->bd_addr,
+                                              &properties[num_properties]) ==
+      BT_STATUS_SUCCESS) {
+    BTIF_TRACE_DEBUG("%s retrieve dev type from storage", __func__);
+    dev_type = (bt_device_type_t)(remote_dev_type | p_auth_cmpl->dev_type);
+  } else {
+    dev_type = (bt_device_type_t)(p_auth_cmpl->dev_type);
+  }
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_auth_cmpl->bd_addr, IOT_CONF_KEY_DEVTYPE,
+                                 (int)dev_type);
+
+  // save remote addr type to iot conf file
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_auth_cmpl->bd_addr, IOT_CONF_KEY_ADDRTYPE,
+                                 (int)p_auth_cmpl->addr_type);
+
+  // save remote versions to iot conf file
+  btm_status = BTM_ReadRemoteVersion(p_auth_cmpl->bd_addr, &lmp_ver, &mfct_set,
+                                     &lmp_subver);
+
+  if (btm_status == BTM_SUCCESS) {
+    DEVICE_IOT_CONFIG_ADDR_SET_INT(p_auth_cmpl->bd_addr,
+                                   IOT_CONF_KEY_MANUFACTURER, mfct_set);
+    DEVICE_IOT_CONFIG_ADDR_SET_INT(p_auth_cmpl->bd_addr, IOT_CONF_KEY_LMPVER,
+                                   lmp_ver);
+    DEVICE_IOT_CONFIG_ADDR_SET_INT(p_auth_cmpl->bd_addr, IOT_CONF_KEY_LMPSUBVER,
+                                   lmp_subver);
+  }
+
+  // save remote pair type to iot conf file
+  btif_iot_save_pair_type(p_auth_cmpl->bd_addr, is_ble, is_ssp);
+
+  device_iot_config_flush();
+}
diff --git a/system/btif/src/stack_manager.cc b/system/btif/src/stack_manager.cc
index 3217147e7d92cb7c0ebd7052762acd6c0b142d3e..f74583db1721c5819af78280b1ad78a9adff5eae 100644
--- a/system/btif/src/stack_manager.cc
+++ b/system/btif/src/stack_manager.cc
@@ -43,6 +43,7 @@
 #include "bta/sys/bta_sys.h"
 #include "btif_config.h"
 #include "btif_profile_queue.h"
+#include "device/include/device_iot_config.h"
 #include "internal_include/bt_target.h"
 #include "stack/include/gatt_api.h"
 #include "stack/include/l2c_api.h"
@@ -209,13 +210,13 @@ static bool get_stack_is_running() { return stack_is_running; }
 extern const module_t bt_utils_module;
 extern const module_t bte_logmsg_module;
 extern const module_t btif_config_module;
-extern const module_t bt_utils_module;
 extern const module_t gd_controller_module;
 extern const module_t gd_idle_module;
 extern const module_t gd_shim_module;
 extern const module_t interop_module;
 extern const module_t osi_module;
 extern const module_t stack_config_module;
+extern const module_t device_iot_config_module;
 
 struct module_lookup {
   const char* name;
@@ -232,6 +233,7 @@ const struct module_lookup module_table[] = {
     {INTEROP_MODULE, &interop_module},
     {OSI_MODULE, &osi_module},
     {STACK_CONFIG_MODULE, &stack_config_module},
+    {DEVICE_IOT_CONFIG_MODULE, &device_iot_config_module},
     {NULL, NULL},
 };
 
@@ -254,6 +256,7 @@ static void init_stack_internal(bluetooth::core::CoreInterface* interface) {
 
   module_management_start();
 
+  module_init(get_local_module(DEVICE_IOT_CONFIG_MODULE));
   module_init(get_local_module(OSI_MODULE));
   module_init(get_local_module(BT_UTILS_MODULE));
   module_start_up(get_local_module(GD_IDLE_MODULE));
@@ -384,6 +387,7 @@ static void event_shut_down_stack(ProfileStopCallback stopProfiles) {
   BTA_dm_on_hw_off();
 
   module_shut_down(get_local_module(BTIF_CONFIG_MODULE));
+  module_shut_down(get_local_module(DEVICE_IOT_CONFIG_MODULE));
 
   future_await(local_hack_future);
 
@@ -434,6 +438,8 @@ static void event_clean_up_stack(std::promise<void> promise,
   module_clean_up(get_local_module(INTEROP_MODULE));
 
   module_clean_up(get_local_module(BTIF_CONFIG_MODULE));
+  module_clean_up(get_local_module(DEVICE_IOT_CONFIG_MODULE));
+
   module_clean_up(get_local_module(BT_UTILS_MODULE));
   module_clean_up(get_local_module(OSI_MODULE));
   module_shut_down(get_local_module(GD_IDLE_MODULE));
diff --git a/system/device/Android.bp b/system/device/Android.bp
index 105dbe560d7ef5e2b93b2ccd95494eb5a6fc7738..53383da41d7185fb85c68e6646138772832ff6be 100644
--- a/system/device/Android.bp
+++ b/system/device/Android.bp
@@ -25,6 +25,8 @@ cc_library_static {
         "src/controller.cc",
         "src/esco_parameters.cc",
         "src/interop.cc",
+        "src/device_iot_config.cc",
+        "src/device_iot_config_int.cc",
     ],
     min_sdk_version: "Tiramisu"
 }
diff --git a/system/device/BUILD.gn b/system/device/BUILD.gn
index b60059cbdcc03ecc00fc79d71da10d18f8ee2dc8..c57c8ee60273c901fbc718f37dbb8e644a180727 100644
--- a/system/device/BUILD.gn
+++ b/system/device/BUILD.gn
@@ -19,6 +19,8 @@ static_library("device") {
     "src/controller.cc",
     "src/esco_parameters.cc",
     "src/interop.cc",
+    "src/device_iot_config.cc",
+    "src/device_iot_config_int.cc",
   ]
 
   include_dirs = [
diff --git a/system/device/include/device_iot_conf_defs.h b/system/device/include/device_iot_conf_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..76f41248be152fc7745a3f077f1ac9a13266479d
--- /dev/null
+++ b/system/device/include/device_iot_conf_defs.h
@@ -0,0 +1,109 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2018 The Linux Foundation
+ *
+ *  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
+
+#define IOT_CONF_KEY_SECTION_ADAPTER "Adapter"
+#define IOT_CONF_KEY_ADDRESS "Address"
+#define IOT_CONF_KEY_BT_ONOFF_COUNT "BTOnOffCount"
+
+#define IOT_CONF_KEY_REMOTE_NAME "Name"
+#define IOT_CONF_KEY_DEVCLASS "DevClass"
+#define IOT_CONF_KEY_DEVTYPE "DevType"
+#define IOT_CONF_KEY_ADDRTYPE "AddrType"
+#define IOT_CONF_KEY_MANUFACTURER "Manufacturer"
+#define IOT_CONF_KEY_LMPVER "LmpVer"
+#define IOT_CONF_KEY_LMPSUBVER "LmpSubVer"
+#define IOT_CONF_KEY_PAIRTYPE "PairType"
+#define IOT_CONF_KEY_LE_PAIRTYPE "LE_PairType"
+#define IOT_CONF_KEY_RT_SUPP_FEATURES "RemoteSupportedFeatures"
+#define IOT_CONF_KEY_RT_EXT_FEATURES "RemoteExtendedFeatures"
+#define IOT_CONF_KEY_LE_RT_FEATURES "LE_RemoteSupportedFeatures"
+#define IOT_CONF_KEY_RECORDED "Recorded"
+
+#define IOT_CONF_KEY_GAP_CONN_COUNT "ProfileGap_ConnectCount"
+#define IOT_CONF_KEY_GAP_CONN_FAIL_COUNT "ProfileGap_ConnectFailCount"
+#define IOT_CONF_KEY_GAP_DISC_COUNT "ProfileGap_DiscCount"
+#define IOT_CONF_KEY_GAP_DISC_AUTHFAIL_COUNT "ProfileGap_DiscAuthFailCount"
+#define IOT_CONF_KEY_GAP_DISC_CONNTIMEOUT_COUNT \
+  "ProfileGap_DiscConnTimeoutCount"
+#define IOT_CONF_KEY_A2DP_ROLE "ProfileA2dp_Role"
+#define IOT_CONF_KEY_A2DP_VERSION "ProfileA2dp_Version"
+#define IOT_CONF_KEY_A2DP_CODECTYPE "ProfileA2dp_CodecType"
+#define IOT_CONF_KEY_A2DP_CONN_COUNT "ProfileA2dp_ConnectCount"
+#define IOT_CONF_KEY_A2DP_CONN_FAIL_COUNT "ProfileA2dp_ConnectFailCount"
+#define IOT_CONF_KEY_HFP_ROLE "ProfileHfp_Role"
+#define IOT_CONF_KEY_HFP_VERSION "ProfileHfp_Version"
+#define IOT_CONF_KEY_HFP_CODECTYPE "ProfileHfp_CodecType"
+#define IOT_CONF_KEY_HFP_SLC_CONN_COUNT "ProfileHfp_SlcConnectCount"
+#define IOT_CONF_KEY_HFP_SLC_CONN_FAIL_COUNT "ProfileHfp_SlcConnectFailCount"
+#define IOT_CONF_KEY_HFP_SCO_CONN_COUNT "ProfileHfp_ScoConnectCount"
+#define IOT_CONF_KEY_HFP_SCO_CONN_FAIL_COUNT "ProfileHfp_ScoConnectFailCount"
+#define IOT_CONF_KEY_HFP_FEATURES "ProfileHfp_Features"
+#define IOT_CONF_KEY_AVRCP_CTRL_VERSION "ProfileAvrcp_ControllerVersion"
+#define IOT_CONF_KEY_AVRCP_TG_VERSION "ProfileAvrcp_TargetVersion"
+#define IOT_CONF_KEY_AVRCP_CONN_COUNT "ProfileAvrcp_ConnectCount"
+#define IOT_CONF_KEY_AVRCP_CONN_FAIL_COUNT "ProfileAvrcp_ConnectFailCount"
+#define IOT_CONF_KEY_AVRCP_FEATURES "ProfileAvrcp_Features"
+#define IOT_CONF_KEY_HID_ROLE "ProfileHid_Role"
+#define IOT_CONF_KEY_HID_VERSION "ProfileHid_Version"
+#define IOT_CONF_KEY_HID_CONN_COUNT "ProfileHid_ConnectCount"
+#define IOT_CONF_KEY_HID_CONN_FAIL_COUNT "ProfileHid_ConnectFailCount"
+#define IOT_CONF_KEY_PBAP_ROLE "ProfilePbap_Role"
+#define IOT_CONF_KEY_PBAP_VERSION "ProfilePbap_Version"
+#define IOT_CONF_KEY_PBAP_CONN_COUNT "ProfilePbap_ConnectCount"
+#define IOT_CONF_KEY_PBAP_CONN_FAIL_COUNT "ProfilePbap_ConnectFailCount"
+#define IOT_CONF_KEY_MAP_ROLE "ProfileMap_Role"
+#define IOT_CONF_KEY_MAP_VERSION "ProfileMap_Version"
+#define IOT_CONF_KEY_MAP_CONN_COUNT "ProfileMap_ConnectCount"
+#define IOT_CONF_KEY_MAP_CONN_FAIL_COUNT "ProfileMap_ConnectFailCount"
+
+#define IOT_CONF_VAL_PAIR_TYPE_LEGACY 0
+#define IOT_CONF_VAL_PAIR_TYPE_SSP 1
+#define IOT_CONF_VAL_LE_PAIRTYPE_LEGACY 0
+#define IOT_CONF_VAL_LE_PAIRTYPE_SECURE 1
+#define IOT_CONF_VAL_RECORDED_DEFAULT 0
+
+#define IOT_CONF_VAL_A2DP_ROLE_SINK 0
+#define IOT_CONF_VAL_A2DP_ROLE_SOURCE 1
+#define IOT_CONF_VAL_A2DP_CODECTYPE_SBC 0x01
+#define IOT_CONF_VAL_A2DP_CODECTYPE_APTX 0x02
+#define IOT_CONF_VAL_A2DP_CODECTYPE_APTXHD 0x04
+#define IOT_CONF_VAL_A2DP_CODECTYPE_AAC 0x08
+#define IOT_CONF_VAL_A2DP_CODECTYPE_LDAC 0x10
+#define IOT_CONF_VAL_A2DP_CODECTYPE_APTXADAPTIVE 0x20
+#define IOT_CONF_VAL_A2DP_CODECTYPE_APTXTWS 0x40
+
+#define IOT_CONF_VAL_HFP_ROLE_CLIENT 0
+#define IOT_CONF_VAL_HFP_ROLE_AG 1
+#define IOT_CONF_VAL_HFP_CODECTYPE_CVSD 0x01
+#define IOT_CONF_VAL_HFP_CODECTYPE_CVSDMSBC 0x02
+
+#define IOT_CONF_VAL_HID_ROLE_HOST 0
+#define IOT_CONF_VAL_HID_ROLE_DEVICE 1
+
+#define IOT_CONF_VAL_PBAP_ROLE_CLIENT 0
+#define IOT_CONF_VAL_PBAP_ROLE_SERVER 1
+
+#define IOT_CONF_VAL_MAP_ROLE_CLIENT 0
+#define IOT_CONF_VAL_MAP_ROLE_SERVER 1
+
+#define IOT_CONF_BYTE_NUM_1 1
+#define IOT_CONF_BYTE_NUM_2 2
+#define IOT_CONF_BYTE_NUM_3 3
+#define IOT_CONF_BYTE_NUM_4 4
\ No newline at end of file
diff --git a/system/device/include/device_iot_config.h b/system/device/include/device_iot_config.h
new file mode 100644
index 0000000000000000000000000000000000000000..df2f94c31a50c701c0ad357571a69e13091d0fc7
--- /dev/null
+++ b/system/device/include/device_iot_config.h
@@ -0,0 +1,105 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2014 Google, Inc.
+ *  Copyright (C) 2018 The Linux Foundation
+ *
+ *  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 <stdbool.h>
+#include <stddef.h>
+
+#include "bt_target.h"
+#include "bt_types.h"
+#include "device_iot_conf_defs.h"
+#include "raw_address.h"
+
+static const char DEVICE_IOT_CONFIG_MODULE[] = "device_iot_config_module";
+
+bool device_iot_config_get_int(const std::string& section,
+                               const std::string& key, int& value);
+bool device_iot_config_set_int(const std::string& section,
+                               const std::string& key, int value);
+bool device_iot_config_int_add_one(const std::string& section,
+                                   const std::string& key);
+bool device_iot_config_get_hex(const std::string& section,
+                               const std::string& key, int& value);
+bool device_iot_config_set_hex(const std::string& section,
+                               const std::string& key, int value, int byte_num);
+bool device_iot_config_set_hex_if_greater(const std::string& section,
+                                          const std::string& key, int value,
+                                          int byte_num);
+bool device_iot_config_get_str(const std::string& section,
+                               const std::string& key, char* value,
+                               int* size_bytes);
+bool device_iot_config_set_str(const std::string& section,
+                               const std::string& key,
+                               const std::string& value);
+bool device_iot_config_get_bin(const std::string& section,
+                               const std::string& key, uint8_t* value,
+                               size_t* length);
+bool device_iot_config_set_bin(const std::string& section,
+                               const std::string& key, const uint8_t* value,
+                               size_t length);
+size_t device_iot_config_get_bin_length(const std::string& section,
+                                        const std::string& key);
+
+#define DEVICE_IOT_CONFIG_ADDR(method, addr, ...) \
+  device_iot_config_##method((addr).ToString(), ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_GET_INT(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(get_int, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_SET_INT(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(set_int, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(int_add_one, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_GET_HEX(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(get_hex, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_SET_HEX(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(set_hex, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_SET_HEX_IF_GREATER(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(set_hex_if_greater, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_GET_STR(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(set_gtr, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_SET_STR(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(set_str, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_GET_BIN(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(get_bin, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_SET_BIN(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(set_bin, addr, ##__VA_ARGS__)
+
+#define DEVICE_IOT_CONFIG_ADDR_GET_BIN_LENGTH(addr, ...) \
+  DEVICE_IOT_CONFIG_ADDR(get_bin, addr, ##__VA_ARGS__)
+
+bool device_iot_config_has_section(const std::string& section);
+bool device_iot_config_exist(const std::string& section,
+                             const std::string& key);
+bool device_iot_config_remove(const std::string& section,
+                              const std::string& key);
+
+void device_iot_config_flush(void);
+bool device_iot_config_clear(void);
+
+void device_debug_iot_config_dump(int fd);
diff --git a/system/device/src/device_iot_config.cc b/system/device/src/device_iot_config.cc
new file mode 100644
index 0000000000000000000000000000000000000000..8693d59bb5196240db139150420e9fe3fba310ea
--- /dev/null
+++ b/system/device/src/device_iot_config.cc
@@ -0,0 +1,379 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2014 Google, Inc.
+ *  Copyright (C) 2018 The Linux Foundation
+ *
+ *  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 "bt_target.h"
+
+#define LOG_TAG "device_iot_config"
+#include <base/logging.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <mutex>
+#include <string>
+
+#include "bt_types.h"
+#include "btcore/include/module.h"
+#include "btif/include/btif_api.h"
+#include "btif/include/btif_util.h"
+#include "device/include/device_iot_config.h"
+#include "device_iot_config_int.h"
+#include "osi/include/alarm.h"
+#include "osi/include/allocator.h"
+#include "osi/include/compat.h"
+#include "osi/include/config.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+
+enum ConfigSource device_iot_config_source = NOT_LOADED;
+
+int device_iot_config_devices_loaded = -1;
+char device_iot_config_time_created[TIME_STRING_LENGTH];
+
+std::mutex config_lock;  // protects operations on |config|.
+std::unique_ptr<config_t> config;
+alarm_t* config_timer;
+bool iot_logging_enabled = false;
+
+bool device_iot_config_has_section(const std::string& section) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  return config_has_section(*config, section);
+}
+
+bool device_iot_config_exist(const std::string& section,
+                             const std::string& key) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  return config_has_key(*config, section, key);
+}
+
+bool device_iot_config_get_int(const std::string& section,
+                               const std::string& key, int& value) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  bool ret = config_has_key(*config, section, key);
+  if (ret) value = config_get_int(*config, section, key, value);
+
+  return ret;
+}
+
+bool device_iot_config_set_int(const std::string& section,
+                               const std::string& key, int value) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  char value_str[32] = {0};
+  snprintf(value_str, sizeof(value_str), "%d", value);
+  if (device_iot_config_has_key_value(section, key, value_str)) return true;
+
+  config_set_string(config.get(), section, key, value_str);
+  device_iot_config_save_async();
+
+  return true;
+}
+
+bool device_iot_config_int_add_one(const std::string& section,
+                                   const std::string& key) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  int result = 0;
+  std::unique_lock<std::mutex> lock(config_lock);
+  result = config_get_int(*config, section, key, result);
+  if (result >= 0) {
+    result += 1;
+  } else {
+    result = 0;
+  }
+  config_set_int(config.get(), section, key, result);
+  device_iot_config_save_async();
+
+  return true;
+}
+
+bool device_iot_config_get_hex(const std::string& section,
+                               const std::string& key, int& value) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  const std::string* stored_value =
+      config_get_string(*config, section, key, NULL);
+  if (!stored_value) return false;
+
+  errno = 0;
+  char* endptr = nullptr;
+  int result = strtoul(stored_value->c_str(), &endptr, 16);
+  if (stored_value->c_str() == endptr) return false;
+  if (endptr == nullptr || endptr[0] != '\0') return false;
+  if (errno) return false;
+
+  value = result;
+  return true;
+}
+
+bool device_iot_config_set_hex(const std::string& section,
+                               const std::string& key, int value,
+                               int byte_num) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  char value_str[32] = {0};
+  if (byte_num == 1)
+    snprintf(value_str, sizeof(value_str), "%02x", value);
+  else if (byte_num == 2)
+    snprintf(value_str, sizeof(value_str), "%04x", value);
+  else if (byte_num == 3)
+    snprintf(value_str, sizeof(value_str), "%06x", value);
+  else if (byte_num == 4)
+    snprintf(value_str, sizeof(value_str), "%08x", value);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  if (device_iot_config_has_key_value(section, key, value_str)) return true;
+
+  config_set_string(config.get(), section, key, value_str);
+  device_iot_config_save_async();
+
+  return true;
+}
+
+bool device_iot_config_set_hex_if_greater(const std::string& section,
+                                          const std::string& key, int value,
+                                          int byte_num) {
+  CHECK_LOGGING_ENABLED(false);
+
+  int stored_value = 0;
+  bool ret = device_iot_config_get_hex(section, key, stored_value);
+  if (ret && stored_value >= value) return true;
+
+  return device_iot_config_set_hex(section, key, value, byte_num);
+}
+
+bool device_iot_config_get_str(const std::string& section,
+                               const std::string& key, char* value,
+                               int* size_bytes) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+  CHECK(value != NULL);
+  CHECK(size_bytes != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  const std::string* stored_value =
+      config_get_string(*config, section, key, NULL);
+
+  if (!stored_value) return false;
+
+  strlcpy(value, stored_value->c_str(), *size_bytes);
+  *size_bytes = strlen(value) + 1;
+
+  return true;
+}
+
+bool device_iot_config_set_str(const std::string& section,
+                               const std::string& key,
+                               const std::string& value) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  if (device_iot_config_has_key_value(section, key, value)) return true;
+
+  config_set_string(config.get(), section, key, value);
+  device_iot_config_save_async();
+
+  return true;
+}
+
+bool device_iot_config_get_bin(const std::string& section,
+                               const std::string& key, uint8_t* value,
+                               size_t* length) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+  CHECK(value != NULL);
+  CHECK(length != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  const std::string* value_string =
+      config_get_string(*config, section, key, NULL);
+
+  if (!value_string) return false;
+
+  const char* value_str = value_string->c_str();
+
+  size_t value_len = strlen(value_str);
+  if ((value_len % 2) != 0 || *length < (value_len / 2)) return false;
+
+  for (size_t i = 0; i < value_len; ++i)
+    if (!isxdigit(value_str[i])) return false;
+
+  for (*length = 0; *value_str; value_str += 2, *length += 1) {
+    errno = 0;
+    char* endptr = nullptr;
+    value[*length] = strtoul(value_str, &endptr, 16);
+    if (value_str == endptr) return false;
+    if (*endptr) return false;
+    if (errno) return false;
+  }
+
+  return true;
+}
+
+size_t device_iot_config_get_bin_length(const std::string& section,
+                                        const std::string& key) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  const std::string* value_str = config_get_string(*config, section, key, NULL);
+
+  if (!value_str) return 0;
+
+  size_t value_len = strlen(value_str->c_str());
+  return ((value_len % 2) != 0) ? 0 : (value_len / 2);
+}
+
+bool device_iot_config_set_bin(const std::string& section,
+                               const std::string& key, const uint8_t* value,
+                               size_t length) {
+  CHECK_LOGGING_ENABLED(false);
+
+  const char* lookup = "0123456789abcdef";
+
+  CHECK(config != NULL);
+
+  LOG_VERBOSE("Key = %s", key.c_str());
+  if (length > 0) CHECK(value != NULL);
+
+  char* str = (char*)osi_calloc(length * 2 + 1);
+  if (str == NULL) {
+    LOG_ERROR("Unable to allocate a str.");
+    return false;
+  }
+
+  for (size_t i = 0; i < length; ++i) {
+    str[(i * 2) + 0] = lookup[(value[i] >> 4) & 0x0F];
+    str[(i * 2) + 1] = lookup[value[i] & 0x0F];
+  }
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  if (device_iot_config_has_key_value(section, key, str)) {
+    osi_free(str);
+    return true;
+  }
+
+  config_set_string(config.get(), section, key, str);
+  device_iot_config_save_async();
+
+  osi_free(str);
+  return true;
+}
+
+bool device_iot_config_remove(const std::string& section,
+                              const std::string& key) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  return config_remove_key(config.get(), section, key);
+}
+
+void device_iot_config_flush(void) {
+  CHECK_LOGGING_ENABLED((void)0);
+
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  int event = alarm_is_scheduled(config_timer) ? IOT_CONFIG_SAVE_TIMER_FIRED_EVT
+                                               : IOT_CONFIG_FLUSH_EVT;
+  LOG_VERBOSE("evt=%d", event);
+  alarm_cancel(config_timer);
+  device_iot_config_write(event, NULL);
+}
+
+bool device_iot_config_clear(void) {
+  CHECK_LOGGING_ENABLED(false);
+
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  LOG_INFO("");
+  alarm_cancel(config_timer);
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  config.reset();
+
+  config = config_new_empty();
+  if (config == NULL) {
+    return false;
+  }
+
+  bool ret = config_save(*config, IOT_CONFIG_FILE_PATH);
+  device_iot_config_source = RESET;
+  return ret;
+}
+
+void device_debug_iot_config_dump(int fd) {
+  CHECK_LOGGING_ENABLED((void)0);
+
+  dprintf(fd, "\nBluetooth Iot Config:\n");
+
+  dprintf(fd, "  Config Source: ");
+  switch (device_iot_config_source) {
+    case NOT_LOADED:
+      dprintf(fd, "Not loaded\n");
+      break;
+    case ORIGINAL:
+      dprintf(fd, "Original file\n");
+      break;
+    case BACKUP:
+      dprintf(fd, "Backup file\n");
+      break;
+    case NEW_FILE:
+      dprintf(fd, "New file\n");
+      break;
+    case RESET:
+      dprintf(fd, "Reset file\n");
+      break;
+  }
+
+  dprintf(fd, "  Devices loaded: %d\n", device_iot_config_devices_loaded);
+  dprintf(fd, "  File created/tagged: %s\n", device_iot_config_time_created);
+}
diff --git a/system/device/src/device_iot_config_int.cc b/system/device/src/device_iot_config_int.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e8f57b83f101e88fe31e61d7cc9903d8c9175d68
--- /dev/null
+++ b/system/device/src/device_iot_config_int.cc
@@ -0,0 +1,331 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2014 Google, Inc.
+ *  Copyright (C) 2018 The Linux Foundation
+ *
+ *  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.
+ *
+ ******************************************************************************/
+
+#define LOG_TAG "device_iot_config"
+#include "device_iot_config_int.h"
+
+#include <base/logging.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <mutex>
+#include <string>
+
+#include "bt_types.h"
+#include "btcore/include/module.h"
+#include "btif/include/btif_api.h"
+#include "btif/include/btif_util.h"
+#include "common/init_flags.h"
+#include "device/include/device_iot_config.h"
+#include "osi/include/alarm.h"
+#include "osi/include/allocator.h"
+#include "osi/include/compat.h"
+#include "osi/include/config.h"
+#include "osi/include/log.h"
+#include "osi/include/osi.h"
+#include "osi/include/properties.h"
+
+extern enum ConfigSource device_iot_config_source;
+
+extern int device_iot_config_devices_loaded;
+extern char device_iot_config_time_created[TIME_STRING_LENGTH];
+
+extern std::mutex config_lock;  // protects operations on |config|.
+extern std::unique_ptr<config_t> config;
+extern alarm_t* config_timer;
+extern bool iot_logging_enabled;
+
+static void cleanup() {
+  alarm_free(config_timer);
+  config.reset();
+  config_timer = NULL;
+  config = NULL;
+  device_iot_config_source = NOT_LOADED;
+}
+
+// Module lifecycle functions
+future_t* device_iot_config_module_init(void) {
+  LOG_INFO("");
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  iot_logging_enabled = osi_property_get_bool(PROPERTY_ENABLE_LOGGING, false);
+  if (!iot_logging_enabled) {
+    device_iot_config_delete_files();
+    return future_new_immediate(FUTURE_SUCCESS);
+  }
+
+  if (device_iot_config_is_factory_reset()) {
+    device_iot_config_delete_files();
+  }
+
+  config = config_new(IOT_CONFIG_FILE_PATH);
+  device_iot_config_source = ORIGINAL;
+  if (!config) {
+    LOG_WARN("Unable to load config file: %s; using backup.",
+             IOT_CONFIG_FILE_PATH);
+    config = config_new(IOT_CONFIG_BACKUP_PATH);
+    device_iot_config_source = BACKUP;
+  }
+
+  if (!config) {
+    LOG_ERROR("Unable to load bak file; creating empty config.");
+    config = config_new_empty();
+    device_iot_config_source = NEW_FILE;
+  }
+
+  if (!config) {
+    LOG_ERROR("Unable to allocate a config object.");
+    cleanup();
+    return future_new_immediate(FUTURE_FAIL);
+  }
+
+  int version;
+  if (device_iot_config_source == NEW_FILE) {
+    version = DEVICE_IOT_INFO_CURRENT_VERSION;
+    config_set_int(config.get(), INFO_SECTION, VERSION_KEY, version);
+  } else {
+    version = config_get_int(*config, INFO_SECTION, VERSION_KEY, -1);
+    if (version == -1) {
+      version = DEVICE_IOT_INFO_FIRST_VERSION;
+      config_set_int(config.get(), INFO_SECTION, VERSION_KEY, version);
+    }
+  }
+
+  if (version != DEVICE_IOT_INFO_CURRENT_VERSION) {
+    LOG_INFO("Version in file is %d, CURRENT_VERSION is %d ", version,
+             DEVICE_IOT_INFO_CURRENT_VERSION);
+    remove(IOT_CONFIG_FILE_PATH);
+    remove(IOT_CONFIG_BACKUP_PATH);
+    config.reset();
+    config = config_new_empty();
+    if (!config) {
+      LOG_ERROR("Unable to allocate a config object.");
+      cleanup();
+      return future_new_immediate(FUTURE_FAIL);
+    }
+    config_set_int(config.get(), INFO_SECTION, VERSION_KEY,
+                   DEVICE_IOT_INFO_CURRENT_VERSION);
+    device_iot_config_source = NEW_FILE;
+  }
+
+  device_iot_config_devices_loaded = device_iot_config_get_device_num(*config);
+  LOG_INFO("Devices loaded %d", device_iot_config_devices_loaded);
+
+  // Read or set config file creation timestamp
+  const std::string* time_str =
+      config_get_string(*config, INFO_SECTION, FILE_CREATED_TIMESTAMP, NULL);
+  if (time_str != NULL) {
+    strncpy(device_iot_config_time_created, time_str->c_str(),
+            TIME_STRING_LENGTH);
+  } else {
+    // Read or set config file creation timestamp
+    time_t current_time = time(NULL);
+    struct tm* time_created = localtime(&current_time);
+    if (time_created) {
+      strftime(device_iot_config_time_created, TIME_STRING_LENGTH,
+               TIME_STRING_FORMAT, time_created);
+      config_set_string(config.get(), INFO_SECTION, FILE_CREATED_TIMESTAMP,
+                        std::string(device_iot_config_time_created));
+    }
+  }
+
+  // TODO: use a non-wake alarm for this once we have
+  // API support for it. There's no need to wake the system to
+  // write back to disk.
+  config_timer = alarm_new("btif.iot.config");
+  if (!config_timer) {
+    LOG_ERROR("Unable to create alarm.");
+    cleanup();
+    return future_new_immediate(FUTURE_FAIL);
+  }
+
+  LOG_EVENT_INT(BT_IOT_CONFIG_SOURCE_TAG_NUM, device_iot_config_source);
+
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+future_t* device_iot_config_module_start_up(void) {
+  LOG_INFO("");
+  device_iot_config_int_add_one(IOT_CONF_KEY_SECTION_ADAPTER,
+                                IOT_CONF_KEY_BT_ONOFF_COUNT);
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+future_t* device_iot_config_module_shut_down(void) {
+  LOG_INFO("");
+  device_iot_config_flush();
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+future_t* device_iot_config_module_clean_up(void) {
+  LOG_INFO("");
+  if (config_timer != NULL && alarm_is_scheduled(config_timer))
+    device_iot_config_flush();
+
+  alarm_free(config_timer);
+  config_timer = NULL;
+
+  std::unique_lock<std::mutex> lock(config_lock);
+  config.reset();
+  config = NULL;
+  return future_new_immediate(FUTURE_SUCCESS);
+}
+
+EXPORT_SYMBOL module_t device_iot_config_module = {
+    .name = DEVICE_IOT_CONFIG_MODULE,
+    .init = device_iot_config_module_init,
+    .start_up = device_iot_config_module_start_up,
+    .shut_down = device_iot_config_module_shut_down,
+    .clean_up = device_iot_config_module_clean_up};
+
+void device_iot_config_write(uint16_t event, UNUSED_ATTR char* p_param) {
+  CHECK_LOGGING_ENABLED((void)0);
+
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  LOG_INFO("evt=%d", event);
+  std::unique_lock<std::mutex> lock(config_lock);
+  if (event == IOT_CONFIG_SAVE_TIMER_FIRED_EVT) {
+    device_iot_config_set_modified_time();
+  }
+
+  rename(IOT_CONFIG_FILE_PATH, IOT_CONFIG_BACKUP_PATH);
+  device_iot_config_restrict_device_num(*config);
+  device_iot_config_sections_sort_by_entry_key(*config,
+                                               device_iot_config_compare_key);
+  config_save(*config, IOT_CONFIG_FILE_PATH);
+}
+
+void device_iot_config_sections_sort_by_entry_key(config_t& config,
+                                                  compare_func comp) {
+  for (auto& entry : config.sections) {
+    entry.entries.sort(comp);
+  }
+}
+
+bool device_iot_config_has_key_value(const std::string& section,
+                                     const std::string& key,
+                                     const std::string& value_str) {
+  CHECK(config != NULL);
+
+  const std::string* stored_value =
+      config_get_string(*config, section, key, NULL);
+
+  if (!stored_value || value_str.compare(*stored_value) != 0) return false;
+
+  return true;
+}
+
+void device_iot_config_save_async(void) {
+  CHECK(config != NULL);
+  CHECK(config_timer != NULL);
+
+  LOG_VERBOSE("");
+  alarm_set(config_timer, CONFIG_SETTLE_PERIOD_MS,
+            device_iot_config_timer_save_cb, NULL);
+}
+
+int device_iot_config_get_device_num(const config_t& conf) {
+  CHECK_LOGGING_ENABLED(0);
+
+  int devices = 0;
+
+  for (const auto& entry : conf.sections) {
+    if (RawAddress::IsValidAddress(entry.name)) {
+      devices++;
+    }
+  }
+  return devices;
+}
+
+void device_iot_config_restrict_device_num(config_t& config) {
+  int curr_num = device_iot_config_get_device_num(config);
+  int removed_devices = 0;
+  int need_remove_devices_num;
+
+  if (curr_num <= DEVICES_MAX_NUM_IN_IOT_INFO_FILE) {
+    return;
+  }
+
+  need_remove_devices_num =
+      curr_num - DEVICES_MAX_NUM_IN_IOT_INFO_FILE + DEVICES_NUM_MARGIN;
+  LOG_INFO("curr_num=%d, need_remove_num=%d", curr_num,
+           need_remove_devices_num);
+
+  std::list<section_t>::iterator i = config.sections.begin();
+  while (i != config.sections.end()) {
+    if (!RawAddress::IsValidAddress(i->name)) {
+      ++i;
+      continue;
+    }
+
+    i = config.sections.erase(i);
+    if (++removed_devices >= need_remove_devices_num) {
+      break;
+    }
+  }
+}
+
+bool device_iot_config_compare_key(const entry_t& first,
+                                   const entry_t& second) {
+  bool first_is_profile_key = strncasecmp(first.key.c_str(), "Profile", 7) == 0;
+  bool second_is_profile_key =
+      strncasecmp(second.key.c_str(), "Profile", 7) == 0;
+  if (!first_is_profile_key && !second_is_profile_key) {
+    return true;
+  } else if (first_is_profile_key && second_is_profile_key) {
+    return strcasecmp(first.key.c_str(), second.key.c_str()) <= 0;
+  } else {
+    return !first_is_profile_key;
+  }
+}
+
+void device_iot_config_timer_save_cb(UNUSED_ATTR void* data) {
+  // Moving file I/O to btif context instead of timer callback because
+  // it usually takes a lot of time to be completed, introducing
+  // delays during A2DP playback causing blips or choppiness.
+  LOG_VERBOSE("");
+  btif_transfer_context(device_iot_config_write,
+                        IOT_CONFIG_SAVE_TIMER_FIRED_EVT, NULL, 0, NULL);
+}
+
+void device_iot_config_set_modified_time() {
+  time_t current_time = time(NULL);
+  struct tm* time_modified = localtime(&current_time);
+  char device_iot_config_time_modified[TIME_STRING_LENGTH];
+  if (time_modified) {
+    strftime(device_iot_config_time_modified, TIME_STRING_LENGTH,
+             TIME_STRING_FORMAT, time_modified);
+    config_set_string(config.get(), INFO_SECTION, FILE_MODIFIED_TIMESTAMP,
+                      device_iot_config_time_modified);
+  }
+}
+
+bool device_iot_config_is_factory_reset(void) {
+  return osi_property_get_bool(PROPERTY_FACTORY_RESET, false);
+}
+
+void device_iot_config_delete_files(void) {
+  remove(IOT_CONFIG_FILE_PATH);
+  remove(IOT_CONFIG_BACKUP_PATH);
+}
\ No newline at end of file
diff --git a/system/device/src/device_iot_config_int.h b/system/device/src/device_iot_config_int.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc897703ed6c63315298381b5ee44c943030968e
--- /dev/null
+++ b/system/device/src/device_iot_config_int.h
@@ -0,0 +1,97 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2014 Google, Inc.
+ *  Copyright (C) 2018 The Linux Foundation
+ *
+ *  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 "osi/include/config.h"
+#include "osi/include/osi.h"
+
+#define BT_IOT_CONFIG_SOURCE_TAG_NUM 1010003
+
+#define PROPERTY_ENABLE_LOGGING \
+  "persist.bluetooth.device_iot_config.enablelogging"
+#define PROPERTY_FACTORY_RESET "persist.bluetooth.factoryreset"
+
+#define INFO_SECTION "Info"
+#define VERSION_KEY "Version"
+#define FILE_CREATED_TIMESTAMP "TimeCreated"
+#define FILE_MODIFIED_TIMESTAMP "TimeModified"
+#define TIME_STRING_LENGTH sizeof("YYYY-MM-DD HH:MM:SS")
+static const char* TIME_STRING_FORMAT = "%Y-%m-%d %H:%M:%S";
+
+#ifndef DEVICES_MAX_NUM_IN_IOT_INFO_FILE
+#define DEVICES_MAX_NUM_IN_IOT_INFO_FILE 40
+#endif
+#define DEVICES_NUM_MARGIN 5
+
+#if (DEVICES_MAX_NUM_IN_IOT_INFO_FILE < DEVICES_NUM_MARGIN)
+#undef DEVICES_MAX_NUM_IN_IOT_INFO_FILE
+#define DEVICES_MAX_NUM_IN_IOT_INFO_FILE DEVICES_NUM_MARGIN
+#endif
+
+#define DEVICE_IOT_INFO_CURRENT_VERSION 1
+#define DEVICE_IOT_INFO_FIRST_VERSION 1
+
+#define IOT_CONFIG_FLUSH_EVT 0
+#define IOT_CONFIG_SAVE_TIMER_FIRED_EVT 1
+
+#if defined(OS_GENERIC)
+static const char* IOT_CONFIG_FILE_PATH = "bt_remote_dev_info.conf";
+static const char* IOT_CONFIG_BACKUP_PATH = "bt_remote_dev_info.bak";
+#else   // !defined(OS_GENERIC)
+static const char* IOT_CONFIG_FILE_PATH =
+    "/data/misc/bluedroid/bt_remote_dev_info.conf";
+static const char* IOT_CONFIG_BACKUP_PATH =
+    "/data/misc/bluedroid/bt_remote_dev_info.bak";
+#endif  // defined(OS_GENERIC)
+static const uint64_t CONFIG_SETTLE_PERIOD_MS = 12000;
+
+enum ConfigSource { NOT_LOADED, ORIGINAL, BACKUP, NEW_FILE, RESET };
+
+#define CHECK_LOGGING_ENABLED(return_value)          \
+  do {                                               \
+    if (!iot_logging_enabled) return (return_value); \
+  } while (0)
+
+struct config_t;
+struct future_t;
+
+typedef bool (*compare_func)(const entry_t& first, const entry_t& second);
+
+// config_lock is used by the callee in the following methods
+future_t* device_iot_config_module_init(void);
+future_t* device_iot_config_module_start_up(void);
+future_t* device_iot_config_module_shut_down(void);
+future_t* device_iot_config_module_clean_up(void);
+void device_iot_config_write(uint16_t event, char* p_param);
+
+// config_lock is used by the caller of the following methods
+void device_iot_config_sections_sort_by_entry_key(config_t& config,
+                                                  compare_func comp);
+bool device_iot_config_has_key_value(const std::string& section,
+                                     const std::string& key,
+                                     const std::string& value_str);
+void device_iot_config_save_async(void);
+int device_iot_config_get_device_num(const config_t& config);
+void device_iot_config_restrict_device_num(config_t& config);
+bool device_iot_config_compare_key(const entry_t& first, const entry_t& second);
+void device_iot_config_timer_save_cb(UNUSED_ATTR void* data);
+void device_iot_config_set_modified_time();
+bool device_iot_config_is_factory_reset(void);
+void device_iot_config_delete_files(void);
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index ed8d3e4cedf92a7af9d451954678015848590c65..a2586142c10316a60cc9e3a7acca9c5aa5e7afee 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -65,6 +65,7 @@ cc_library_static {
         "packages/modules/Bluetooth/system/udrv/include",
         "packages/modules/Bluetooth/system/bta/include",
         "packages/modules/Bluetooth/system/bta/sys",
+        "packages/modules/Bluetooth/system/device/include",
         "packages/modules/Bluetooth/system/utils/include",
         "packages/modules/Bluetooth/system/gd/rust/shim",
     ],
@@ -197,6 +198,7 @@ cc_library_static {
         "btm/btm_dev.cc",
         "btm/btm_devctl.cc",
         "btm/btm_inq.cc",
+        "btm/btm_iot_config.cc",
         "btm/btm_main.cc",
         "acl/btm_pm.cc",
         "btm/btm_sco.cc",
@@ -884,6 +886,7 @@ cc_test {
     ],
     include_dirs: [
         "packages/modules/Bluetooth/system",
+        "packages/modules/Bluetooth/system/device/include",
         "packages/modules/Bluetooth/system/gd",
         "packages/modules/Bluetooth/system/vnd/ble",
     ],
@@ -933,6 +936,7 @@ cc_test {
         "btm/btm_dev.cc",
         "btm/btm_devctl.cc",
         "btm/btm_inq.cc",
+        "btm/btm_iot_config.cc",
         "btm/btm_iso.cc",
         "btm/btm_main.cc",
         "btm/btm_sco.cc",
@@ -1329,11 +1333,13 @@ cc_test {
         ":TestMockStackSdp",
         ":TestMockStackSmp",
         "acl/*.cc",
+        "btm/btm_iot_config.cc",
         "test/stack_acl_test.cc",
     ],
     static_libs: [
         "libbt-common",
         "libbt-protos-lite",
+        "libbtdevice",
         "libflatbuffers-cpp",
         "libgmock",
         "liblog",
diff --git a/system/stack/BUILD.gn b/system/stack/BUILD.gn
index b9a47e8db952764ed2210426ea9f17c47a4d58a3..f9a0e2ca84aa26149bb135e022d6665e157beb2c 100644
--- a/system/stack/BUILD.gn
+++ b/system/stack/BUILD.gn
@@ -116,6 +116,7 @@ source_set("stack") {
     "btm/btm_dev.cc",
     "btm/btm_devctl.cc",
     "btm/btm_inq.cc",
+    "btm/btm_iot_config.cc",
     "btm/btm_iso.cc",
     "btm/btm_main.cc",
     "btm/btm_scn.cc",
diff --git a/system/stack/a2dp/a2dp_codec_config.cc b/system/stack/a2dp/a2dp_codec_config.cc
index 34503c8cfa66fa679354af6a4779533224f00a05..6356d11aa491b3a105072856cd7b941740fff669 100644
--- a/system/stack/a2dp/a2dp_codec_config.cc
+++ b/system/stack/a2dp/a2dp_codec_config.cc
@@ -37,6 +37,7 @@
 #endif
 
 #include "bta/av/bta_av_int.h"
+#include "device/include/device_iot_config.h"
 #include "osi/include/log.h"
 #include "osi/include/properties.h"
 #include "stack/include/bt_hdr.h"
@@ -522,6 +523,44 @@ void A2dpCodecConfig::debug_codec_dump(int fd) {
   dprintf(fd, "  Local capability: %s\n", result.c_str());
 }
 
+int A2DP_IotGetPeerSinkCodecType(const uint8_t* p_codec_info) {
+  int peer_codec_type = 0;
+  tA2DP_CODEC_TYPE codec_type = A2DP_GetCodecType(p_codec_info);
+  LOG_VERBOSE("%s: codec_type = 0x%x", __func__, codec_type);
+  switch (codec_type) {
+    case A2DP_MEDIA_CT_SBC:
+      peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_SBC;
+      break;
+#if !defined(EXCLUDE_NONSTANDARD_CODECS)
+    case A2DP_MEDIA_CT_NON_A2DP: {
+      uint16_t codec_id = A2DP_VendorCodecGetCodecId(p_codec_info);
+      uint32_t vendor_id = A2DP_VendorCodecGetVendorId(p_codec_info);
+
+      LOG_VERBOSE("%s codec_id = %d", __func__, codec_id);
+      LOG_VERBOSE("%s vendor_id = %x", __func__, vendor_id);
+
+      if (codec_id == A2DP_APTX_CODEC_ID_BLUETOOTH &&
+          vendor_id == A2DP_APTX_VENDOR_ID) {
+        peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_APTX;
+      } else if (codec_id == A2DP_APTX_HD_CODEC_ID_BLUETOOTH &&
+                 vendor_id == A2DP_APTX_HD_VENDOR_ID) {
+        peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_APTXHD;
+      } else if (codec_id == A2DP_LDAC_CODEC_ID &&
+                 vendor_id == A2DP_LDAC_VENDOR_ID) {
+        peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_LDAC;
+      }
+      break;
+    }
+    case A2DP_MEDIA_CT_AAC:
+      peer_codec_type = IOT_CONF_VAL_A2DP_CODECTYPE_AAC;
+      break;
+#endif
+    default:
+      break;
+  }
+  return peer_codec_type;
+}
+
 //
 // Compares two codecs |lhs| and |rhs| based on their priority.
 // Returns true if |lhs| has higher priority (larger priority value).
diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc
index 7eaa790566f038531b5ec0daa2781ab00440e924..a35a931a836c3f5c5c10d0f692d2527d0cee6c92 100644
--- a/system/stack/acl/btm_acl.cc
+++ b/system/stack/acl/btm_acl.cc
@@ -42,6 +42,7 @@
 #include "btif/include/btif_acl.h"
 #include "common/metrics.h"
 #include "device/include/controller.h"
+#include "device/include/device_iot_config.h"
 #include "device/include/interop.h"
 #include "include/l2cap_hci_link_interface.h"
 #include "main/shim/acl_api.h"
@@ -89,6 +90,8 @@ void l2c_link_hci_conn_comp(tHCI_STATUS status, uint16_t handle,
 void BTM_db_reset(void);
 
 extern tBTM_CB btm_cb;
+extern void btm_iot_save_remote_properties(tACL_CONN* p_acl_cb);
+extern void btm_iot_save_remote_versions(tACL_CONN* p_acl_cb);
 
 struct StackAclBtmAcl {
   tACL_CONN* acl_allocate_connection();
@@ -422,6 +425,10 @@ void btm_acl_created(const RawAddress& bda, uint16_t hci_handle,
     btm_ble_refresh_local_resolvable_private_addr(
         bda, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr);
   }
+
+  // save remote properties to iot conf file
+  btm_iot_save_remote_properties(p_acl);
+
   /* if BR/EDR do something more */
   if (transport == BT_TRANSPORT_BR_EDR) {
     btsnd_hcic_read_rmt_clk_offset(hci_handle);
@@ -817,6 +824,9 @@ static void maybe_chain_more_commands_after_read_remote_version_complete(
                 bt_transport_text(p_acl_cb->transport).c_str(),
                 PRIVATE_ADDRESS(p_acl_cb->remote_addr));
   }
+
+  // save remote versions to iot conf file
+  btm_iot_save_remote_versions(p_acl_cb);
 }
 
 void btm_process_remote_version_complete(uint8_t status, uint16_t handle,
@@ -918,6 +928,13 @@ void btm_read_remote_features_complete(uint16_t handle, uint8_t* features) {
                   HCI_FEATURE_BYTES_PER_PAGE);
   p_acl_cb->peer_lmp_feature_valid[0] = true;
 
+  /* save remote supported features to iot conf file */
+  std::string key = IOT_CONF_KEY_RT_SUPP_FEATURES "_" + std::to_string(0);
+
+  DEVICE_IOT_CONFIG_ADDR_SET_BIN(p_acl_cb->remote_addr, key,
+                                 p_acl_cb->peer_lmp_feature_pages[0],
+                                 BD_FEATURES_LEN);
+
   if ((HCI_LMP_EXTENDED_SUPPORTED(p_acl_cb->peer_lmp_feature_pages[0])) &&
       (controller_get_interface()
            ->supports_reading_remote_extended_features())) {
@@ -993,6 +1010,13 @@ void btm_read_remote_ext_features_complete(uint16_t handle, uint8_t page_num,
                   HCI_FEATURE_BYTES_PER_PAGE);
   p_acl_cb->peer_lmp_feature_valid[page_num] = true;
 
+  /* save remote extended features to iot conf file */
+  std::string key = IOT_CONF_KEY_RT_EXT_FEATURES "_" + std::to_string(page_num);
+
+  DEVICE_IOT_CONFIG_ADDR_SET_BIN(p_acl_cb->remote_addr, key,
+                                 p_acl_cb->peer_lmp_feature_pages[page_num],
+                                 BD_FEATURES_LEN);
+
   /* If there is the next remote features page and
    * we have space to keep this page data - read this page */
   if ((page_num < max_page) && (page_num < HCI_EXT_FEATURES_PAGE_MAX)) {
@@ -2575,6 +2599,12 @@ bool acl_set_peer_le_features_from_handle(uint16_t hci_handle,
   STREAM_TO_ARRAY(p_acl->peer_le_features, p, BD_FEATURES_LEN);
   p_acl->peer_le_features_valid = true;
   LOG_DEBUG("Completed le feature read request");
+
+  /* save LE remote supported features to iot conf file */
+  std::string key = IOT_CONF_KEY_RT_SUPP_FEATURES "_" + std::to_string(0);
+
+  DEVICE_IOT_CONFIG_ADDR_SET_BIN(p_acl->remote_addr, key,
+                                 p_acl->peer_le_features, BD_FEATURES_LEN);
   return true;
 }
 
diff --git a/system/stack/avct/avct_lcb.cc b/system/stack/avct/avct_lcb.cc
index c4f82014f5c369b96186a2c793a4c4e73e450c9e..9543f60393d4c42801c6d4259ad1b078c2150280 100644
--- a/system/stack/avct/avct_lcb.cc
+++ b/system/stack/avct/avct_lcb.cc
@@ -30,6 +30,7 @@
 #include "avct_int.h"
 #include "bt_target.h"
 #include "bt_utils.h"
+#include "device/include/device_iot_config.h"
 #include "osi/include/allocator.h"
 #include "osi/include/osi.h"
 #include "types/raw_address.h"
@@ -173,6 +174,10 @@ void avct_lcb_event(tAVCT_LCB* p_lcb, uint8_t event, tAVCT_LCB_EVT* p_data) {
   /* look up the state table for the current state */
   state_table = avct_lcb_st_tbl[p_lcb->state];
 
+  if (p_lcb->state == AVCT_LCB_IDLE_ST && event == AVCT_LCB_LL_OPEN_EVT)
+    DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(p_lcb->peer_addr,
+                                       IOT_CONF_KEY_AVRCP_CONN_COUNT);
+
   /* set next state */
   p_lcb->state = state_table[event][AVCT_LCB_NEXT_STATE];
 
diff --git a/system/stack/avct/avct_lcb_act.cc b/system/stack/avct/avct_lcb_act.cc
index 2a2f6b2161b2b33d4ca40d8396b1f3e61c305a62..67431f1e20b982dc1ad85d20017223e5aaabf2b0 100644
--- a/system/stack/avct/avct_lcb_act.cc
+++ b/system/stack/avct/avct_lcb_act.cc
@@ -30,6 +30,7 @@
 #include "bt_utils.h"
 #include "bta/include/bta_api.h"
 #include "btm_api.h"
+#include "device/include/device_iot_config.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 #include "osi/include/osi.h"
@@ -251,6 +252,8 @@ void avct_lcb_open_ind(tAVCT_LCB* p_lcb, tAVCT_LCB_EVT* p_data) {
 
   /* if no ccbs bound to this lcb, disconnect */
   if (!bind) {
+    DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(p_lcb->peer_addr,
+                                       IOT_CONF_KEY_AVRCP_CONN_FAIL_COUNT);
     avct_lcb_event(p_lcb, AVCT_LCB_INT_CLOSE_EVT, p_data);
   }
 }
@@ -274,6 +277,8 @@ void avct_lcb_open_fail(tAVCT_LCB* p_lcb, tAVCT_LCB_EVT* p_data) {
     if (p_ccb->allocated && (p_ccb->p_lcb == p_lcb)) {
       avct_ccb_dealloc(p_ccb, AVCT_CONNECT_CFM_EVT, p_data->result,
                        &p_lcb->peer_addr);
+      DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(p_lcb->peer_addr,
+                                         IOT_CONF_KEY_AVRCP_CONN_FAIL_COUNT);
     }
   }
 }
@@ -410,6 +415,8 @@ void avct_lcb_chnl_disc(tAVCT_LCB* p_lcb, UNUSED_ATTR tAVCT_LCB_EVT* p_data) {
  ******************************************************************************/
 void avct_lcb_bind_fail(UNUSED_ATTR tAVCT_LCB* p_lcb, tAVCT_LCB_EVT* p_data) {
   avct_ccb_dealloc(p_data->p_ccb, AVCT_CONNECT_CFM_EVT, AVCT_RESULT_FAIL, NULL);
+  DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(p_lcb->peer_addr,
+                                     IOT_CONF_KEY_AVRCP_CONN_FAIL_COUNT);
 }
 
 /*******************************************************************************
diff --git a/system/stack/btm/btm_ble.cc b/system/stack/btm/btm_ble.cc
index 276dd14c7668a586e76b33fe4454ff9a6ab9eaf4..25e19819f42006530588b98741085dfd2e03f86d 100644
--- a/system/stack/btm/btm_ble.cc
+++ b/system/stack/btm/btm_ble.cc
@@ -25,8 +25,11 @@
 
 #define LOG_TAG "bt_btm_ble"
 
+#include <base/logging.h>
+
 #include <cstdint>
 
+#include "btif/include/btif_storage.h"
 #include "device/include/controller.h"
 #include "main/shim/btm_api.h"
 #include "main/shim/l2c_api.h"
@@ -48,8 +51,6 @@
 #include "stack/include/smp_api.h"
 #include "types/raw_address.h"
 
-#include <base/logging.h>
-
 extern tBTM_CB btm_cb;
 
 extern bool btm_ble_init_pseudo_addr(tBTM_SEC_DEV_REC* p_dev_rec,
@@ -111,6 +112,33 @@ void BTM_SecAddBleDevice(const RawAddress& bd_addr, tBT_DEVICE_TYPE dev_type,
   }
 }
 
+/*******************************************************************************
+ *
+ * Function         BTM_GetRemoteDeviceName
+ *
+ * Description      This function is called to get the dev name of remote device
+ *                  from NV
+ *
+ * Returns          TRUE if success; otherwise failed.
+ *
+ ******************************************************************************/
+bool BTM_GetRemoteDeviceName(const RawAddress& bd_addr, BD_NAME bd_name) {
+  BTM_TRACE_DEBUG("%s", __func__);
+  bool ret = FALSE;
+  bt_bdname_t bdname;
+  bt_property_t prop_name;
+  BTIF_STORAGE_FILL_PROPERTY(&prop_name, BT_PROPERTY_BDNAME,
+                             sizeof(bt_bdname_t), &bdname);
+
+  if (btif_storage_get_remote_device_property(&bd_addr, &prop_name) ==
+      BT_STATUS_SUCCESS) {
+    APPL_TRACE_DEBUG("%s, NV name = %s", __func__, bdname.name);
+    strncpy((char*)bd_name, (char*)bdname.name, BD_NAME_LEN + 1);
+    ret = TRUE;
+  }
+  return ret;
+}
+
 /*******************************************************************************
  *
  * Function         BTM_SecAddBleKey
diff --git a/system/stack/btm/btm_iot_config.cc b/system/stack/btm/btm_iot_config.cc
new file mode 100644
index 0000000000000000000000000000000000000000..be80f43a23362c8935178b8f6c67dca79583fbfe
--- /dev/null
+++ b/system/stack/btm/btm_iot_config.cc
@@ -0,0 +1,95 @@
+/******************************************************************************
+ *
+ *  Copyright (C) 2018 The Linux Foundation
+ *
+ *  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 "bt_target.h"
+#include "btif/include/btif_storage.h"
+#include "btif/include/btif_util.h"
+#include "btm_ble_api.h"
+#include "btm_int_types.h"
+#include "device/include/device_iot_config.h"
+
+/*******************************************************************************
+ *
+ * Function         btm_iot_save_remote_properties
+ *
+ * Description      Store remote basic properties to iot conf file
+ *
+ * Returns          void
+ *
+ *******************************************************************************/
+void btm_iot_save_remote_properties(tACL_CONN* p_acl_cb) {
+  BD_NAME bd_name;
+  bt_property_t prop_name;
+  uint32_t cod = 0;
+  tBT_DEVICE_TYPE dev_type;
+  tBLE_ADDR_TYPE addr_type;
+
+  // save remote name to iot conf file
+  if (BTM_GetRemoteDeviceName(p_acl_cb->remote_addr, bd_name)) {
+    std::string name_str{(char*)bd_name};
+    DEVICE_IOT_CONFIG_ADDR_SET_STR(p_acl_cb->remote_addr,
+                                   IOT_CONF_KEY_REMOTE_NAME, name_str);
+  }
+
+  /* Try to retrieve cod from storage */
+  BTIF_STORAGE_FILL_PROPERTY(&prop_name, BT_PROPERTY_CLASS_OF_DEVICE,
+                             sizeof(cod), &cod);
+  if (btif_storage_get_remote_device_property(&p_acl_cb->remote_addr,
+                                              &prop_name) == BT_STATUS_SUCCESS)
+    BTIF_TRACE_DEBUG("%s cod retrieved from storage is 0x%06x", __func__, cod);
+  if (cod == 0) {
+    BTIF_TRACE_DEBUG("%s cod is 0, set as unclassified", __func__);
+    cod = (0x1F) << 8;
+  }
+
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_acl_cb->remote_addr, IOT_CONF_KEY_DEVCLASS,
+                                 (int)cod);
+
+  BTM_ReadDevInfo(p_acl_cb->remote_addr, &dev_type, &addr_type);
+
+  // save remote dev type to iot conf file
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_acl_cb->remote_addr, IOT_CONF_KEY_DEVTYPE,
+                                 (int)dev_type);
+
+  // save remote addr type to iot conf file
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_acl_cb->remote_addr, IOT_CONF_KEY_ADDRTYPE,
+                                 (int)addr_type);
+
+  // save default recorded value to iot conf file
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_acl_cb->remote_addr, IOT_CONF_KEY_RECORDED,
+                                 IOT_CONF_VAL_RECORDED_DEFAULT);
+}
+
+/*******************************************************************************
+ *
+ * Function         btm_iot_save_remote_versions
+ *
+ * Description      Store remote versions to iot conf file
+ *
+ * Returns          void
+ *
+ *******************************************************************************/
+void btm_iot_save_remote_versions(tACL_CONN* p_acl_cb) {
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_acl_cb->remote_addr,
+                                 IOT_CONF_KEY_MANUFACTURER,
+                                 p_acl_cb->remote_version_info.manufacturer);
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_acl_cb->remote_addr, IOT_CONF_KEY_LMPVER,
+                                 p_acl_cb->remote_version_info.lmp_version);
+  DEVICE_IOT_CONFIG_ADDR_SET_INT(p_acl_cb->remote_addr, IOT_CONF_KEY_LMPSUBVER,
+                                 p_acl_cb->remote_version_info.lmp_subversion);
+}
diff --git a/system/stack/btm/btm_sco.cc b/system/stack/btm/btm_sco.cc
index d31ff62a84fc8900220e35bc8bbb7631a873e484..7d9773bc1af2fa354f682f5d95e3bdd5281c73c8 100644
--- a/system/stack/btm/btm_sco.cc
+++ b/system/stack/btm/btm_sco.cc
@@ -31,6 +31,7 @@
 #include <string>
 
 #include "device/include/controller.h"
+#include "device/include/device_iot_config.h"
 #include "embdrv/sbc/decoder/include/oi_codec_sbc.h"
 #include "embdrv/sbc/decoder/include/oi_status.h"
 #include "osi/include/allocator.h"
@@ -807,6 +808,8 @@ void btm_sco_conn_req(const RawAddress& bda, const DEV_CLASS& dev_class,
   tSCO_CONN* p = &p_sco->sco_db[0];
   tBTM_ESCO_CONN_REQ_EVT_DATA evt_data = {};
 
+  DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(bda, IOT_CONF_KEY_HFP_SCO_CONN_COUNT);
+
   for (uint16_t sco_index = 0; sco_index < BTM_MAX_SCO_LINKS;
        sco_index++, p++) {
     /*
@@ -987,8 +990,12 @@ void btm_sco_connection_failed(tHCI_STATUS hci_status, const RawAddress& bda,
         if (p->state == SCO_ST_CONNECTING) {
           p->state = SCO_ST_UNUSED;
           (*p->p_disc_cb)(xx);
-        } else
+        } else {
           p->state = SCO_ST_LISTENING;
+          if (bda != RawAddress::kEmpty)
+            DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(
+                bda, IOT_CONF_KEY_HFP_SCO_CONN_FAIL_COUNT);
+        }
         BTM_LogHistory(
             kBtmLogTag, bda, "Connection failed",
             base::StringPrintf(
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index 0668c33ba968d66006ae18b8234e7fc8c570e592..1394d95204b0cb8082c01a4e57072aec496ef560 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -36,6 +36,7 @@
 #include "common/metrics.h"
 #include "common/time_util.h"
 #include "device/include/controller.h"
+#include "device/include/device_iot_config.h"
 #include "l2c_api.h"
 #include "main/shim/btm_api.h"
 #include "main/shim/dumpsys.h"
@@ -3788,7 +3789,11 @@ void btm_sec_disconnected(uint16_t handle, tHCI_REASON reason,
       status = HCI_ERR_REPEATED_ATTEMPTS;
     } else if (old_pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) {
       status = HCI_ERR_HOST_REJECT_SECURITY;
+    } else {
+      DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(p_dev_rec->bd_addr,
+                                         IOT_CONF_KEY_GAP_DISC_AUTHFAIL_COUNT);
     }
+
     NotifyBondingChange(*p_dev_rec, status);
 
     p_dev_rec = btm_find_dev_by_handle(handle);
diff --git a/system/stack/hcic/hcicmds.cc b/system/stack/hcic/hcicmds.cc
index 1fea1cd463ccbedf3ecbd4fe7b10341f4733ddd0..d41f11c2f8f446f3a5373e5597ee3745bc1ee17e 100644
--- a/system/stack/hcic/hcicmds.cc
+++ b/system/stack/hcic/hcicmds.cc
@@ -29,6 +29,7 @@
 
 #include "bt_target.h"
 #include "btu.h"
+#include "device/include/device_iot_config.h"
 #include "device/include/esco_parameters.h"
 #include "hcidefs.h"
 #include "hcimsgs.h"
@@ -567,6 +568,7 @@ void btsnd_hcic_create_conn(const RawAddress& dest, uint16_t packet_types,
   acl_cache_role(dest, HCI_ROLE_CENTRAL, /*overwrite_cache=*/false);
 
   btm_acl_paging(p, dest);
+  DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(dest, IOT_CONF_KEY_GAP_CONN_COUNT);
 }
 
 static void btsnd_hcic_disconnect(uint16_t handle, uint8_t reason) {
diff --git a/system/stack/include/a2dp_codec_api.h b/system/stack/include/a2dp_codec_api.h
index 2d0e7d2ba204ed2b717d7edbd9a23e7984b0dc80..01e01fd51597a558a5dea2367ad13cd19f83e800 100644
--- a/system/stack/include/a2dp_codec_api.h
+++ b/system/stack/include/a2dp_codec_api.h
@@ -587,6 +587,10 @@ bool A2DP_IsPeerSinkCodecValid(const uint8_t* p_codec_info);
 // Returns true if the A2DP Sink codec is supported, otherwise false.
 bool A2DP_IsSinkCodecSupported(const uint8_t* p_codec_info);
 
+// Gets peer sink endpoint codec type.
+// |p_codec_info| contains information about the codec capabilities.
+int A2DP_IotGetPeerSinkCodecType(const uint8_t* p_codec_info);
+
 // Checks whether an A2DP Source codec for a peer Source device is supported.
 // |p_codec_info| contains information about the codec capabilities of the
 // peer device.
diff --git a/system/stack/include/btm_ble_api.h b/system/stack/include/btm_ble_api.h
index e980906cef5ec4ef9f0522a38f120e134f83cfe5..fb74fe8aeb1bdb7caf1022a1c86095964367d688 100644
--- a/system/stack/include/btm_ble_api.h
+++ b/system/stack/include/btm_ble_api.h
@@ -460,6 +460,18 @@ extern void BTM_ReadDevInfo(const RawAddress& remote_bda,
                             tBT_DEVICE_TYPE* p_dev_type,
                             tBLE_ADDR_TYPE* p_addr_type);
 
+/*******************************************************************************
+ *
+ * Function         BTM_GetRemoteDeviceName
+ *
+ * Description      This function is called to get the dev name of remote device
+ *                  from NV
+ *
+ * Returns          true if success; otherwise failed.
+ *
+ *******************************************************************************/
+extern bool BTM_GetRemoteDeviceName(const RawAddress& bda, BD_NAME bd_name);
+
 /*******************************************************************************
  *
  * Function         BTM_ReadConnectedTransportAddress
diff --git a/system/stack/l2cap/l2c_link.cc b/system/stack/l2cap/l2c_link.cc
index f2be73891ec78e4a6d548a09941918278fdcee2c..1d31102f649ae7f5d17d502b37c5d485e87ee62c 100644
--- a/system/stack/l2cap/l2c_link.cc
+++ b/system/stack/l2cap/l2c_link.cc
@@ -27,6 +27,7 @@
 
 #include <cstdint>
 
+#include "device/include/device_iot_config.h"
 #include "main/shim/l2c_api.h"
 #include "main/shim/shim.h"
 #include "osi/include/allocator.h"
@@ -325,6 +326,36 @@ void l2c_link_sec_comp2(const RawAddress& p_bda,
   }
 }
 
+/*******************************************************************************
+**
+** Function         l2c_link_iot_store_disc_reason
+**
+** Description      iot store disconnection reason to local conf file
+**
+** Returns          void
+**
+*******************************************************************************/
+static void l2c_link_iot_store_disc_reason(RawAddress& bda, uint8_t reason) {
+  const char* disc_keys[] = {
+      IOT_CONF_KEY_GAP_DISC_CONNTIMEOUT_COUNT,
+  };
+  const uint8_t disc_reasons[] = {
+      HCI_ERR_CONNECTION_TOUT,
+  };
+  int i = 0;
+  int num = sizeof(disc_keys) / sizeof(disc_keys[0]);
+
+  if (reason == (uint8_t)-1) return;
+
+  DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(bda, IOT_CONF_KEY_GAP_DISC_COUNT);
+  for (i = 0; i < num; i++) {
+    if (disc_reasons[i] == reason) {
+      DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(bda, disc_keys[i]);
+      break;
+    }
+  }
+}
+
 /*******************************************************************************
  *
  * Function         l2c_link_hci_disc_comp
@@ -349,6 +380,8 @@ bool l2c_link_hci_disc_comp(uint16_t handle, tHCI_REASON reason) {
   if (!p_lcb) {
     status = false;
   } else {
+    l2c_link_iot_store_disc_reason(p_lcb->remote_bd_addr, reason);
+
     p_lcb->SetDisconnectReason(reason);
 
     /* Just in case app decides to try again in the callback context */
diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc
index e2824588fa108d9f5aa9fa5be699a5df673ece17..9964c1d8c4d7bd59d882204ba881c5bf16324583 100644
--- a/system/stack/test/btm/stack_btm_test.cc
+++ b/system/stack/test/btm/stack_btm_test.cc
@@ -54,6 +54,7 @@ namespace mock = test::mock::stack_hcic_hcicmds;
 
 extern tBTM_CB btm_cb;
 
+uint8_t btif_trace_level = BT_TRACE_LEVEL_DEBUG;
 uint8_t appl_trace_level = BT_TRACE_LEVEL_VERBOSE;
 btif_hh_cb_t btif_hh_cb;
 tL2C_CB l2cb;
diff --git a/system/stack/test/stack_acl_test.cc b/system/stack/test/stack_acl_test.cc
index 6b741e2d19a62894e141ebf3f94479e4126f019b..c479a1e28e73e63665d7983e5c2dee87fce2d5a6 100644
--- a/system/stack/test/stack_acl_test.cc
+++ b/system/stack/test/stack_acl_test.cc
@@ -33,6 +33,7 @@
 #include "types/hci_role.h"
 #include "types/raw_address.h"
 
+uint8_t btif_trace_level = BT_TRACE_LEVEL_DEBUG;
 tBTM_CB btm_cb;
 
 void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
diff --git a/system/test/mock/mock_stack_a2dp_codec_config.cc b/system/test/mock/mock_stack_a2dp_codec_config.cc
index 066f82fa4afa94abc66db508de55e0c65870fcec..81ecb0776840b5dec4a51ec211040e91b75e375c 100644
--- a/system/test/mock/mock_stack_a2dp_codec_config.cc
+++ b/system/test/mock/mock_stack_a2dp_codec_config.cc
@@ -345,6 +345,10 @@ void A2DP_InitDefaultCodec(uint8_t* p_codec_info) {
 void A2dpCodecConfig::debug_codec_dump(int fd) {
   mock_function_count_map[__func__]++;
 }
+int A2DP_IotGetPeerSinkCodecType(const uint8_t* p_codec_info) {
+  mock_function_count_map[__func__]++;
+  return 0;
+}
 void A2dpCodecConfig::setCodecPriority(
     btav_a2dp_codec_priority_t codec_priority) {
   mock_function_count_map[__func__]++;
diff --git a/system/test/mock/mock_stack_btm_ble.cc b/system/test/mock/mock_stack_btm_ble.cc
index 8d1871671eb61e4df02277300099a08c0468a895..99843587160288410208db15b0a937c9e0ff2260 100644
--- a/system/test/mock/mock_stack_btm_ble.cc
+++ b/system/test/mock/mock_stack_btm_ble.cc
@@ -57,6 +57,7 @@ struct BTM_GetDeviceEncRoot BTM_GetDeviceEncRoot;
 struct BTM_GetDeviceIDRoot BTM_GetDeviceIDRoot;
 struct BTM_ReadConnectedTransportAddress BTM_ReadConnectedTransportAddress;
 struct BTM_ReadDevInfo BTM_ReadDevInfo;
+struct BTM_GetRemoteDeviceName BTM_GetRemoteDeviceName;
 struct BTM_SecAddBleDevice BTM_SecAddBleDevice;
 struct BTM_SecAddBleKey BTM_SecAddBleKey;
 struct BTM_SecurityGrant BTM_SecurityGrant;
@@ -103,6 +104,7 @@ namespace test {
 namespace mock {
 namespace stack_btm_ble {
 
+bool BTM_GetRemoteDeviceName::return_value = false;
 bool BTM_BleDataSignature::return_value = false;
 bool BTM_BleVerifySignature::return_value = false;
 const Octet16 BTM_GetDeviceDHK::return_value{0xd5, 0xcb, 0x84, 0x54, 0xd1, 0x77,
@@ -231,6 +233,10 @@ void BTM_ReadDevInfo(const RawAddress& remote_bda, tBT_DEVICE_TYPE* p_dev_type,
   test::mock::stack_btm_ble::BTM_ReadDevInfo(remote_bda, p_dev_type,
                                              p_addr_type);
 }
+bool BTM_GetRemoteDeviceName(const RawAddress& bd_addr, BD_NAME bd_name) {
+  mock_function_count_map[__func__]++;
+  return test::mock::stack_btm_ble::BTM_GetRemoteDeviceName(bd_addr, bd_name);
+}
 void BTM_SecAddBleDevice(const RawAddress& bd_addr, tBT_DEVICE_TYPE dev_type,
                          tBLE_ADDR_TYPE addr_type) {
   mock_function_count_map[__func__]++;
diff --git a/system/test/mock/mock_stack_btm_ble.h b/system/test/mock/mock_stack_btm_ble.h
index 3881d68f6f2aa66eb0aedee85404409b06a4b239..e68edb7213d97699b73a6c30eb06b6b7e758c4b2 100644
--- a/system/test/mock/mock_stack_btm_ble.h
+++ b/system/test/mock/mock_stack_btm_ble.h
@@ -334,6 +334,19 @@ struct BTM_SecAddBleDevice {
 };
 extern struct BTM_SecAddBleDevice BTM_SecAddBleDevice;
 
+// Name: BTM_GetRemoteDeviceName
+// Params: const RawAddress& bd_addr, BD_NAME bd_name
+// Return: bool
+struct BTM_GetRemoteDeviceName {
+  static bool return_value;
+  std::function<bool(const RawAddress& bd_addr, BD_NAME bd_name)> body{
+      [](const RawAddress& bd_addr, BD_NAME bd_name) { return return_value; }};
+  bool operator()(const RawAddress& bd_addr, BD_NAME bd_name) {
+    return body(bd_addr, bd_name);
+  };
+};
+extern struct BTM_GetRemoteDeviceName BTM_GetRemoteDeviceName;
+
 // Name: BTM_SecAddBleKey
 // Params: const RawAddress& bd_addr, tBTM_LE_KEY_VALUE* p_le_key,
 // tBTM_LE_KEY_TYPE key_type Return: void