Merge "Disable btif_gatt_check_encrypted_link." into tm-qpr-dev
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
index 9bc4fad..7ca6166 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -106,7 +106,6 @@
     boolean mA2dpOffloadEnabled = false;
 
     private BroadcastReceiver mBondStateChangedReceiver;
-    private BroadcastReceiver mConnectionStateChangedReceiver;
 
     public static boolean isEnabled() {
         return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false);
@@ -169,10 +168,6 @@
         filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mBondStateChangedReceiver = new BondStateChangedReceiver();
         registerReceiver(mBondStateChangedReceiver, filter);
-        filter = new IntentFilter();
-        filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
-        mConnectionStateChangedReceiver = new ConnectionStateChangedReceiver();
-        registerReceiver(mConnectionStateChangedReceiver, filter);
 
         // Step 8: Mark service as started
         setA2dpService(this);
@@ -208,8 +203,6 @@
         setA2dpService(null);
 
         // Step 7: Unregister broadcast receivers
-        unregisterReceiver(mConnectionStateChangedReceiver);
-        mConnectionStateChangedReceiver = null;
         unregisterReceiver(mBondStateChangedReceiver);
         mBondStateChangedReceiver = null;
 
@@ -1242,7 +1235,7 @@
         }
     }
 
-    private void connectionStateChanged(BluetoothDevice device, int fromState, int toState) {
+    void connectionStateChanged(BluetoothDevice device, int fromState, int toState) {
         if ((device == null) || (fromState == toState)) {
             return;
         }
@@ -1269,27 +1262,6 @@
     }
 
     /**
-     * Receiver for processing device connection state changes.
-     *
-     * <ul>
-     * <li> Update codec support per device when device is (re)connected
-     * <li> Delete the state machine instance if the device is disconnected and unbond
-     * </ul>
-     */
-    private class ConnectionStateChangedReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (!BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
-                return;
-            }
-            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-            int toState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-            int fromState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1);
-            connectionStateChanged(device, fromState, toState);
-        }
-    }
-
-    /**
      * Retrieves the most recently connected device in the A2DP connected devices list.
      */
     public BluetoothDevice getFallbackDevice() {
diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
index 9e7323f..11d33ad 100644
--- a/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -701,6 +701,7 @@
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        mA2dpService.connectionStateChanged(mDevice, prevState, newState);
         mA2dpService.sendBroadcast(intent, BLUETOOTH_CONNECT,
                 Utils.getTempAllowlistBroadcastOptions());
     }
diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
index b75da6a..56e63d1 100644
--- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -2018,8 +2018,7 @@
     public void onConnectionStateChangedFromStateMachine(BluetoothDevice device, int fromState,
             int toState) {
         synchronized (mStateMachines) {
-            List<BluetoothDevice> audioConnectableDevices =
-                    getDevicesMatchingConnectionStates(CONNECTING_CONNECTED_STATES);
+            List<BluetoothDevice> audioConnectableDevices = getConnectedDevices();
             if (fromState != BluetoothProfile.STATE_CONNECTED
                     && toState == BluetoothProfile.STATE_CONNECTED) {
                 if (audioConnectableDevices.size() > 1) {
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientContent.java b/android/app/src/com/android/bluetooth/mapclient/MapClientContent.java
index 36d4535..aa60b5b 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MapClientContent.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientContent.java
@@ -137,6 +137,10 @@
         SubscriptionManager subscriptionManager =
                 context.getSystemService(SubscriptionManager.class);
         List<SubscriptionInfo> subscriptions = subscriptionManager.getActiveSubscriptionInfoList();
+        if (subscriptions == null) {
+            Log.w(TAG, "Active subscription list is missing");
+            return;
+        }
         for (SubscriptionInfo info : subscriptions) {
             if (info.getSubscriptionType() == SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM) {
                 clearMessages(context, info.getSubscriptionId());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
index 85fdf8b..6acc5a0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceFactoryResetTest.java
@@ -145,7 +145,7 @@
     private int mForegroundUserId;
 
     private void configureEnabledProfiles() {
-        Log.e("AdapterServiceTest", "configureEnabledProfiles");
+        Log.e(TAG, "configureEnabledProfiles");
         Config.setProfileEnabled(PanService.class, true);
         Config.setProfileEnabled(BluetoothPbapService.class, true);
         Config.setProfileEnabled(GattService.class, true);
@@ -176,7 +176,7 @@
 
     @BeforeClass
     public static void setupClass() {
-        Log.e("AdapterServiceTest", "setupClass");
+        Log.e(TAG, "setupClass");
         // Bring native layer up and down to make sure config files are properly loaded
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -194,7 +194,7 @@
 
     @Before
     public void setUp() throws PackageManager.NameNotFoundException {
-        Log.e("AdapterServiceTest", "setUp()");
+        Log.e(TAG, "setUp()");
         MockitoAnnotations.initMocks(this);
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -327,7 +327,10 @@
 
     @After
     public void tearDown() {
-        Log.e("AdapterServiceTest", "tearDown()");
+        Log.e(TAG, "tearDown()");
+
+        // Enable the stack to re-create the config. Next tests rely on it.
+        doEnable(0, false);
 
         // Restores the foregroundUserId to the ID prior to the test setup
         Utils.setForegroundUserId(mForegroundUserId);
@@ -351,7 +354,7 @@
     }
 
     private void doEnable(int invocationNumber, boolean onlyGatt) {
-        Log.e("AdapterServiceTest", "doEnable() start");
+        Log.e(TAG, "doEnable() start");
         Assert.assertFalse(mAdapterService.getState() == BluetoothAdapter.STATE_ON);
 
         int startServiceCalls;
@@ -398,7 +401,7 @@
                 || scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
         Assert.assertTrue(mAdapterService.getState() == BluetoothAdapter.STATE_ON);
 
-        Log.e("AdapterServiceTest", "doEnable() complete success");
+        Log.e(TAG, "doEnable() complete success");
     }
 
     /**
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
index 900ab86..c93837b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceRestartTest.java
@@ -131,7 +131,7 @@
     private int mForegroundUserId;
 
     private void configureEnabledProfiles() {
-        Log.e("AdapterServiceTest", "configureEnabledProfiles");
+        Log.e(TAG, "configureEnabledProfiles");
         Config.setProfileEnabled(PanService.class, true);
         Config.setProfileEnabled(BluetoothPbapService.class, true);
         Config.setProfileEnabled(GattService.class, true);
@@ -162,7 +162,7 @@
 
     @BeforeClass
     public static void setupClass() {
-        Log.e("AdapterServiceTest", "setupClass");
+        Log.e(TAG, "setupClass");
         // Bring native layer up and down to make sure config files are properly loaded
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -180,7 +180,7 @@
 
     @Before
     public void setUp() throws PackageManager.NameNotFoundException {
-        Log.e("AdapterServiceTest", "setUp()");
+        Log.e(TAG, "setUp()");
         MockitoAnnotations.initMocks(this);
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -309,7 +309,7 @@
 
     @After
     public void tearDown() {
-        Log.e("AdapterServiceTest", "tearDown()");
+        Log.e(TAG, "tearDown()");
 
         // Restores the foregroundUserId to the ID prior to the test setup
         Utils.setForegroundUserId(mForegroundUserId);
@@ -341,6 +341,11 @@
                 obfuscatedAddress1);
         tearDown();
         setUp();
+
+        byte[] metricsSalt2 = AdapterServiceTest.getMetricsSalt(mAdapterConfig);
+        Assert.assertNotNull(metricsSalt2);
+        Assert.assertArrayEquals(metricsSalt, metricsSalt2);
+
         Assert.assertFalse(mAdapterService.getState() == BluetoothAdapter.STATE_ON);
         byte[] obfuscatedAddress2 = mAdapterService.obfuscateAddress(device);
         Assert.assertTrue(obfuscatedAddress2.length > 0);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
index 1763f83..c320b35 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java
@@ -741,7 +741,6 @@
     /**
      * Test: Check if obfuscated Bluetooth address stays the same after toggling Bluetooth
      */
-    @Ignore("b/265588558")
     @Test
     public void testObfuscateBluetoothAddress_PersistentBetweenToggle() {
         Assert.assertFalse(mAdapterService.getState() == BluetoothAdapter.STATE_ON);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
index f64cda7..db5fdd5 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachineTest.java
@@ -1,5 +1,26 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.bluetooth.hfpclient;
 
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
+
 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.AT_OK;
 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.ENTER_PRIVATE_MODE;
 import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.EXPLICIT_CALL_TRANSFER;
@@ -21,6 +42,7 @@
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.HandlerThread;
+import android.os.Looper;
 import android.os.Message;
 import android.util.Pair;
 
@@ -36,6 +58,7 @@
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.bluetooth.hfp.HeadsetStackEvent;
 
 import org.hamcrest.core.AllOf;
 import org.hamcrest.core.IsInstanceOf;
@@ -47,18 +70,22 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.hamcrest.MockitoHamcrest;
 
 import java.util.List;
 import java.util.Set;
 
+/**
+ * Test cases for {@link HeadsetClientStateMachine}.
+ */
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class HeadsetClientStateMachineTest {
     private BluetoothAdapter mAdapter;
     private HandlerThread mHandlerThread;
-    private HeadsetClientStateMachine mHeadsetClientStateMachine;
+    private TestHeadsetClientStateMachine mHeadsetClientStateMachine;
     private BluetoothDevice mTestDevice;
     private Context mTargetContext;
 
@@ -77,6 +104,7 @@
     private static final int QUERY_CURRENT_CALLS_WAIT_MILLIS = 2000;
     private static final int QUERY_CURRENT_CALLS_TEST_WAIT_MILLIS = QUERY_CURRENT_CALLS_WAIT_MILLIS
             * 3 / 2;
+    private static final int TIMEOUT_MS = 1000;
 
     @Before
     public void setUp() throws Exception {
@@ -109,9 +137,8 @@
         mHandlerThread = new HandlerThread("HeadsetClientStateMachineTestHandlerThread");
         mHandlerThread.start();
         // Manage looper execution in main test thread explicitly to guarantee timing consistency
-        mHeadsetClientStateMachine =
-                new HeadsetClientStateMachine(mHeadsetClientService, mHandlerThread.getLooper(),
-                                              mNativeInterface);
+        mHeadsetClientStateMachine = new TestHeadsetClientStateMachine(mHeadsetClientService,
+                mHandlerThread.getLooper(), mNativeInterface);
         mHeadsetClientStateMachine.start();
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
     }
@@ -123,6 +150,7 @@
         }
         TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
         TestUtils.clearAdapterService(mAdapterService);
+        mHeadsetClientStateMachine.allowConnect = null;
         mHeadsetClientStateMachine.doQuit();
         mHandlerThread.quit();
     }
@@ -1073,4 +1101,316 @@
         mHeadsetClientStateMachine.setAudioPolicy(dummyAudioPolicy);
         verify(mNativeInterface).sendAndroidAt(mTestDevice, "+ANDROID=1,1,2,1");
     }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        mHeadsetClientStateMachine.dump(new StringBuilder());
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onDisconnectedState() {
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED,
+                mHeadsetClientStateMachine.getConnectionState(mTestDevice));
+    }
+
+    @Test
+    public void testProcessConnectMessage_onDisconnectedState() {
+        doReturn(true).when(mNativeInterface).connect(any(BluetoothDevice.class));
+        sendMessageAndVerifyTransition(
+                mHeadsetClientStateMachine
+                        .obtainMessage(HeadsetClientStateMachine.CONNECT, mTestDevice),
+                HeadsetClientStateMachine.Connecting.class);
+    }
+
+    @Test
+    public void testStackEvent_toConnectingState_onDisconnectedState() {
+        allowConnection(true);
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED;
+        event.device = mTestDevice;
+        sendMessageAndVerifyTransition(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event),
+                HeadsetClientStateMachine.Connecting.class);
+    }
+
+    @Test
+    public void testStackEvent_toConnectingState_disallowConnection_onDisconnectedState() {
+        allowConnection(false);
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED;
+        event.device = mTestDevice;
+        sendMessageAndVerifyTransition(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event),
+                HeadsetClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testProcessConnectMessage_onConnectingState() {
+        initToConnectingState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertTrue(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                HeadsetClientStateMachine.CONNECT));
+    }
+
+    @Test
+    public void testProcessStackEvent_ConnectionStateChanged_Disconnected_onConnectingState() {
+        initToConnectingState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED;
+        event.device = mTestDevice;
+        sendMessageAndVerifyTransition(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event),
+                HeadsetClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testProcessStackEvent_ConnectionStateChanged_Connected_onConnectingState() {
+        initToConnectingState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class));
+    }
+
+    @Test
+    public void testProcessStackEvent_ConnectionStateChanged_Connecting_onConnectingState() {
+        initToConnectingState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTING;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class));
+    }
+
+    @Test
+    public void testProcessStackEvent_Call_onConnectingState() {
+        initToConnectingState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALL);
+        event.valueInt = StackEvent.EVENT_TYPE_CALL;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertTrue(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                StackEvent.STACK_EVENT));
+    }
+
+    @Test
+    public void testProcessStackEvent_CmdResultWithEmptyQueuedActions_onConnectingState() {
+        initToConnectingState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT);
+        event.valueInt = StackEvent.CMD_RESULT_TYPE_OK;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connecting.class));
+    }
+
+    @Test
+    public void testProcessStackEvent_Unknown_onConnectingState() {
+        String atCommand = "+ANDROID: 1";
+
+        initToConnectingState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT);
+        event.valueString = atCommand;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class));
+    }
+
+    @Test
+    public void testProcessConnectTimeoutMessage_onConnectingState() {
+        initToConnectingState();
+        Message msg = mHeadsetClientStateMachine
+                .obtainMessage(HeadsetClientStateMachine.CONNECTING_TIMEOUT);
+        sendMessageAndVerifyTransition(msg, HeadsetClientStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testProcessConnectMessage_onConnectedState() {
+        initToConnectedState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class));
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onConnectedState() {
+        initToConnectedState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).disconnect(any(BluetoothDevice.class));
+    }
+
+    @Test
+    public void testProcessConnectAudioMessage_onConnectedState() {
+        initToConnectedState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.CONNECT_AUDIO);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).connectAudio(any(BluetoothDevice.class));
+    }
+
+    @Test
+    public void testProcessDisconnectAudioMessage_onConnectedState() {
+        initToConnectedState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).disconnectAudio(any(BluetoothDevice.class));
+    }
+
+    @Test
+    public void testProcessVoiceRecognitionStartMessage_onConnectedState() {
+        initToConnectedState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.VOICE_RECOGNITION_START);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).startVoiceRecognition(any(BluetoothDevice.class));
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onAudioOnState() {
+        initToAudioOnState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT, mTestDevice);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertTrue(mHeadsetClientStateMachine.doesSuperHaveDeferredMessages(
+                HeadsetClientStateMachine.DISCONNECT));
+    }
+
+    @Test
+    public void testProcessDisconnectAudioMessage_onAudioOnState() {
+        initToAudioOnState();
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.DISCONNECT_AUDIO,
+                mTestDevice);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).disconnectAudio(any(BluetoothDevice.class));
+    }
+
+    @Test
+    public void testProcessHoldCall_onAudioOnState() {
+        initToAudioOnState();
+        HfpClientCall call = new HfpClientCall(mTestDevice, 0, HfpClientCall.CALL_STATE_ACTIVE,
+                "1", true, false, false);
+        mHeadsetClientStateMachine.mCalls.put(0, call);
+        int[] states = new int[1];
+        states[0] = HfpClientCall.CALL_STATE_ACTIVE;
+        mHeadsetClientStateMachine.sendMessage(HeadsetClientStateMachine.HOLD_CALL,
+                mTestDevice);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).handleCallAction(any(BluetoothDevice.class), anyInt(), eq(0));
+    }
+
+    @Test
+    public void testProcessStackEvent_ConnectionStateChanged_onAudioOnState() {
+        initToAudioOnState();
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.AudioOn.class));
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_DISCONNECTED;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Disconnected.class));
+    }
+
+    @Test
+    public void testProcessStackEvent_AudioStateChanged_onAudioOnState() {
+        initToAudioOnState();
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.AudioOn.class));
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_DISCONNECTED;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class));
+    }
+
+    /**
+     * Allow/disallow connection to any device
+     *
+     * @param allow if true, connection is allowed
+     */
+    private void allowConnection(boolean allow) {
+        mHeadsetClientStateMachine.allowConnect = allow;
+    }
+
+    private void initToConnectingState() {
+        doReturn(true).when(mNativeInterface).connect(any(BluetoothDevice.class));
+        sendMessageAndVerifyTransition(
+                mHeadsetClientStateMachine
+                        .obtainMessage(HeadsetClientStateMachine.CONNECT, mTestDevice),
+                HeadsetClientStateMachine.Connecting.class);
+    }
+
+    private void initToConnectedState() {
+        String atCommand = "+ANDROID: 1";
+        initToConnectingState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT);
+        event.valueString = atCommand;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.Connected.class));
+    }
+
+    private void initToAudioOnState() {
+        mHeadsetClientStateMachine.setAudioRouteAllowed(true);
+        initToConnectedState();
+        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED);
+        event.valueInt = HeadsetClientHalConstants.AUDIO_STATE_CONNECTED;
+        event.device = mTestDevice;
+        mHeadsetClientStateMachine.sendMessage(
+                mHeadsetClientStateMachine.obtainMessage(StackEvent.STACK_EVENT, event));
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(HeadsetClientStateMachine.AudioOn.class));
+    }
+
+    private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) {
+        Mockito.clearInvocations(mHeadsetClientService);
+        mHeadsetClientStateMachine.sendMessage(msg);
+        // Verify that one connection state broadcast is executed
+        verify(mHeadsetClientService, timeout(TIMEOUT_MS)).sendBroadcastMultiplePermissions(
+                any(Intent.class), any(String[].class), any(BroadcastOptions.class)
+        );
+        Assert.assertThat(mHeadsetClientStateMachine.getCurrentState(),
+                IsInstanceOf.instanceOf(type));
+    }
+
+    public static class TestHeadsetClientStateMachine extends HeadsetClientStateMachine {
+
+        Boolean allowConnect = null;
+
+        TestHeadsetClientStateMachine(HeadsetClientService context, Looper looper,
+                NativeInterface nativeInterface) {
+            super(context, looper, nativeInterface);
+        }
+
+        public boolean doesSuperHaveDeferredMessages(int what) {
+            return super.hasDeferredMessages(what);
+        }
+
+        @Override
+        public boolean okToConnect(BluetoothDevice device) {
+            return allowConnect != null ? allowConnect : super.okToConnect(device);
+        }
+    }
 }
diff --git a/system/bta/hf_client/bta_hf_client_at.cc b/system/bta/hf_client/bta_hf_client_at.cc
index 7a710bf..97fcb97 100644
--- a/system/bta/hf_client/bta_hf_client_at.cc
+++ b/system/bta/hf_client/bta_hf_client_at.cc
@@ -2149,7 +2149,7 @@
   at_len = snprintf(buf, sizeof(buf), "AT+BIA=");
 
   const int32_t position = osi_property_get_int32(
-      "bluetooth.headsetclient.disable_indicator.position", -1);
+      "bluetooth.headset_client.disable_indicator.position", -1);
 
   for (i = 0; i < BTA_HF_CLIENT_AT_INDICATOR_COUNT; i++) {
     int sup = client_cb->at_cb.indicator_lookup[i] == -1 ? 0 : 1;
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 49f80f5..6da4c40 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -681,8 +681,6 @@
       LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
       const bluetooth::hci::iso_manager::cis_establish_cmpl_evt* event)
       override {
-    std::vector<uint8_t> value;
-
     auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);
 
     if (event->status) {
@@ -734,11 +732,17 @@
     }
 
     if (!leAudioDevice->HaveAllActiveAsesCisEst()) {
-      /* More cis established event has to come */
+      /* More cis established events has to come */
       return;
     }
 
-    std::vector<uint8_t> ids;
+    if (!leAudioDevice->IsReadyToCreateStream()) {
+      /* Device still remains in ready to create stream state. It means that
+       * more enabling status notifications has to come. This may only happen
+       * for reconnection scenario for bi-directional CIS.
+       */
+      return;
+    }
 
     /* All CISes created. Send start ready for source ASE before we can go
      * to streaming state.
@@ -749,21 +753,8 @@
                "id: %d, cis handle 0x%04x",
                leAudioDevice->address_.ToString().c_str(), event->cig_id,
                event->cis_conn_hdl);
-    do {
-      if (ase->direction == le_audio::types::kLeAudioDirectionSource)
-        ids.push_back(ase->id);
-    } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
 
-    if (ids.size() > 0) {
-      le_audio::client_parser::ascs::PrepareAseCtpAudioReceiverStartReady(
-          ids, value);
-
-      BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
-                                        leAudioDevice->ctp_hdls_.val_hdl, value,
-                                        GATT_WRITE_NO_RSP, NULL, NULL);
-
-      return;
-    }
+    PrepareAndSendReceiverStartReady(leAudioDevice, ase);
 
     /* Cis establishment may came after setting group state to streaming, e.g.
      * for autonomous scenario when ase is sink */
@@ -2187,6 +2178,28 @@
     }
   }
 
+  void PrepareAndSendReceiverStartReady(LeAudioDevice* leAudioDevice,
+                                        struct ase* ase) {
+    std::vector<uint8_t> ids;
+    std::vector<uint8_t> value;
+
+    do {
+      if (ase->direction == le_audio::types::kLeAudioDirectionSource)
+        ids.push_back(ase->id);
+    } while ((ase = leAudioDevice->GetNextActiveAse(ase)));
+
+    if (ids.size() > 0) {
+      le_audio::client_parser::ascs::PrepareAseCtpAudioReceiverStartReady(
+          ids, value);
+
+      BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
+                                        leAudioDevice->ctp_hdls_.val_hdl, value,
+                                        GATT_WRITE_NO_RSP, NULL, NULL);
+
+      return;
+    }
+  }
+
   void AseStateMachineProcessEnabling(
       struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
       LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
@@ -2200,8 +2213,32 @@
         ase->state = AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING;
 
         if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
-          /* We are here because of the reconnection of the single device. */
-          CisCreateForDevice(leAudioDevice);
+          if (ase->data_path_state < AudioStreamDataPathState::CIS_PENDING) {
+            /* We are here because of the reconnection of the single device. */
+            CisCreateForDevice(leAudioDevice);
+          }
+
+          if (!leAudioDevice->HaveAllActiveAsesCisEst()) {
+            /* More cis established events has to come */
+            return;
+          }
+
+          if (!leAudioDevice->IsReadyToCreateStream()) {
+            /* Device still remains in ready to create stream state. It means
+             * that more enabling status notifications has to come.
+             */
+            return;
+          }
+
+          /* All CISes created. Send start ready for source ASE before we can go
+           * to streaming state.
+           */
+          struct ase* ase = leAudioDevice->GetFirstActiveAse();
+          ASSERT_LOG(ase != nullptr,
+                     "shouldn't be called without an active ASE, device %s",
+                     leAudioDevice->address_.ToString().c_str());
+          PrepareAndSendReceiverStartReady(leAudioDevice, ase);
+
           return;
         }
 
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index 1cafe06..d704a76 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -3100,6 +3100,118 @@
   ASSERT_NE(ase->retrans_nb, 0);
 }
 
+TEST_F(StateMachineTest, testAttachDeviceToTheConversationalStream) {
+  const auto context_type = kContextTypeConversational;
+  const auto leaudio_group_id = 6;
+  const auto num_devices = 2;
+
+  ContentControlIdKeeper::GetInstance()->SetCcid(call_context, call_ccid);
+
+  // Prepare multiple fake connected devices in a group
+  auto* group =
+      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+  PrepareReceiverStartReady(group);
+  PrepareDisableHandler(group);
+  PrepareReleaseHandler(group);
+
+  auto* leAudioDevice = group->GetFirstDevice();
+  LeAudioDevice* lastDevice;
+  LeAudioDevice* fistDevice = leAudioDevice;
+
+  auto expected_devices_written = 0;
+  while (leAudioDevice) {
+    /* Three Writes:
+     * 1: Codec Config
+     * 2: Codec QoS
+     * 3: Enabling
+     */
+    lastDevice = leAudioDevice;
+    EXPECT_CALL(gatt_queue,
+                WriteCharacteristic(leAudioDevice->conn_id_,
+                                    leAudioDevice->ctp_hdls_.val_hdl, _,
+                                    GATT_WRITE_NO_RSP, _, _))
+        .Times(AtLeast(3));
+    expected_devices_written++;
+    leAudioDevice = group->GetNextDevice(leAudioDevice);
+  }
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(4);
+
+  InjectInitialIdleNotification(group);
+
+  // Start the configuration and stream Conversational content
+  LeAudioGroupStateMachine::Get()->StartStream(
+      group, static_cast<LeAudioContextType>(context_type),
+      types::AudioContexts(context_type));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);
+
+  // Inject CIS and ACL disconnection of first device
+  InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT);
+  InjectAclDisconnected(group, lastDevice);
+
+  // Check if group keeps streaming
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
+  lastDevice->conn_id_ = 3;
+  group->UpdateAudioContextTypeAvailability();
+
+  // Make sure ASE with disconnected CIS are not left in STREAMING
+  ASSERT_EQ(lastDevice->GetFirstAseWithState(
+                ::le_audio::types::kLeAudioDirectionSink,
+                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
+            nullptr);
+  ASSERT_EQ(lastDevice->GetFirstAseWithState(
+                ::le_audio::types::kLeAudioDirectionSource,
+                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
+            nullptr);
+
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
+                                              lastDevice->ctp_hdls_.val_hdl, _,
+                                              GATT_WRITE_NO_RSP, _, _))
+      .Times(AtLeast(3));
+
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);
+  LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice);
+
+  // Check if group keeps streaming
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+
+  // Verify that the joining device receives the right CCID list
+  auto lastMeta = lastDevice->GetFirstActiveAse()->metadata;
+  bool parsedOk = false;
+  auto ltv = le_audio::types::LeAudioLtvMap::Parse(lastMeta.data(),
+                                                   lastMeta.size(), parsedOk);
+  ASSERT_TRUE(parsedOk);
+
+  auto ccids = ltv.Find(le_audio::types::kLeAudioMetadataTypeCcidList);
+  ASSERT_TRUE(ccids.has_value());
+  ASSERT_NE(std::find(ccids->begin(), ccids->end(), call_ccid), ccids->end());
+
+  /* Verify that ASE of first device are still good*/
+  auto ase = fistDevice->GetFirstActiveAse();
+  ASSERT_NE(ase->max_transport_latency, 0);
+  ASSERT_NE(ase->retrans_nb, 0);
+
+  // Make sure ASEs with reconnected CIS are in STREAMING state
+  ASSERT_TRUE(lastDevice->HaveAllActiveAsesSameState(
+      types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING));
+}
+
 TEST_F(StateMachineTest, StartStreamAfterConfigure) {
   const auto context_type = kContextTypeMedia;
   const auto leaudio_group_id = 6;
diff --git a/system/btif/src/btif_config.cc b/system/btif/src/btif_config.cc
index 4b99365..a777ba8 100644
--- a/system/btif/src/btif_config.cc
+++ b/system/btif/src/btif_config.cc
@@ -112,7 +112,7 @@
     metrics_salt.fill(0);
   }
   if (!AddressObfuscator::IsSaltValid(metrics_salt)) {
-    LOG(INFO) << __func__ << ": Metrics salt is not invalid, creating new one";
+    LOG(INFO) << __func__ << ": Metrics salt is invalid, creating new one";
     if (RAND_bytes(metrics_salt.data(), metrics_salt.size()) != 1) {
       LOG(FATAL) << __func__ << "Failed to generate salt for metrics";
     }
diff --git a/system/gd/hal/snoop_logger.cc b/system/gd/hal/snoop_logger.cc
index 8591027..e3b585c 100644
--- a/system/gd/hal/snoop_logger.cc
+++ b/system/gd/hal/snoop_logger.cc
@@ -462,9 +462,8 @@
 size_t SnoopLogger::GetMaxPacketsPerBuffer() {
   // We want to use at most 256 KB memory for btsnooz log for release builds
   // and 512 KB memory for userdebug/eng builds
-  auto is_debuggable = os::GetSystemProperty(kIsDebuggableProperty);
-  size_t btsnooz_max_memory_usage_bytes =
-      ((is_debuggable.has_value() && common::StringTrim(is_debuggable.value()) == "1") ? 1024 : 256) * 1024;
+  auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
+  size_t btsnooz_max_memory_usage_bytes = (is_debuggable ? 1024 : 256) * 1024;
   // Calculate max number of packets based on max memory usage and max packet size
   return btsnooz_max_memory_usage_bytes / kDefaultBtSnoozMaxBytesPerPacket;
 }
@@ -474,8 +473,8 @@
   // In userdebug/eng build, it can also be overwritten by modifying the global setting
   std::string default_mode = kBtSnoopLogModeDisabled;
   {
-    auto is_debuggable = os::GetSystemProperty(kIsDebuggableProperty);
-    if (is_debuggable.has_value() && common::StringTrim(is_debuggable.value()) == "1") {
+    auto is_debuggable = os::GetSystemPropertyBool(kIsDebuggableProperty, false);
+    if (is_debuggable) {
       auto default_mode_property = os::GetSystemProperty(kBtSnoopDefaultLogModeProperty);
       if (default_mode_property) {
         default_mode = std::move(default_mode_property.value());
diff --git a/system/gd/os/Android.bp b/system/gd/os/Android.bp
index 7431f15..292da06 100644
--- a/system/gd/os/Android.bp
+++ b/system/gd/os/Android.bp
@@ -17,6 +17,7 @@
 filegroup {
     name: "BluetoothOsSources_android",
     srcs: [
+        "system_properties_common.cc",
         "android/metrics.cc",
         "android/parameter_provider.cc",
         "android/system_properties.cc",
@@ -35,6 +36,7 @@
 filegroup {
     name: "BluetoothOsSources_host",
     srcs: [
+        "system_properties_common.cc",
         "host/metrics.cc",
         "host/parameter_provider.cc",
         "host/system_properties.cc",
diff --git a/system/gd/os/BUILD.gn b/system/gd/os/BUILD.gn
index 48337e1..7204ccd 100644
--- a/system/gd/os/BUILD.gn
+++ b/system/gd/os/BUILD.gn
@@ -18,6 +18,7 @@
     "linux/parameter_provider.cc",
     "linux/system_properties.cc",
     "linux/wakelock_native.cc",
+    "system_properties_common.cc",
     "syslog.cc",
   ]
 
diff --git a/system/gd/os/android/metrics.cc b/system/gd/os/android/metrics.cc
index db42fc5..eacaa72 100644
--- a/system/gd/os/android/metrics.cc
+++ b/system/gd/os/android/metrics.cc
@@ -236,7 +236,7 @@
 }
 
 void LogMetricSmpPairingEvent(
-    const Address& address, uint8_t smp_cmd, android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason) {
+    const Address& address, uint16_t smp_cmd, android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason) {
   int metric_id = 0;
   if (!address.IsEmpty()) {
     metric_id = MetricIdManager::GetInstance().AllocateId(address);
diff --git a/system/gd/os/host/metrics.cc b/system/gd/os/host/metrics.cc
index 955d22e..2e85e73 100644
--- a/system/gd/os/host/metrics.cc
+++ b/system/gd/os/host/metrics.cc
@@ -96,7 +96,7 @@
     const char* attribute_value) {}
 
 void LogMetricSmpPairingEvent(
-    const Address& address, uint8_t smp_cmd, android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason) {}
+    const Address& address, uint16_t smp_cmd, android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason) {}
 
 void LogMetricA2dpPlaybackEvent(const Address& address, int playback_state, int audio_coding_mode) {}
 
diff --git a/system/gd/os/linux/metrics.cc b/system/gd/os/linux/metrics.cc
index 65fb181..3941f77 100644
--- a/system/gd/os/linux/metrics.cc
+++ b/system/gd/os/linux/metrics.cc
@@ -96,7 +96,7 @@
     const char* attribute_value) {}
 
 void LogMetricSmpPairingEvent(
-    const Address& address, uint8_t smp_cmd, android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason) {}
+    const Address& address, uint16_t smp_cmd, android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason) {}
 
 void LogMetricA2dpPlaybackEvent(const Address& address, int playback_state, int audio_coding_mode) {}
 
diff --git a/system/gd/os/linux_generic/queue_unittest.cc b/system/gd/os/linux_generic/queue_unittest.cc
index 7994aba..706fcd2 100644
--- a/system/gd/os/linux_generic/queue_unittest.cc
+++ b/system/gd/os/linux_generic/queue_unittest.cc
@@ -19,6 +19,7 @@
 #include <sys/eventfd.h>
 
 #include <atomic>
+#include <chrono>
 #include <future>
 #include <unordered_map>
 
@@ -26,6 +27,8 @@
 #include "gtest/gtest.h"
 #include "os/reactor.h"
 
+using namespace std::chrono_literals;
+
 namespace bluetooth {
 namespace os {
 namespace {
@@ -60,6 +63,11 @@
   Handler* enqueue_handler_;
   Thread* dequeue_thread_;
   Handler* dequeue_handler_;
+
+  void sync_enqueue_handler() {
+    ASSERT(enqueue_thread_ != nullptr);
+    ASSERT(enqueue_thread_->GetReactor()->WaitForIdle(2s));
+  }
 };
 
 class TestEnqueueEnd {
@@ -338,6 +346,7 @@
   test_enqueue_end.RegisterEnqueue(&enqueue_promise_map);
   enqueue_future.wait();
   EXPECT_EQ(enqueue_future.get(), 0);
+  sync_enqueue_handler();
 }
 
 // Enqueue end level : 1
diff --git a/system/gd/os/metrics.h b/system/gd/os/metrics.h
index 339a2cf..cb35d68 100644
--- a/system/gd/os/metrics.h
+++ b/system/gd/os/metrics.h
@@ -166,7 +166,10 @@
  * @param smp_fail_reason SMP pairing failure reason code from SMP spec
  */
 void LogMetricSmpPairingEvent(
-    const hci::Address& address, uint8_t smp_cmd, android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason);
+    const hci::Address& address,
+    uint16_t smp_cmd,
+    android::bluetooth::DirectionEnum direction,
+    uint16_t smp_fail_reason);
 
 /**
  * Logs there is an event related Bluetooth classic pairing
diff --git a/system/gd/os/system_properties.h b/system/gd/os/system_properties.h
index f5a82b8..42ea4e7 100644
--- a/system/gd/os/system_properties.h
+++ b/system/gd/os/system_properties.h
@@ -26,6 +26,21 @@
 // or if the platform does not support system property
 std::optional<std::string> GetSystemProperty(const std::string& property);
 
+// Get |property| keyed system property as uint32_t from supported platform, return |default_value| if the property
+// does not exist or if the platform does not support system property
+uint32_t GetSystemPropertyUint32(const std::string& property, uint32_t default_value);
+
+// Get |property| keyed system property as uint32_t from supported platform, return |default_value|
+// if the property does not exist or if the platform does not support system property if property is
+// found it will call stoul with |base|
+uint32_t GetSystemPropertyUint32Base(
+    const std::string& property, uint32_t default_value, int base = 0);
+
+// Get |property| keyed property as bool from supported platform, return
+// |default_value| if the property does not exist or if the platform
+// does not support system property
+bool GetSystemPropertyBool(const std::string& property, bool default_value);
+
 // Set |property| keyed system property to |value|, return true if the set was successful and false if the set failed
 // Replace existing value if property already exists
 bool SetSystemProperty(const std::string& property, const std::string& value);
diff --git a/system/gd/os/system_properties_common.cc b/system/gd/os/system_properties_common.cc
new file mode 100644
index 0000000..f59560c
--- /dev/null
+++ b/system/gd/os/system_properties_common.cc
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include "common/strings.h"
+#include "os/system_properties.h"
+
+namespace bluetooth {
+namespace os {
+
+uint32_t GetSystemPropertyUint32(const std::string& property, uint32_t default_value) {
+  return GetSystemPropertyUint32Base(property, default_value, 10);
+}
+
+uint32_t GetSystemPropertyUint32Base(
+    const std::string& property, uint32_t default_value, int base) {
+  std::optional<std::string> result = GetSystemProperty(property);
+  if (result.has_value()) {
+    return static_cast<uint32_t>(std::stoul(*result, nullptr, base));
+  }
+  return default_value;
+}
+
+bool GetSystemPropertyBool(const std::string& property, bool default_value) {
+  std::optional<std::string> result = GetSystemProperty(property);
+  if (result.has_value()) {
+    std::string trimmed_val = common::StringTrim(result.value());
+    if (trimmed_val == "true" || trimmed_val == "1") {
+      return true;
+    }
+    if (trimmed_val == "false" || trimmed_val == "0") {
+      return false;
+    }
+  }
+  return default_value;
+}
+
+}  // namespace os
+}  // namespace bluetooth
diff --git a/system/internal_include/bt_target.h b/system/internal_include/bt_target.h
index a61d1b2..89478dc 100644
--- a/system/internal_include/bt_target.h
+++ b/system/internal_include/bt_target.h
@@ -1026,8 +1026,4 @@
 
 #include "bt_trace.h"
 
-#ifndef BTM_DISABLE_CONCURRENT_PEER_AUTH
-#define BTM_DISABLE_CONCURRENT_PEER_AUTH FALSE
-#endif
-
 #endif /* BT_TARGET_H */
diff --git a/system/main/shim/metrics_api.cc b/system/main/shim/metrics_api.cc
index 99832be..82ad3c1 100644
--- a/system/main/shim/metrics_api.cc
+++ b/system/main/shim/metrics_api.cc
@@ -89,9 +89,9 @@
                                                  transmit_power_level);
 }
 
-void LogMetricSmpPairingEvent(const RawAddress& raw_address, uint8_t smp_cmd,
+void LogMetricSmpPairingEvent(const RawAddress& raw_address, uint16_t smp_cmd,
                               android::bluetooth::DirectionEnum direction,
-                              uint8_t smp_fail_reason) {
+                              uint16_t smp_fail_reason) {
   Address address = bluetooth::ToGdAddress(raw_address);
   bluetooth::os::LogMetricSmpPairingEvent(address, smp_cmd, direction,
                                           smp_fail_reason);
diff --git a/system/main/shim/metrics_api.h b/system/main/shim/metrics_api.h
index ce13876..f838908 100644
--- a/system/main/shim/metrics_api.h
+++ b/system/main/shim/metrics_api.h
@@ -134,9 +134,9 @@
  * @param direction direction of this SMP command
  * @param smp_fail_reason SMP pairing failure reason code from SMP spec
  */
-void LogMetricSmpPairingEvent(const RawAddress& address, uint8_t smp_cmd,
+void LogMetricSmpPairingEvent(const RawAddress& address, uint16_t smp_cmd,
                               android::bluetooth::DirectionEnum direction,
-                              uint8_t smp_fail_reason);
+                              uint16_t smp_fail_reason);
 
 /**
  * Logs there is an event related Bluetooth classic pairing
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 16ffa63..fcf0874 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -1002,9 +1002,14 @@
         "libprotobuf-cpp-lite",
     ],
     sanitize: {
-      hwaddress: true,
-      integer_overflow: true,
-      scs: true,
+        address: true,
+        all_undefined: true,
+        cfi: true,
+        integer_overflow: true,
+        scs: true,
+        diag: {
+            undefined : true
+        },
     },
 }
 
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index 10f56e6..df9b6cc 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -154,6 +154,14 @@
   }
 }
 
+static bool concurrentPeerAuthIsEnabled() {
+  // Was previously named BTM_DISABLE_CONCURRENT_PEER_AUTH.
+  // Renamed to ENABLED for homogeneity with system properties
+  static const bool sCONCURRENT_PEER_AUTH_IS_ENABLED = osi_property_get_bool(
+      "bluetooth.btm.sec.concurrent_peer_auth.enabled", true);
+  return sCONCURRENT_PEER_AUTH_IS_ENABLED;
+}
+
 void NotifyBondingCanceled(tBTM_STATUS btm_status) {
   if (btm_cb.api.p_bond_cancel_cmpl_callback) {
     btm_cb.api.p_bond_cancel_cmpl_callback(BTM_SUCCESS);
@@ -3407,11 +3415,9 @@
                       __func__, p_dev_rec, p_dev_rec->p_callback);
       p_dev_rec->p_callback = NULL;
       l2cu_resubmit_pending_sec_req(&p_dev_rec->bd_addr);
-#ifdef BTM_DISABLE_CONCURRENT_PEER_AUTH
-    } else if (BTM_DISABLE_CONCURRENT_PEER_AUTH &&
+    } else if (!concurrentPeerAuthIsEnabled() &&
                p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) {
       p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;
-#endif
     }
     return;
   }
@@ -4036,10 +4042,9 @@
   tBTM_SEC_DEV_REC* p_dev_rec = btm_find_or_alloc_dev(bda);
 
   VLOG(2) << __func__ << " bda: " << bda;
-#if (defined(BTM_DISABLE_CONCURRENT_PEER_AUTH) && \
-     (BTM_DISABLE_CONCURRENT_PEER_AUTH == TRUE))
-  p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
-#endif
+  if (!concurrentPeerAuthIsEnabled()) {
+    p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
+  }
 
   if ((btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) &&
       (btm_cb.collision_start_time != 0) &&
diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc
index 3861016..2185f6d 100644
--- a/system/stack/gatt/gatt_api.cc
+++ b/system/stack/gatt/gatt_api.cc
@@ -304,7 +304,7 @@
   elem.type = list.asgn_range.is_primary ? GATT_UUID_PRI_SERVICE
                                          : GATT_UUID_SEC_SERVICE;
 
-  if (elem.type == GATT_UUID_PRI_SERVICE) {
+  if (elem.type == GATT_UUID_PRI_SERVICE && gatt_cb.over_br_enabled) {
     Uuid* p_uuid = gatts_get_service_uuid(elem.p_db);
     if (*p_uuid != Uuid::From16Bit(UUID_SERVCLASS_GMCS_SERVER) &&
         *p_uuid != Uuid::From16Bit(UUID_SERVCLASS_GTBS_SERVER)) {
diff --git a/system/stack/gatt/gatt_int.h b/system/stack/gatt/gatt_int.h
index 6cacdfe..fa173f5 100644
--- a/system/stack/gatt/gatt_int.h
+++ b/system/stack/gatt/gatt_int.h
@@ -446,6 +446,7 @@
   tGATT_APPL_INFO cb_info;
 
   tGATT_HDL_CFG hdl_cfg;
+  bool over_br_enabled;
 } tGATT_CB;
 
 #define GATT_SIZE_OF_SRV_CHG_HNDL_RANGE 4
diff --git a/system/stack/gatt/gatt_main.cc b/system/stack/gatt/gatt_main.cc
index 0ee27d8..b9d1120 100644
--- a/system/stack/gatt/gatt_main.cc
+++ b/system/stack/gatt/gatt_main.cc
@@ -123,10 +123,10 @@
 
   L2CA_RegisterFixedChannel(L2CAP_ATT_CID, &fixed_reg);
 
-  bool gatt_over_br_is_disabled =
-      osi_property_get_bool("bluetooth.gatt_over_bredr.disabled", false);
+  gatt_cb.over_br_enabled =
+      osi_property_get_bool("bluetooth.gatt.over_bredr.enabled", true);
   /* Now, register with L2CAP for ATT PSM over BR/EDR */
-  if (!gatt_over_br_is_disabled &&
+  if (gatt_cb.over_br_enabled &&
       !L2CA_Register2(BT_PSM_ATT, dyn_info, false /* enable_snoop */, nullptr,
                       GATT_MAX_MTU_SIZE, 0, BTM_SEC_NONE)) {
     LOG(ERROR) << "ATT Dynamic Registration failed";
diff --git a/system/stack/include/stack_metrics_logging.h b/system/stack/include/stack_metrics_logging.h
index 158c98f..58d869f 100644
--- a/system/stack/include/stack_metrics_logging.h
+++ b/system/stack/include/stack_metrics_logging.h
@@ -34,9 +34,9 @@
     uint32_t hci_cmd, uint16_t hci_event, uint16_t hci_ble_event,
     uint16_t cmd_status, uint16_t reason_code);
 
-void log_smp_pairing_event(const RawAddress& address, uint8_t smp_cmd,
+void log_smp_pairing_event(const RawAddress& address, uint16_t smp_cmd,
                            android::bluetooth::DirectionEnum direction,
-                           uint8_t smp_fail_reason);
+                           uint16_t smp_fail_reason);
 
 void log_sdp_attribute(const RawAddress& address, uint16_t protocol_uuid,
                        uint16_t attribute_id, size_t attribute_size,
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index 9d8eb20..2a7efd8 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -34,10 +34,10 @@
 
 #include "device/include/controller.h"  // TODO Remove
 #include "gd/common/init_flags.h"
+#include "gd/os/system_properties.h"
 #include "main/shim/shim.h"
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
-#include "osi/include/properties.h"
 #include "stack/btm/btm_sec.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/btu.h"  // do_in_main_thread
@@ -68,14 +68,14 @@
 
 uint16_t L2CA_LeCreditDefault() {
   static const uint16_t sL2CAP_LE_CREDIT_DEFAULT =
-      (uint16_t)osi_property_get_int32(
+      bluetooth::os::GetSystemPropertyUint32Base(
           "bluetooth.l2cap.le.credit_default.value", 0xffff);
   return sL2CAP_LE_CREDIT_DEFAULT;
 }
 
 uint16_t L2CA_LeCreditThreshold() {
   static const uint16_t sL2CAP_LE_CREDIT_THRESHOLD =
-      (uint16_t)osi_property_get_int32(
+      bluetooth::os::GetSystemPropertyUint32Base(
           "bluetooth.l2cap.le.credit_threshold.value", 0x0040);
   return sL2CAP_LE_CREDIT_THRESHOLD;
 }
diff --git a/system/stack/metrics/stack_metrics_logging.cc b/system/stack/metrics/stack_metrics_logging.cc
index 4e23bd2..d69d54e 100644
--- a/system/stack/metrics/stack_metrics_logging.cc
+++ b/system/stack/metrics/stack_metrics_logging.cc
@@ -42,9 +42,9 @@
       hci_ble_event, cmd_status, reason_code);
 }
 
-void log_smp_pairing_event(const RawAddress& address, uint8_t smp_cmd,
+void log_smp_pairing_event(const RawAddress& address, uint16_t smp_cmd,
                            android::bluetooth::DirectionEnum direction,
-                           uint8_t smp_fail_reason) {
+                           uint16_t smp_fail_reason) {
   bluetooth::shim::LogMetricSmpPairingEvent(address, smp_cmd, direction,
                                             smp_fail_reason);
 }
diff --git a/system/stack/smp/smp_int.h b/system/stack/smp/smp_int.h
index c4ddf3e..52b5c2b 100644
--- a/system/stack/smp/smp_int.h
+++ b/system/stack/smp/smp_int.h
@@ -32,6 +32,15 @@
 #include "stack/include/bt_octets.h"
 #include "types/raw_address.h"
 
+typedef enum : uint16_t {
+  SMP_METRIC_COMMAND_LE_FLAG = 0x0000,
+  SMP_METRIC_COMMAND_BR_FLAG = 0x0100,
+  SMP_METRIC_COMMAND_LE_PAIRING_CMPL = 0xFF00,
+  SMP_METRIC_COMMAND_BR_PAIRING_CMPL = 0xFF01,
+} tSMP_METRIC_COMMAND;
+
+constexpr uint16_t SMP_METRIC_STATUS_INTERNAL_FLAG = 0x0100;
+
 typedef enum : uint8_t {
   /* Legacy mode */
   SMP_MODEL_ENCRYPTION_ONLY = 0, /* Just Works model */
@@ -414,7 +423,8 @@
 
 /* smp_util.cc */
 extern void smp_log_metrics(const RawAddress& bd_addr, bool is_outgoing,
-                            const uint8_t* p_buf, size_t buf_len);
+                            const uint8_t* p_buf, size_t buf_len,
+                            bool is_over_br);
 extern bool smp_send_cmd(uint8_t cmd_code, tSMP_CB* p_cb);
 extern void smp_cb_cleanup(tSMP_CB* p_cb);
 extern void smp_reset_control_value(tSMP_CB* p_cb);
diff --git a/system/stack/smp/smp_l2c.cc b/system/stack/smp/smp_l2c.cc
index 6a13e8a..264cdc3 100644
--- a/system/stack/smp/smp_l2c.cc
+++ b/system/stack/smp/smp_l2c.cc
@@ -195,7 +195,8 @@
                        smp_rsp_timeout, NULL);
 
     smp_log_metrics(p_cb->pairing_bda, false /* incoming */,
-                    p_buf->data + p_buf->offset, p_buf->len);
+                    p_buf->data + p_buf->offset, p_buf->len,
+                    false /* is_over_br */);
 
     if (cmd == SMP_OPCODE_CONFIRM) {
       SMP_TRACE_DEBUG(
@@ -334,7 +335,8 @@
                        smp_rsp_timeout, NULL);
 
     smp_log_metrics(p_cb->pairing_bda, false /* incoming */,
-                    p_buf->data + p_buf->offset, p_buf->len);
+                    p_buf->data + p_buf->offset, p_buf->len,
+                    true /* is_over_br */);
 
     p_cb->rcvd_cmd_code = cmd;
     p_cb->rcvd_cmd_len = (uint8_t)p_buf->len;
diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc
index 71fa55d..f2aba5e 100644
--- a/system/stack/smp/smp_utils.cc
+++ b/system/stack/smp/smp_utils.cc
@@ -316,22 +316,26 @@
  * @param buf_len length available to read for p_buf
  */
 void smp_log_metrics(const RawAddress& bd_addr, bool is_outgoing,
-                     const uint8_t* p_buf, size_t buf_len) {
+                     const uint8_t* p_buf, size_t buf_len, bool is_over_br) {
   if (buf_len < 1) {
     LOG(WARNING) << __func__ << ": buffer is too small, size is " << buf_len;
     return;
   }
-  uint8_t cmd;
-  STREAM_TO_UINT8(cmd, p_buf);
+  uint8_t raw_cmd;
+  STREAM_TO_UINT8(raw_cmd, p_buf);
   buf_len--;
   uint8_t failure_reason = 0;
-  if (cmd == SMP_OPCODE_PAIRING_FAILED && buf_len >= 1) {
+  if (raw_cmd == SMP_OPCODE_PAIRING_FAILED && buf_len >= 1) {
     STREAM_TO_UINT8(failure_reason, p_buf);
   }
+  uint16_t metric_cmd =
+      is_over_br ? SMP_METRIC_COMMAND_BR_FLAG : SMP_METRIC_COMMAND_LE_FLAG;
+  metric_cmd |= static_cast<uint16_t>(raw_cmd);
   android::bluetooth::DirectionEnum direction =
       is_outgoing ? android::bluetooth::DirectionEnum::DIRECTION_OUTGOING
                   : android::bluetooth::DirectionEnum::DIRECTION_INCOMING;
-  log_smp_pairing_event(bd_addr, cmd, direction, failure_reason);
+  log_smp_pairing_event(bd_addr, metric_cmd, direction,
+                        static_cast<uint16_t>(failure_reason));
 }
 
 /*******************************************************************************
@@ -352,7 +356,8 @@
   SMP_TRACE_EVENT("%s", __func__);
 
   smp_log_metrics(rem_bda, true /* outgoing */,
-                  p_toL2CAP->data + p_toL2CAP->offset, p_toL2CAP->len);
+                  p_toL2CAP->data + p_toL2CAP->offset, p_toL2CAP->len,
+                  smp_cb.smp_over_br /* is_over_br */);
 
   l2cap_ret = L2CA_SendFixedChnlData(fixed_cid, rem_bda, p_toL2CAP);
   if (l2cap_ret == L2CAP_DW_FAILED) {
@@ -980,6 +985,23 @@
                            smp_status_text(evt_data.cmplt.reason).c_str()));
   }
 
+  // Log pairing complete event
+  {
+    auto direction =
+        p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD
+            ? android::bluetooth::DirectionEnum::DIRECTION_OUTGOING
+            : android::bluetooth::DirectionEnum::DIRECTION_INCOMING;
+    uint16_t metric_cmd = p_cb->smp_over_br
+                              ? SMP_METRIC_COMMAND_BR_PAIRING_CMPL
+                              : SMP_METRIC_COMMAND_LE_PAIRING_CMPL;
+    uint16_t metric_status = p_cb->status;
+    if (metric_status > SMP_MAX_FAIL_RSN_PER_SPEC) {
+      metric_status |= SMP_METRIC_STATUS_INTERNAL_FLAG;
+    }
+    log_smp_pairing_event(p_cb->pairing_bda, metric_cmd, direction,
+                          metric_status);
+  }
+
   if (p_cb->status == SMP_SUCCESS && p_cb->smp_over_br) {
     btm_dev_consolidate_existing_connections(pairing_bda);
   }
diff --git a/system/test/mock/mock_main_shim_metrics_api.cc b/system/test/mock/mock_main_shim_metrics_api.cc
index 30aeeb1..8330b85 100644
--- a/system/test/mock/mock_main_shim_metrics_api.cc
+++ b/system/test/mock/mock_main_shim_metrics_api.cc
@@ -128,8 +128,8 @@
       raw_address, handle, cmd_status, transmit_power_level);
 }
 void bluetooth::shim::LogMetricSmpPairingEvent(
-    const RawAddress& raw_address, uint8_t smp_cmd,
-    android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason) {
+    const RawAddress& raw_address, uint16_t smp_cmd,
+    android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason) {
   mock_function_count_map[__func__]++;
   test::mock::main_shim_metrics_api::LogMetricSmpPairingEvent(
       raw_address, smp_cmd, direction, smp_fail_reason);
diff --git a/system/test/mock/mock_main_shim_metrics_api.h b/system/test/mock/mock_main_shim_metrics_api.h
index b42529f..d85088f 100644
--- a/system/test/mock/mock_main_shim_metrics_api.h
+++ b/system/test/mock/mock_main_shim_metrics_api.h
@@ -171,19 +171,19 @@
 };
 extern struct LogMetricReadTxPowerLevelResult LogMetricReadTxPowerLevelResult;
 // Name: LogMetricSmpPairingEvent
-// Params: const RawAddress& raw_address, uint8_t smp_cmd,
+// Params: const RawAddress& raw_address, uint16_t smp_cmd,
 // android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason Returns:
 // void
 struct LogMetricSmpPairingEvent {
-  std::function<void(const RawAddress& raw_address, uint8_t smp_cmd,
+  std::function<void(const RawAddress& raw_address, uint16_t smp_cmd,
                      android::bluetooth::DirectionEnum direction,
-                     uint8_t smp_fail_reason)>
-      body{[](const RawAddress& raw_address, uint8_t smp_cmd,
+                     uint16_t smp_fail_reason)>
+      body{[](const RawAddress& raw_address, uint16_t smp_cmd,
               android::bluetooth::DirectionEnum direction,
-              uint8_t smp_fail_reason) {}};
-  void operator()(const RawAddress& raw_address, uint8_t smp_cmd,
+              uint16_t smp_fail_reason) {}};
+  void operator()(const RawAddress& raw_address, uint16_t smp_cmd,
                   android::bluetooth::DirectionEnum direction,
-                  uint8_t smp_fail_reason) {
+                  uint16_t smp_fail_reason) {
     body(raw_address, smp_cmd, direction, smp_fail_reason);
   };
 };
diff --git a/system/test/mock/mock_stack_metrics_logging.cc b/system/test/mock/mock_stack_metrics_logging.cc
index 1f51191..028636d 100644
--- a/system/test/mock/mock_stack_metrics_logging.cc
+++ b/system/test/mock/mock_stack_metrics_logging.cc
@@ -86,9 +86,9 @@
       address, connection_handle, direction, link_type, hci_cmd, hci_event,
       hci_ble_event, cmd_status, reason_code);
 }
-void log_smp_pairing_event(const RawAddress& address, uint8_t smp_cmd,
+void log_smp_pairing_event(const RawAddress& address, uint16_t smp_cmd,
                            android::bluetooth::DirectionEnum direction,
-                           uint8_t smp_fail_reason) {
+                           uint16_t smp_fail_reason) {
   mock_function_count_map[__func__]++;
   test::mock::stack_metrics_logging::log_smp_pairing_event(
       address, smp_cmd, direction, smp_fail_reason);
diff --git a/system/test/mock/mock_stack_metrics_logging.h b/system/test/mock/mock_stack_metrics_logging.h
index 967b2f8..8b695f4 100644
--- a/system/test/mock/mock_stack_metrics_logging.h
+++ b/system/test/mock/mock_stack_metrics_logging.h
@@ -96,19 +96,19 @@
 };
 extern struct log_link_layer_connection_event log_link_layer_connection_event;
 // Name: log_smp_pairing_event
-// Params: const RawAddress& address, uint8_t smp_cmd,
+// Params: const RawAddress& address, uint16_t smp_cmd,
 // android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason Returns:
 // void
 struct log_smp_pairing_event {
-  std::function<void(const RawAddress& address, uint8_t smp_cmd,
+  std::function<void(const RawAddress& address, uint16_t smp_cmd,
                      android::bluetooth::DirectionEnum direction,
-                     uint8_t smp_fail_reason)>
-      body{[](const RawAddress& address, uint8_t smp_cmd,
+                     uint16_t smp_fail_reason)>
+      body{[](const RawAddress& address, uint16_t smp_cmd,
               android::bluetooth::DirectionEnum direction,
-              uint8_t smp_fail_reason) {}};
-  void operator()(const RawAddress& address, uint8_t smp_cmd,
+              uint16_t smp_fail_reason) {}};
+  void operator()(const RawAddress& address, uint16_t smp_cmd,
                   android::bluetooth::DirectionEnum direction,
-                  uint8_t smp_fail_reason) {
+                  uint16_t smp_fail_reason) {
     body(address, smp_cmd, direction, smp_fail_reason);
   };
 };