Merge "Reduce test flakyness" into tm-qpr-dev
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 65f2fef..d398fd8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -70,6 +70,50 @@
     },
     {
       "name" : "net_test_stack_btm"
+    },
+    {
+      "name" : "BluetoothInstrumentationTests",
+      "options" : [{
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#eventHandler_handleMessage_MSG_CONNECT_TIMEOUT"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#eventHandler_handleMessage_MSG_SHARE_INTERRUPTED_batchFailed"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#eventHandler_handleMessage_TRANSPORT_ERROR_connectThreadIsNull"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#onBatchCanceled_checkStatus"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppUtilityTest#openReceivedFile_fileNotExist"
+      }]
+    },
+    {
+      "name" : "GoogleBluetoothInstrumentationTests",
+      "options" : [{
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#eventHandler_handleMessage_MSG_CONNECT_TIMEOUT"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#eventHandler_handleMessage_MSG_SHARE_INTERRUPTED_batchFailed"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#eventHandler_handleMessage_TRANSPORT_ERROR_connectThreadIsNull"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppTransferTest#onBatchCanceled_checkStatus"
+      }, {
+        // b/259422308
+        "exclude-filter" : "com.android.bluetooth.opp.BluetoothOppUtilityTest#openReceivedFile_fileNotExist"
+      }]
+    },
+    {
+      "name" : "FrameworkBluetoothTests"
+    },
+    {
+      "name" : "ServiceBluetoothTests"
     }
   ]
 }
diff --git a/android/app/res/layout/bluetooth_map_settings.xml b/android/app/res/layout/bluetooth_map_settings.xml
index f256a02..0c7e3b3 100644
--- a/android/app/res/layout/bluetooth_map_settings.xml
+++ b/android/app/res/layout/bluetooth_map_settings.xml
@@ -17,7 +17,7 @@
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/bluetooth_map_settings_liniar_layout"
+    android:id="@+id/bluetooth_map_settings_linear_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
diff --git a/android/app/res/values/config.xml b/android/app/res/values/config.xml
index ecc240d..913c77f 100644
--- a/android/app/res/values/config.xml
+++ b/android/app/res/values/config.xml
@@ -62,9 +62,6 @@
     <!-- For enabling browsed cover art with the AVRCP Controller Cover Artwork feature -->
     <bool name="avrcp_controller_cover_art_browsed_images">false</bool>
 
-    <!-- For enabling the hfp client connection service -->
-    <bool name="hfp_client_connection_service_enabled">false</bool>
-
     <!-- For supporting emergency call through the hfp client connection service  -->
     <bool name="hfp_client_connection_service_support_emergency_call">true</bool>
 
diff --git a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
index 3f7f13b..d1ea7d4 100644
--- a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
+++ b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
@@ -16,12 +16,14 @@
 
 package com.android.bluetooth;
 
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.le.PeriodicAdvertisingCallback;
 import android.bluetooth.le.PeriodicAdvertisingManager;
 import android.bluetooth.le.ScanResult;
+import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -162,6 +164,14 @@
     }
 
     /**
+     * Proxies {@link ContentResolver#acquireUnstableContentProviderClient(String)}.
+     */
+    public ContentProviderClient contentResolverAcquireUnstableContentProviderClient(
+            ContentResolver contentResolver, @NonNull String name) {
+        return contentResolver.acquireUnstableContentProviderClient(name);
+    }
+
+    /**
      * Proxies {@link Context#sendBroadcast(Intent)}.
      */
     public void contextSendBroadcast(Context context, @RequiresPermission Intent intent) {
diff --git a/android/app/src/com/android/bluetooth/Utils.java b/android/app/src/com/android/bluetooth/Utils.java
index fd0a2f1..b22b3aa 100644
--- a/android/app/src/com/android/bluetooth/Utils.java
+++ b/android/app/src/com/android/bluetooth/Utils.java
@@ -408,9 +408,6 @@
 
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)
     public static void enforceBluetoothPrivilegedPermission(Context context) {
-        if (isInstrumentationTestMode()) {
-            return;
-        }
         context.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BLUETOOTH_PRIVILEGED,
                 "Need BLUETOOTH PRIVILEGED permission");
@@ -418,9 +415,6 @@
 
     @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS)
     public static void enforceLocalMacAddressPermission(Context context) {
-        if (isInstrumentationTestMode()) {
-            return;
-        }
         context.enforceCallingOrSelfPermission(
                 android.Manifest.permission.LOCAL_MAC_ADDRESS,
                 "Need LOCAL_MAC_ADDRESS permission");
@@ -428,9 +422,6 @@
 
     @RequiresPermission(android.Manifest.permission.DUMP)
     public static void enforceDumpPermission(Context context) {
-        if (isInstrumentationTestMode()) {
-            return;
-        }
         context.enforceCallingOrSelfPermission(
                 android.Manifest.permission.DUMP,
                 "Need DUMP permission");
@@ -1017,7 +1008,8 @@
         }
         values.put(Telephony.Sms.ERROR_CODE, 0);
 
-        return 1 == context.getContentResolver().update(uri, values, null, null);
+        return 1 == BluetoothMethodProxy.getInstance().contentResolverUpdate(
+                context.getContentResolver(), uri, values, null, null);
     }
 
     /**
diff --git a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
index 878c71e..394f417 100644
--- a/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
+++ b/android/app/src/com/android/bluetooth/bas/BatteryStateMachine.java
@@ -18,7 +18,7 @@
 
 import static android.bluetooth.BluetoothDevice.PHY_LE_1M_MASK;
 import static android.bluetooth.BluetoothDevice.PHY_LE_2M_MASK;
-import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO;
+import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothGatt;
@@ -233,7 +233,7 @@
             mBluetoothGatt.close();
         }
         mBluetoothGatt = mDevice.connectGatt(service, /*autoConnect=*/false,
-                mGattCallback, TRANSPORT_AUTO, /*opportunistic=*/true,
+                mGattCallback, TRANSPORT_LE, /*opportunistic=*/true,
                 PHY_LE_1M_MASK | PHY_LE_2M_MASK, getHandler());
         return mBluetoothGatt != null;
     }
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 29e0692..c5fb78b 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -247,13 +247,7 @@
         return sAdapterService;
     }
 
-    /**
-     * Sets AdapterService for testing.
-     *
-     * @hide
-     */
-    @VisibleForTesting
-    public static synchronized void setAdapterService(AdapterService instance) {
+    private static synchronized void setAdapterService(AdapterService instance) {
         Log.d(TAG, "setAdapterService() - trying to set service to " + instance);
         if (instance == null) {
             return;
@@ -4656,6 +4650,8 @@
                 return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS;
             case /*HCI_ERR_PEER_USER*/ 0x13:
                 return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST;
+            case /*HCI_ERR_REMOTE_POWER_OFF*/ 0x15:
+                return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST;
             case /*HCI_ERR_CONN_CAUSE_LOCAL_HOST*/ 0x16:
                 return BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST;
             case /*HCI_ERR_UNSUPPORTED_REM_FEATURE*/ 0x1A:
diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
index 90d10ee..3fb1080 100644
--- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -38,6 +38,7 @@
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.bluetooth.BluetoothStatsLog;
@@ -754,6 +755,12 @@
             errorLog("Device Properties is null for Device:" + device);
             return;
         }
+        boolean restrict_device_found =
+                SystemProperties.getBoolean("bluetooth.restrict_discovered_device.enabled", false);
+        if (restrict_device_found && (deviceProp.mName == null || deviceProp.mName.isEmpty())) {
+            debugLog("Device name is null or empty: " + device);
+            return;
+        }
 
         Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
index 5945180..062b004 100644
--- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
+++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachine.java
@@ -47,7 +47,7 @@
     static final int CONNECT = 1;
     static final int DISCONNECT = 2;
     @VisibleForTesting static final int STACK_EVENT = 101;
-    private static final int CONNECT_TIMEOUT = 201;
+    @VisibleForTesting static final int CONNECT_TIMEOUT = 201;
 
     // NOTE: the value is not "final" - it is modified in the unit tests
     @VisibleForTesting static int sConnectTimeoutMs = 30000; // 30s
diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
index 4ba889d..f1c83b3 100644
--- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -31,6 +31,7 @@
 
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.gatt.GattService.AdvertiserMap;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -41,7 +42,8 @@
  *
  * @hide
  */
-class AdvertiseManager {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class AdvertiseManager {
     private static final boolean DBG = GattServiceConfig.DBG;
     private static final String TAG = GattServiceConfig.TAG_PREFIX + "AdvertiseManager";
 
diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java
index 2bcedd7..21f596d 100644
--- a/android/app/src/com/android/bluetooth/gatt/GattService.java
+++ b/android/app/src/com/android/bluetooth/gatt/GattService.java
@@ -145,7 +145,8 @@
     /**
      * The default floor value for LE batch scan report delays greater than 0
      */
-    private static final long DEFAULT_REPORT_DELAY_FLOOR = 5000;
+    @VisibleForTesting
+    static final long DEFAULT_REPORT_DELAY_FLOOR = 5000;
 
     // onFoundLost related constants
     private static final int ADVT_STATE_ONFOUND = 0;
@@ -274,9 +275,12 @@
 
     private AdapterService mAdapterService;
     private BluetoothAdapterProxy mBluetoothAdapterProxy;
-    private AdvertiseManager mAdvertiseManager;
-    private PeriodicScanManager mPeriodicScanManager;
-    private ScanManager mScanManager;
+    @VisibleForTesting
+    AdvertiseManager mAdvertiseManager;
+    @VisibleForTesting
+    PeriodicScanManager mPeriodicScanManager;
+    @VisibleForTesting
+    ScanManager mScanManager;
     private AppOpsManager mAppOps;
     private CompanionDeviceManager mCompanionManager;
     private String mExposureNotificationPackage;
@@ -309,7 +313,8 @@
     /**
      * Reliable write queue
      */
-    private Set<String> mReliableQueue = new HashSet<String>();
+    @VisibleForTesting
+    Set<String> mReliableQueue = new HashSet<String>();
 
     static {
         classInitNative();
@@ -4581,7 +4586,8 @@
      *         a new ScanSettings object with the report delay being the floor value if the original
      *         report delay was between 0 and the floor value (exclusive of both)
      */
-    private ScanSettings enforceReportDelayFloor(ScanSettings settings) {
+    @VisibleForTesting
+    ScanSettings enforceReportDelayFloor(ScanSettings settings) {
         if (settings.getReportDelayMillis() == 0) {
             return settings;
         }
diff --git a/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java b/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java
index f4fdb95..e165f0b 100644
--- a/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java
+++ b/android/app/src/com/android/bluetooth/gatt/PeriodicScanManager.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 
 import com.android.bluetooth.btservice.AdapterService;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -39,7 +40,8 @@
  *
  * @hide
  */
-class PeriodicScanManager {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public class PeriodicScanManager {
     private static final boolean DBG = GattServiceConfig.DBG;
     private static final String TAG = GattServiceConfig.TAG_PREFIX + "SyncManager";
 
diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
index 42243ab..8eab0d5 100644
--- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
+++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java
@@ -339,7 +339,6 @@
 
         mActiveAudioOutDevice = null;
         mActiveAudioInDevice = null;
-        mDatabaseManager = null;
         mLeAudioCodecConfig = null;
 
         // Set the service and BLE devices as inactive
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java b/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java
index e0d202e..19c7911 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java
@@ -73,10 +73,13 @@
     private static final int MASK_SUBJECT = 0x00000001;
     @VisibleForTesting
     static final int MASK_DATETIME = 0x00000002;
-    private static final int MASK_SENDER_NAME = 0x00000004;
-    private static final int MASK_SENDER_ADDRESSING = 0x00000008;
+    @VisibleForTesting
+    static final int MASK_SENDER_NAME = 0x00000004;
+    @VisibleForTesting
+    static final int MASK_SENDER_ADDRESSING = 0x00000008;
     private static final int MASK_RECIPIENT_NAME = 0x00000010;
-    private static final int MASK_RECIPIENT_ADDRESSING = 0x00000020;
+    @VisibleForTesting
+    static final int MASK_RECIPIENT_ADDRESSING = 0x00000020;
     private static final int MASK_TYPE = 0x00000040;
     private static final int MASK_SIZE = 0x00000080;
     private static final int MASK_RECEPTION_STATUS = 0x00000100;
@@ -154,7 +157,8 @@
     private final BluetoothMapAccountItem mAccount;
     /* The MasInstance reference is used to update persistent (over a connection) version counters*/
     private final BluetoothMapMasInstance mMasInstance;
-    private String mMessageVersion = BluetoothMapUtils.MAP_V10_STR;
+    @VisibleForTesting
+    String mMessageVersion = BluetoothMapUtils.MAP_V10_STR;
 
     private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK;
     @VisibleForTesting
@@ -219,7 +223,8 @@
     };
 
     /* CONVO LISTING projections and column indexes */
-    private static final String[] MMS_SMS_THREAD_PROJECTION = {
+    @VisibleForTesting
+    static final String[] MMS_SMS_THREAD_PROJECTION = {
             Threads._ID,
             Threads.DATE,
             Threads.SNIPPET,
@@ -999,7 +1004,8 @@
         return sb.toString();
     }
 
-    private void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c,
+    @VisibleForTesting
+    void setRecipientAddressing(BluetoothMapMessageListingElement e, Cursor c,
             FilterInfo fi, BluetoothMapAppParams ap) {
         if ((ap.getParameterMask() & MASK_RECIPIENT_ADDRESSING) != 0) {
             String address = null;
@@ -1079,7 +1085,8 @@
         }
     }
 
-    private void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi,
+    @VisibleForTesting
+    void setSenderAddressing(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi,
             BluetoothMapAppParams ap) {
         if ((ap.getParameterMask() & MASK_SENDER_ADDRESSING) != 0) {
             String address = "";
@@ -1146,10 +1153,10 @@
                 // TODO: This is a BAD hack, that we map the contact ID to a conversation ID!!!
                 //       We need to reach a conclusion on what to do
                 Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT);
-                Cursor contacts =
-                        mResolver.query(contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION,
-                                BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = "
-                                        + contactId, null, null);
+                Cursor contacts = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION,
+                        BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = " + contactId, null,
+                        null);
                 try {
                     // TODO this will not work for group-chats
                     if (contacts != null && contacts.moveToFirst()) {
@@ -1173,7 +1180,8 @@
         }
     }
 
-    private void setSenderName(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi,
+    @VisibleForTesting
+    void setSenderName(BluetoothMapMessageListingElement e, Cursor c, FilterInfo fi,
             BluetoothMapAppParams ap) {
         if ((ap.getParameterMask() & MASK_SENDER_NAME) != 0) {
             String name = "";
@@ -1227,10 +1235,10 @@
                 // For IM we add the contact ID in the addressing
                 long contactId = c.getLong(fi.mMessageColFromAddress);
                 Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT);
-                Cursor contacts =
-                        mResolver.query(contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION,
-                                BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = "
-                                        + contactId, null, null);
+                Cursor contacts = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION,
+                        BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = " + contactId, null,
+                        null);
                 try {
                     // TODO this will not work for group-chats
                     if (contacts != null && contacts.moveToFirst()) {
@@ -1278,9 +1286,8 @@
         }
     }
 
-
-    private void setLastActivity(BluetoothMapConvoListingElement e, Cursor c, FilterInfo fi,
-            BluetoothMapAppParams ap) {
+    @VisibleForTesting
+    void setLastActivity(BluetoothMapConvoListingElement e, Cursor c, FilterInfo fi) {
         long date = 0;
         if (fi.mMsgType == FilterInfo.TYPE_SMS || fi.mMsgType == FilterInfo.TYPE_MMS) {
             date = c.getLong(MMS_SMS_THREAD_COL_DATE);
@@ -1392,7 +1399,7 @@
     private BluetoothMapConvoListingElement createConvoElement(Cursor c, FilterInfo fi,
             BluetoothMapAppParams ap) {
         BluetoothMapConvoListingElement e = new BluetoothMapConvoListingElement();
-        setLastActivity(e, c, fi, ap);
+        setLastActivity(e, c, fi);
         e.setType(getType(c, fi));
 //        setConvoRead(e, c, fi, ap);
         e.setCursorIndex(c.getPosition());
@@ -2788,7 +2795,8 @@
                 }
                 // TODO: Optimize: Reduce projection based on convo parameter mask
                 smsMmsCursor =
-                        mResolver.query(uri, MMS_SMS_THREAD_PROJECTION, selection.toString(), args,
+                        BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver, uri,
+                                MMS_SMS_THREAD_PROJECTION, selection.toString(), null,
                                 sortOrder.toString());
                 if (smsMmsCursor != null) {
                     // store column index so we don't have to look them up anymore (optimization)
@@ -2849,13 +2857,12 @@
                     Log.v(TAG, "URI with parameters: " + contentUri.toString());
                 }
                 // TODO: Optimize: Reduce projection based on convo parameter mask
-                imEmailCursor =
-                        mResolver.query(contentUri, BluetoothMapContract.BT_CONVERSATION_PROJECTION,
-                                null, null,
-                                BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY
-                                        + " DESC, "
-                                        + BluetoothMapContract.ConversationColumns.THREAD_ID
-                                        + " ASC");
+                imEmailCursor = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contentUri, BluetoothMapContract.BT_CONVERSATION_PROJECTION, null, null,
+                        BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY
+                                + " DESC, "
+                                + BluetoothMapContract.ConversationColumns.THREAD_ID
+                                + " ASC");
                 if (imEmailCursor != null) {
                     BluetoothMapConvoListingElement e = null;
                     // store column index so we don't have to look them up anymore (optimization)
@@ -4087,8 +4094,8 @@
 
         BluetoothMapbMessageEmail message = new BluetoothMapbMessageEmail();
         Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-        Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
-                "_ID = " + id, null, null);
+        Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver, contentUri,
+                BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " + id, null, null);
         try {
             if (c != null && c.moveToFirst()) {
                 BluetoothMapFolderElement folderElement;
@@ -4185,7 +4192,8 @@
                 // Get email message body content
                 int count = 0;
                 try {
-                    fd = mResolver.openFileDescriptor(uri, "r");
+                    fd = BluetoothMethodProxy.getInstance().contentResolverOpenFileDescriptor(
+                            mResolver, uri, "r");
                     is = new FileInputStream(fd.getFileDescriptor());
                     StringBuilder email = new StringBuilder("");
                     byte[] buffer = new byte[1024];
@@ -4256,8 +4264,8 @@
 
         BluetoothMapbMessageMime message = new BluetoothMapbMessageMime();
         Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-        Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
-                "_ID = " + id, null, null);
+        Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver, contentUri,
+                BluetoothMapContract.BT_MESSAGE_PROJECTION, "_ID = " + id, null, null);
         Cursor contacts = null;
         try {
             if (c != null && c.moveToFirst()) {
@@ -4310,7 +4318,8 @@
                 // FIXME end temp code
 
                 Uri contactsUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_CONVOCONTACT);
-                contacts = mResolver.query(contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION,
+                contacts = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contactsUri, BluetoothMapContract.BT_CONTACT_PROJECTION,
                         BluetoothMapContract.ConvoContactColumns.CONVO_ID + " = " + threadId, null,
                         null);
                 // TODO this will not work for group-chats
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index a2af6f2..1862f33 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -142,18 +142,23 @@
     private BluetoothMapMasInstance mMasInstance = null;
     private int mMasId;
     private boolean mEnableSmsMms = false;
-    private boolean mObserverRegistered = false;
-    private BluetoothMapAccountItem mAccount;
-    private String mAuthority = null;
+    @VisibleForTesting
+    boolean mObserverRegistered = false;
+    @VisibleForTesting
+    BluetoothMapAccountItem mAccount;
+    @VisibleForTesting
+    String mAuthority = null;
 
     // Default supported feature bit mask is 0x1f
     private int mMapSupportedFeatures = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK;
     // Default event report version is 1.0
-    private int mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V10;
+    @VisibleForTesting
+    int mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V10;
 
     private BluetoothMapFolderElement mFolders = new BluetoothMapFolderElement("DUMMY", null);
     // Will be set by the MAS when generated.
-    private Uri mMessageUri = null;
+    @VisibleForTesting
+    Uri mMessageUri = null;
     @VisibleForTesting
     Uri mContactUri = null;
 
@@ -1456,8 +1461,14 @@
                             if (mTransmitEvents && // extract contact details only if needed
                                     mMapEventReportVersion
                                             > BluetoothMapUtils.MAP_EVENT_REPORT_V10) {
-                                String date = BluetoothMapUtils.getDateTimeString(
-                                        c.getLong(c.getColumnIndex(Sms.DATE)));
+                                long timestamp = c.getLong(c.getColumnIndex(Sms.DATE));
+                                String date = BluetoothMapUtils.getDateTimeString(timestamp);
+                                if (BluetoothMapUtils.isDateTimeOlderThanOneYear(timestamp)) {
+                                    // Skip sending new message events older than one year
+                                    listChanged = false;
+                                    msgListSms.remove(id);
+                                    continue;
+                                }
                                 String subject = c.getString(c.getColumnIndex(Sms.BODY));
                                 if (subject == null) {
                                     subject = "";
@@ -1574,7 +1585,8 @@
         }
     }
 
-    private void handleMsgListChangesMms() {
+    @VisibleForTesting
+    void handleMsgListChangesMms() {
         if (V) {
             Log.d(TAG, "handleMsgListChangesMms");
         }
@@ -1584,9 +1596,11 @@
         Cursor c;
         synchronized (getMsgListMms()) {
             if (mMapEventReportVersion == BluetoothMapUtils.MAP_EVENT_REPORT_V10) {
-                c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION_SHORT, null, null, null);
+                c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        Mms.CONTENT_URI, MMS_PROJECTION_SHORT, null, null, null);
             } else {
-                c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION_SHORT_EXT, null, null, null);
+                c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        Mms.CONTENT_URI, MMS_PROJECTION_SHORT_EXT, null, null, null);
             }
 
             try {
@@ -1625,8 +1639,14 @@
                             if (mTransmitEvents && // extract contact details only if needed
                                     mMapEventReportVersion
                                             != BluetoothMapUtils.MAP_EVENT_REPORT_V10) {
-                                String date = BluetoothMapUtils.getDateTimeString(
-                                        c.getLong(c.getColumnIndex(Mms.DATE)));
+                                long timestamp = c.getLong(c.getColumnIndex(Mms.DATE));
+                                String date = BluetoothMapUtils.getDateTimeString(timestamp);
+                                if (BluetoothMapUtils.isDateTimeOlderThanOneYear(timestamp)) {
+                                    // Skip sending new message events older than one year
+                                    listChanged = false;
+                                    msgListMms.remove(id);
+                                    continue;
+                                }
                                 String subject = c.getString(c.getColumnIndex(Mms.SUBJECT));
                                 if (subject == null || subject.length() == 0) {
                                     /* Get subject from mms text body parts - if any exists */
@@ -1742,7 +1762,8 @@
         }
     }
 
-    private void handleMsgListChangesMsg(Uri uri) throws RemoteException {
+    @VisibleForTesting
+    void handleMsgListChangesMsg(Uri uri) throws RemoteException {
         if (V) {
             Log.v(TAG, "handleMsgListChangesMsg uri: " + uri.toString());
         }
@@ -1855,7 +1876,8 @@
                                         && sentFolder.getFolderId() == folderId
                                         && msg.localInitiatedSend) {
                                     if (msg.transparent) {
-                                        mResolver.delete(
+                                        BluetoothMethodProxy.getInstance().contentResolverDelete(
+                                                mResolver,
                                                 ContentUris.withAppendedId(mMessageUri, id), null,
                                                 null);
                                     } else {
@@ -1955,7 +1977,8 @@
         }
     }
 
-    private void handleContactListChanges(Uri uri) {
+    @VisibleForTesting
+    void handleContactListChanges(Uri uri) {
         if (uri.getAuthority().equals(mAuthority)) {
             try {
                 if (V) {
@@ -2931,7 +2954,8 @@
         if (handle != -1) {
             String whereClause = " _id= " + handle;
             Uri uri = Mms.CONTENT_URI;
-            Cursor queryResult = resolver.query(uri, null, whereClause, null, null);
+            Cursor queryResult = BluetoothMethodProxy.getInstance().contentResolverQuery(resolver,
+                    uri, null, whereClause, null, null);
             try {
                 if (queryResult != null) {
                     if (queryResult.getCount() > 0) {
@@ -2939,7 +2963,8 @@
                         ContentValues data = new ContentValues();
                         /* set folder to be outbox */
                         data.put(Mms.MESSAGE_BOX, folder);
-                        resolver.update(uri, data, whereClause, null);
+                        BluetoothMethodProxy.getInstance().contentResolverUpdate(resolver, uri,
+                                data, whereClause, null);
                         if (D) {
                             Log.d(TAG, "moved MMS message to " + getMmsFolderName(folder));
                         }
@@ -3558,7 +3583,7 @@
             if (D) {
                 Log.d(TAG, "Transparent in use - delete");
             }
-            resolver.delete(uri, null, null);
+            BluetoothMethodProxy.getInstance().contentResolverDelete(resolver, uri, null, null);
         } else if (result == Activity.RESULT_OK) {
             /* This will trigger a notification */
             moveMmsToFolder(handle, resolver, Mms.MESSAGE_BOX_SENT);
@@ -3633,7 +3658,7 @@
             /* Delete from DB */
             ContentResolver resolver = context.getContentResolver();
             if (resolver != null) {
-                resolver.delete(uri, null, null);
+                BluetoothMethodProxy.getInstance().contentResolverDelete(resolver, uri, null, null);
             } else {
                 Log.w(TAG, "Unable to get resolver");
             }
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java b/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
index 43daaf8..2567da6 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapMasInstance.java
@@ -30,6 +30,7 @@
 import com.android.bluetooth.map.BluetoothMapContentObserver.Msg;
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
 import com.android.bluetooth.sdp.SdpManager;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.obex.ServerSession;
 
 import java.io.IOException;
@@ -39,8 +40,10 @@
 import java.util.concurrent.atomic.AtomicLong;
 
 public class BluetoothMapMasInstance implements IObexConnectionHandler {
-    private final String mTag;
-    private static volatile int sInstanceCounter = 0;
+    @VisibleForTesting
+    final String mTag;
+    @VisibleForTesting
+    static volatile int sInstanceCounter = 0;
 
     private static final boolean D = BluetoothMapService.DEBUG;
     private static final boolean V = BluetoothMapService.VERBOSE;
@@ -146,7 +149,8 @@
     }
 
     /* Needed only for test */
-    protected BluetoothMapMasInstance() {
+    @VisibleForTesting
+    BluetoothMapMasInstance() {
         mTag = "BluetoothMapMasInstance" + sInstanceCounter++;
     }
 
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
index 911fc91..f855c78 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
@@ -29,9 +29,11 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 
+import com.android.bluetooth.BluetoothMethodProxy;
 import com.android.bluetooth.SignedLongLong;
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
 import com.android.bluetooth.mapapi.BluetoothMapContract;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.obex.HeaderSet;
 import com.android.obex.Operation;
 import com.android.obex.ResponseCodes;
@@ -168,8 +170,8 @@
      *
      */
     private ContentProviderClient acquireUnstableContentProviderOrThrow() throws RemoteException {
-        ContentProviderClient providerClient =
-                mResolver.acquireUnstableContentProviderClient(mAuthority);
+        ContentProviderClient providerClient = BluetoothMethodProxy.getInstance()
+                .contentResolverAcquireUnstableContentProviderClient(mResolver, mAuthority);
         if (providerClient == null) {
             throw new RemoteException("Failed to acquire provider for " + mAuthority);
         }
@@ -276,7 +278,8 @@
      *        folder.getFolderId() will be used to query sub-folders.
      *        Use a parentFolder with id -1 to get all folders from root.
      */
-    private void addEmailFolders(BluetoothMapFolderElement parentFolder) throws RemoteException {
+    @VisibleForTesting
+    void addEmailFolders(BluetoothMapFolderElement parentFolder) throws RemoteException {
         // Select all parent folders
         BluetoothMapFolderElement newFolder;
 
@@ -529,7 +532,7 @@
                             + appParams.getChatState() + ", ChatStatusConvoId: "
                             + appParams.getChatStateConvoIdString());
                 }
-                return setOwnerStatus(name, appParams);
+                return setOwnerStatus(appParams);
             }
 
         } catch (RemoteException e) {
@@ -841,7 +844,8 @@
         return ResponseCodes.OBEX_HTTP_OK;
     }
 
-    private int setOwnerStatus(String msgHandle, BluetoothMapAppParams appParams)
+    @VisibleForTesting
+    int setOwnerStatus(BluetoothMapAppParams appParams)
             throws RemoteException {
         // This does only work for IM
         if (mAccount != null && mAccount.getType() == BluetoothMapUtils.TYPE.IM) {
@@ -1845,7 +1849,7 @@
                             + appParams.getChatState() + ", ChatStatusConvoId: "
                             + appParams.getChatStateConvoIdString());
                 }
-                return setOwnerStatus(name, appParams);
+                return setOwnerStatus(appParams);
             }
 
         } catch (RemoteException e) {
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
index 7a00e8a..e4dfe64 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -104,10 +104,12 @@
     static final int MSG_OBSERVER_REGISTRATION = 5008;
 
     private static final int START_LISTENER = 1;
-    private static final int USER_TIMEOUT = 2;
+    @VisibleForTesting
+    static final int USER_TIMEOUT = 2;
     private static final int DISCONNECT_MAP = 3;
     private static final int SHUTDOWN = 4;
-    private static final int UPDATE_MAS_INSTANCES = 5;
+    @VisibleForTesting
+    static final int UPDATE_MAS_INSTANCES = 5;
 
     private static final int RELEASE_WAKE_LOCK_DELAY = 10000;
     private PowerManager.WakeLock mWakeLock = null;
@@ -148,7 +150,8 @@
     private boolean mAccountChanged = false;
     private boolean mSdpSearchInitiated = false;
     private SdpMnsRecord mMnsRecord = null;
-    private MapServiceMessageHandler mSessionStatusHandler;
+    @VisibleForTesting
+    Handler mSessionStatusHandler;
     private boolean mServiceStarted = false;
 
     private static BluetoothMapService sBluetoothMapService;
@@ -834,7 +837,8 @@
      * If the key 255 is in use, the first free masId will be returned.
      * @return a free MasId
      */
-    private int getNextMasId() {
+    @VisibleForTesting
+    int getNextMasId() {
         // Find the largest masId in use
         int largestMasId = 0;
         for (int i = 0, c = mMasInstances.size(); i < c; i++) {
@@ -1026,7 +1030,8 @@
         } // Can only be null during shutdown
     }
 
-    private void sendConnectTimeoutMessage() {
+    @VisibleForTesting
+    void sendConnectTimeoutMessage() {
         if (DEBUG) {
             Log.d(TAG, "sendConnectTimeoutMessage()");
         }
@@ -1036,7 +1041,8 @@
         } // Can only be null during shutdown
     }
 
-    private void sendConnectCancelMessage() {
+    @VisibleForTesting
+    void sendConnectCancelMessage() {
         if (mSessionStatusHandler != null) {
             Message msg = mSessionStatusHandler.obtainMessage(MSG_MAS_CONNECT_CANCEL);
             msg.sendToTarget();
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java b/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java
index a3710a3..7d7047f 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java
@@ -677,6 +677,21 @@
         return format.format(cal.getTime());
     }
 
+    static boolean isDateTimeOlderThanOneYear(long timestamp) {
+        Calendar cal = Calendar.getInstance();
+        cal.setTimeInMillis(timestamp);
+        Calendar oneYearAgo = Calendar.getInstance();
+        oneYearAgo.add(Calendar.YEAR, -1);
+        if (cal.compareTo(oneYearAgo) > 0) {
+            if (V) {
+                Log.v(TAG, "isDateTimeOlderThanOneYear timestamp : " + timestamp
+                        + " oneYearAgo: " + oneYearAgo);
+            }
+            return true;
+        }
+        return false;
+    }
+
     static void savePeerSupportUtcTimeStamp(int remoteFeatureMask) {
         if ((remoteFeatureMask & MAP_FEATURE_DEFINED_TIMESTAMP_FORMAT_BIT)
                 == MAP_FEATURE_DEFINED_TIMESTAMP_FORMAT_BIT) {
diff --git a/android/app/src/com/android/bluetooth/map/SmsMmsContacts.java b/android/app/src/com/android/bluetooth/map/SmsMmsContacts.java
index b02d148..b00bd24 100644
--- a/android/app/src/com/android/bluetooth/map/SmsMmsContacts.java
+++ b/android/app/src/com/android/bluetooth/map/SmsMmsContacts.java
@@ -49,7 +49,8 @@
     private static final Uri ADDRESS_URI =
             MmsSms.CONTENT_URI.buildUpon().appendPath("canonical-addresses").build();
 
-    private static final String[] ADDRESS_PROJECTION = {
+    @VisibleForTesting
+    static final String[] ADDRESS_PROJECTION = {
             CanonicalAddressesColumns._ID, CanonicalAddressesColumns.ADDRESS
     };
     private static final int COL_ADDR_ID =
@@ -57,7 +58,8 @@
     private static final int COL_ADDR_ADDR =
             Arrays.asList(ADDRESS_PROJECTION).indexOf(CanonicalAddressesColumns.ADDRESS);
 
-    private static final String[] CONTACT_PROJECTION = {Contacts._ID, Contacts.DISPLAY_NAME};
+    @VisibleForTesting
+    static final String[] CONTACT_PROJECTION = {Contacts._ID, Contacts.DISPLAY_NAME};
     private static final String CONTACT_SEL_VISIBLE = Contacts.IN_VISIBLE_GROUP + "=1";
     private static final int COL_CONTACT_ID =
             Arrays.asList(CONTACT_PROJECTION).indexOf(Contacts._ID);
diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
index 40bf81c..ff38353 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java
@@ -166,6 +166,9 @@
     }
 
     private synchronized void addDeviceToMapAndConnect(BluetoothDevice device) {
+        if (Utils.isInstrumentationTestMode()) {
+            Log.d(TAG, "addDeviceToMapAndConnect: device=" + device, new Exception());
+        }
         // When creating a new statemachine, its state is set to CONNECTING - which will trigger
         // connect.
         MceStateMachine mapStateMachine = new MceStateMachine(this, device);
@@ -365,6 +368,9 @@
         }
         removeUncleanAccounts();
         mMapInstanceMap.clear();
+        if (Utils.isInstrumentationTestMode()) {
+            Log.d(TAG, "cleanup() called.", new Exception());
+        }
         // TODO(b/72948646): should be moved to stop()
         setMapClientService(null);
     }
diff --git a/android/app/src/com/android/bluetooth/mapclient/MnsObexServer.java b/android/app/src/com/android/bluetooth/mapclient/MnsObexServer.java
index 6c0d536..accdbfb 100644
--- a/android/app/src/com/android/bluetooth/mapclient/MnsObexServer.java
+++ b/android/app/src/com/android/bluetooth/mapclient/MnsObexServer.java
@@ -20,6 +20,7 @@
 
 import com.android.bluetooth.ObexAppParameters;
 import com.android.bluetooth.ObexServerSockets;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.obex.HeaderSet;
 import com.android.obex.Operation;
 import com.android.obex.ResponseCodes;
@@ -34,7 +35,8 @@
     private static final String TAG = "MnsObexServer";
     private static final boolean VDBG = MapClientService.VDBG;
 
-    private static final byte[] MNS_TARGET = new byte[]{
+    @VisibleForTesting
+    static final byte[] MNS_TARGET = new byte[]{
             (byte) 0xbb,
             0x58,
             0x2b,
@@ -53,7 +55,8 @@
             0x66
     };
 
-    private static final String TYPE = "x-bt/MAP-event-report";
+    @VisibleForTesting
+    static final String TYPE = "x-bt/MAP-event-report";
 
     private final WeakReference<MceStateMachine> mStateMachineReference;
     private final ObexServerSockets mObexServerSockets;
diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java
index a1b73e8..93a4a0c 100644
--- a/android/app/src/com/android/bluetooth/pan/PanService.java
+++ b/android/app/src/com/android/bluetooth/pan/PanService.java
@@ -70,10 +70,12 @@
     private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
     private static final int BLUETOOTH_PREFIX_LENGTH = 24;
 
-    private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
+    @VisibleForTesting
+    HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
     private int mMaxPanDevices;
     private String mPanIfName;
-    private boolean mIsTethering = false;
+    @VisibleForTesting
+    boolean mIsTethering = false;
     private boolean mNativeAvailable;
     private HashMap<String, IBluetoothPanCallback> mBluetoothTetheringCallbacks;
 
@@ -602,9 +604,8 @@
         public int remote_role;
     }
 
-    ;
-
-    private void onConnectStateChanged(byte[] address, int state, int error, int localRole,
+    @VisibleForTesting
+    void onConnectStateChanged(byte[] address, int state, int error, int localRole,
             int remoteRole) {
         if (DBG) {
             Log.d(TAG, "onConnectStateChanged: " + state + ", local role:" + localRole
@@ -615,7 +616,8 @@
         mHandler.sendMessage(msg);
     }
 
-    private void onControlStateChanged(int localRole, int state, int error, String ifname) {
+    @VisibleForTesting
+    void onControlStateChanged(int localRole, int state, int error, String ifname) {
         if (DBG) {
             Log.d(TAG, "onControlStateChanged: " + state + ", error: " + error + ", ifname: "
                     + ifname);
@@ -625,7 +627,8 @@
         }
     }
 
-    private static int convertHalState(int halState) {
+    @VisibleForTesting
+    static int convertHalState(int halState) {
         switch (halState) {
             case CONN_STATE_CONNECTED:
                 return BluetoothProfile.STATE_CONNECTED;
@@ -748,25 +751,6 @@
         sendBroadcast(intent, BLUETOOTH_CONNECT);
     }
 
-    private List<BluetoothDevice> getConnectedPanDevices() {
-        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
-
-        for (BluetoothDevice device : mPanDevices.keySet()) {
-            if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
-                devices.add(device);
-            }
-        }
-        return devices;
-    }
-
-    private int getPanDeviceConnectionState(BluetoothDevice device) {
-        BluetoothPanDevice panDevice = mPanDevices.get(device);
-        if (panDevice == null) {
-            return BluetoothProfile.STATE_DISCONNECTED;
-        }
-        return panDevice.mState;
-    }
-
     @Override
     public void dump(StringBuilder sb) {
         super.dump(sb);
@@ -779,7 +763,8 @@
         }
     }
 
-    private class BluetoothPanDevice {
+    @VisibleForTesting
+    static class BluetoothPanDevice {
         private int mState;
         private String mIface;
         private int mLocalRole; // Which local role is this PAN device bound to
@@ -795,10 +780,14 @@
 
     // Constants matching Hal header file bt_hh.h
     // bthh_connection_state_t
-    private static final int CONN_STATE_CONNECTED = 0;
-    private static final int CONN_STATE_CONNECTING = 1;
-    private static final int CONN_STATE_DISCONNECTED = 2;
-    private static final int CONN_STATE_DISCONNECTING = 3;
+    @VisibleForTesting
+    static final int CONN_STATE_CONNECTED = 0;
+    @VisibleForTesting
+    static final int CONN_STATE_CONNECTING = 1;
+    @VisibleForTesting
+    static final int CONN_STATE_DISCONNECTED = 2;
+    @VisibleForTesting
+    static final int CONN_STATE_DISCONNECTING = 3;
 
     private static native void classInitNative();
 
diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
index 331fb60..62ebfd5 100644
--- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
+++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java
@@ -166,7 +166,8 @@
 
     private PbapHandler mSessionStatusHandler;
     private HandlerThread mHandlerThread;
-    private final HashMap<BluetoothDevice, PbapStateMachine> mPbapStateMachineMap = new HashMap<>();
+    @VisibleForTesting
+    final HashMap<BluetoothDevice, PbapStateMachine> mPbapStateMachineMap = new HashMap<>();
     private volatile int mNextNotificationId = PBAP_NOTIFICATION_ID_START;
 
     // package and class name to which we send intent to check phone book access permission
@@ -276,7 +277,8 @@
         }
     }
 
-    private BroadcastReceiver mPbapReceiver = new BroadcastReceiver() {
+    @VisibleForTesting
+    BroadcastReceiver mPbapReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             parseIntent(intent);
@@ -446,10 +448,6 @@
     public int getConnectionState(BluetoothDevice device) {
         enforceCallingOrSelfPermission(
                 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission");
-        if (mPbapStateMachineMap == null) {
-            return BluetoothProfile.STATE_DISCONNECTED;
-        }
-
         synchronized (mPbapStateMachineMap) {
             PbapStateMachine sm = mPbapStateMachineMap.get(device);
             if (sm == null) {
@@ -460,9 +458,6 @@
     }
 
     List<BluetoothDevice> getConnectedDevices() {
-        if (mPbapStateMachineMap == null) {
-            return new ArrayList<>();
-        }
         synchronized (mPbapStateMachineMap) {
             return new ArrayList<>(mPbapStateMachineMap.keySet());
         }
@@ -470,7 +465,7 @@
 
     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
         List<BluetoothDevice> devices = new ArrayList<>();
-        if (mPbapStateMachineMap == null || states == null) {
+        if (states == null) {
             return devices;
         }
         synchronized (mPbapStateMachineMap) {
@@ -631,6 +626,7 @@
         getContentResolver().unregisterContentObserver(mContactChangeObserver);
         mContactChangeObserver = null;
         setComponentAvailable(PBAP_ACTIVITY, false);
+        mPbapStateMachineMap.clear();
         return true;
     }
 
diff --git a/android/app/tests/unit/AndroidTest.xml b/android/app/tests/unit/AndroidTest.xml
index 8414185..4358ed8 100644
--- a/android/app/tests/unit/AndroidTest.xml
+++ b/android/app/tests/unit/AndroidTest.xml
@@ -64,9 +64,10 @@
         <option name="hidden-api-checks" value="false"/>
     </test>
 
-    <!-- Only run Cts Tests in MTS if the Bluetooth Mainline module is installed. -->
+    <!-- Only run if the Bluetooth Mainline module is installed. -->
     <object type="module_controller"
             class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="enable" value="true" />
         <option name="mainline-module-package-name" value="com.android.btservices" />
     </object>
 </configuration>
diff --git a/android/app/tests/unit/GoogleAndroidTest.xml b/android/app/tests/unit/GoogleAndroidTest.xml
index 24fa605..8300f6c 100644
--- a/android/app/tests/unit/GoogleAndroidTest.xml
+++ b/android/app/tests/unit/GoogleAndroidTest.xml
@@ -64,9 +64,10 @@
         <option name="hidden-api-checks" value="false"/>
     </test>
 
-    <!-- Only run Cts Tests in MTS if the Bluetooth Mainline module is installed. -->
+    <!-- Only run if the Google Bluetooth Mainline module is installed. -->
     <object type="module_controller"
             class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="enable" value="true" />
         <option name="mainline-module-package-name" value="com.google.android.btservices" />
     </object>
 </configuration>
diff --git a/android/app/tests/unit/src/com/android/bluetooth/BluetoothPrefsTest.java b/android/app/tests/unit/src/com/android/bluetooth/BluetoothPrefsTest.java
new file mode 100644
index 0000000..dc8ba43
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/BluetoothPrefsTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2023 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;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+
+import static androidx.test.espresso.intent.Intents.intended;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.espresso.intent.Intents;
+import androidx.test.espresso.intent.matcher.IntentMatchers;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothPrefsTest {
+
+    Context mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    Intent mIntent;
+
+    ActivityScenario<BluetoothPrefs> mActivityScenario;
+
+    @Before
+    public void setUp() {
+        Intents.init();
+        enableActivity(true);
+
+        mIntent = new Intent();
+        mIntent.setClass(mTargetContext, BluetoothPrefs.class);
+
+        mActivityScenario = ActivityScenario.launch(mIntent);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mActivityScenario != null) {
+            // Workaround for b/159805732. Without this, test hangs for 45 seconds.
+            Thread.sleep(1_000);
+            mActivityScenario.close();
+        }
+
+        enableActivity(false);
+    }
+
+    @Test
+    public void initialize_launchesBluetoothSettingsActivity() {
+        intended(IntentMatchers.hasAction(BluetoothPrefs.BLUETOOTH_SETTING_ACTION));
+    }
+
+    private void enableActivity(boolean enable) {
+        int enabledState = enable ? COMPONENT_ENABLED_STATE_ENABLED
+                : COMPONENT_ENABLED_STATE_DEFAULT;
+
+        mTargetContext.getPackageManager().setApplicationEnabledSetting(
+                mTargetContext.getPackageName(), enabledState, DONT_KILL_APP);
+
+        ComponentName activityName = new ComponentName(mTargetContext, BluetoothPrefs.class);
+        mTargetContext.getPackageManager().setComponentEnabledSetting(
+                activityName, enabledState, DONT_KILL_APP);
+    }
+}
\ No newline at end of file
diff --git a/android/app/tests/unit/src/com/android/bluetooth/SignedLongLongTest.java b/android/app/tests/unit/src/com/android/bluetooth/SignedLongLongTest.java
new file mode 100644
index 0000000..0a4b682
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/SignedLongLongTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2023 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for SignedLongLong.java
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SignedLongLongTest {
+
+    @Test
+    public void compareTo_sameValue_returnsZero() {
+        long mostSigBits = 1352;
+        long leastSigBits = 53423;
+
+        SignedLongLong value = new SignedLongLong(mostSigBits, leastSigBits);
+        SignedLongLong sameValue = new SignedLongLong(mostSigBits, leastSigBits);
+
+        assertThat(value.compareTo(sameValue)).isEqualTo(0);
+    }
+
+    @Test
+    public void compareTo_biggerLeastSigBits_returnsMinusOne() {
+        long commonMostSigBits = 12345;
+        long leastSigBits = 1;
+        SignedLongLong value = new SignedLongLong(leastSigBits, commonMostSigBits);
+
+        long biggerLeastSigBits = 2;
+        SignedLongLong biggerValue = new SignedLongLong(biggerLeastSigBits, commonMostSigBits);
+
+        assertThat(value.compareTo(biggerValue)).isEqualTo(-1);
+    }
+
+    @Test
+    public void compareTo_smallerLeastSigBits_returnsOne() {
+        long commonMostSigBits = 12345;
+        long leastSigBits = 2;
+        SignedLongLong value = new SignedLongLong(leastSigBits, commonMostSigBits);
+
+        long smallerLeastSigBits = 1;
+        SignedLongLong smallerValue = new SignedLongLong(smallerLeastSigBits, commonMostSigBits);
+
+        assertThat(value.compareTo(smallerValue)).isEqualTo(1);
+    }
+
+    @Test
+    public void compareTo_biggerMostSigBits_returnsMinusOne() {
+        long commonLeastSigBits = 12345;
+        long mostSigBits = 1;
+        SignedLongLong value = new SignedLongLong(commonLeastSigBits, mostSigBits);
+
+        long biggerMostSigBits = 2;
+        SignedLongLong biggerValue = new SignedLongLong(commonLeastSigBits, biggerMostSigBits);
+
+        assertThat(value.compareTo(biggerValue)).isEqualTo(-1);
+    }
+
+    @Test
+    public void compareTo_smallerMostSigBits_returnsOne() {
+        long commonLeastSigBits = 12345;
+        long mostSigBits = 2;
+        SignedLongLong value = new SignedLongLong(commonLeastSigBits, mostSigBits);
+
+        long smallerMostSigBits = 1;
+        SignedLongLong smallerValue = new SignedLongLong(commonLeastSigBits, smallerMostSigBits);
+
+        assertThat(value.compareTo(smallerValue)).isEqualTo(1);
+    }
+
+    @Test
+    public void toString_RepresentedAsHexValues() {
+        SignedLongLong value = new SignedLongLong(2, 11);
+
+        assertThat(value.toString()).isEqualTo("B0000000000000002");
+    }
+
+    @SuppressWarnings("EqualsIncompatibleType")
+    @Test
+    public void equals_variousCases() {
+        SignedLongLong value = new SignedLongLong(1, 2);
+
+        assertThat(value.equals(value)).isTrue();
+        assertThat(value.equals(null)).isFalse();
+        assertThat(value.equals("a random string")).isFalse();
+        assertThat(value.equals(new SignedLongLong(1, 1))).isFalse();
+        assertThat(value.equals(new SignedLongLong(2, 2))).isFalse();
+        assertThat(value.equals(new SignedLongLong(1, 2))).isTrue();
+    }
+
+    @Test
+    public void fromString_whenStringIsNull_throwsNPE() {
+        assertThrows(NullPointerException.class, () -> SignedLongLong.fromString(null));
+    }
+
+    @Test
+    public void fromString_whenLengthIsInvalid_throwsNumberFormatException() {
+        assertThrows(NumberFormatException.class, () -> SignedLongLong.fromString(""));
+    }
+
+    @Test
+    public void fromString_whenLengthIsNotGreaterThan16() throws Exception {
+        String strValue = "1";
+
+        assertThat(SignedLongLong.fromString(strValue))
+                .isEqualTo(new SignedLongLong(1, 0));
+    }
+
+    @Test
+    public void fromString_whenLengthIsGreaterThan16() throws Exception {
+        String strValue = "B0000000000000002";
+
+        assertThat(SignedLongLong.fromString(strValue))
+                .isEqualTo(new SignedLongLong(2, 11));
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java b/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java
index 6a63eb9..20e830c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/UtilsTest.java
@@ -17,12 +17,16 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 import android.location.LocationManager;
+import android.os.Build;
 import android.os.ParcelUuid;
 import android.os.UserHandle;
 
@@ -36,6 +40,11 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.UUID;
@@ -168,4 +177,76 @@
         String loggableAddress = "xx:xx:xx:xx:" + device.getAddress().substring(12);
         assertThat(Utils.getLoggableAddress(device)).isEqualTo(loggableAddress);
     }
+
+    @Test
+    public void checkCallerIsSystemMethods_doesNotCrash() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        String tag = "test_tag";
+
+        Utils.checkCallerIsSystemOrActiveOrManagedUser(context, tag);
+        Utils.checkCallerIsSystemOrActiveOrManagedUser(null, tag);
+        Utils.checkCallerIsSystemOrActiveUser(tag);
+    }
+
+    @Test
+    public void testCopyStream() throws Exception {
+        byte[] data = new byte[] {1, 2, 3, 4, 5, 6, 7, 8};
+        ByteArrayInputStream in = new ByteArrayInputStream(data);
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        int bufferSize = 4;
+
+        Utils.copyStream(in, out, bufferSize);
+
+        assertThat(out.toByteArray()).isEqualTo(data);
+    }
+
+    @Test
+    public void debugGetAdapterStateString() {
+        assertThat(Utils.debugGetAdapterStateString(BluetoothAdapter.STATE_OFF))
+                .isEqualTo("STATE_OFF");
+        assertThat(Utils.debugGetAdapterStateString(BluetoothAdapter.STATE_ON))
+                .isEqualTo("STATE_ON");
+        assertThat(Utils.debugGetAdapterStateString(BluetoothAdapter.STATE_TURNING_ON))
+                .isEqualTo("STATE_TURNING_ON");
+        assertThat(Utils.debugGetAdapterStateString(BluetoothAdapter.STATE_TURNING_OFF))
+                .isEqualTo("STATE_TURNING_OFF");
+        assertThat(Utils.debugGetAdapterStateString(-124))
+                .isEqualTo("UNKNOWN");
+    }
+
+    @Test
+    public void ellipsize() {
+        if (!Build.TYPE.equals("user")) {
+            // Only ellipsize release builds
+            String input = "a_long_string";
+            assertThat(Utils.ellipsize(input)).isEqualTo(input);
+            return;
+        }
+
+        assertThat(Utils.ellipsize("ab")).isEqualTo("ab");
+        assertThat(Utils.ellipsize("abc")).isEqualTo("a⋯c");
+        assertThat(Utils.ellipsize(null)).isEqualTo(null);
+    }
+
+    @Test
+    public void safeCloseStream_inputStream_doesNotCrash() throws Exception {
+        InputStream is = mock(InputStream.class);
+        Utils.safeCloseStream(is);
+        verify(is).close();
+
+        Mockito.clearInvocations(is);
+        doThrow(new IOException()).when(is).close();
+        Utils.safeCloseStream(is);
+    }
+
+    @Test
+    public void safeCloseStream_outputStream_doesNotCrash() throws Exception {
+        OutputStream os = mock(OutputStream.class);
+        Utils.safeCloseStream(os);
+        verify(os).close();
+
+        Mockito.clearInvocations(os);
+        doThrow(new IOException()).when(os).close();
+        Utils.safeCloseStream(os);
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
index 416661d..1d216f7 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java
@@ -91,6 +91,7 @@
         }
 
         TestUtils.setAdapterService(mAdapterService);
+        doReturn(true).when(mAdapterService).isA2dpOffloadEnabled();
         doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices();
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
         doReturn(false).when(mAdapterService).isQuietModeEnabled();
@@ -865,6 +866,11 @@
                 verifySupportTime, verifyNotSupportTime, verifyEnabledTime);
     }
 
+    @Test
+    public void testDumpDoesNotCrash() {
+        mA2dpService.dump(new StringBuilder());
+    }
+
     private void connectDevice(BluetoothDevice device) {
         connectDeviceWithCodecStatus(device, null);
     }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
index e087fe5..d4fe9ed 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java
@@ -417,4 +417,22 @@
         // Check if low latency audio been update.
         verify(mA2dpService, times(6)).updateLowLatencyAudioSupport(mTestDevice);
     }
+
+    @Test
+    public void dump_doesNotCrash() {
+        BluetoothCodecConfig[] codecsSelectableSbc;
+        codecsSelectableSbc = new BluetoothCodecConfig[1];
+        codecsSelectableSbc[0] = mCodecConfigSbc;
+
+        BluetoothCodecConfig[] codecsSelectableSbcAac;
+        codecsSelectableSbcAac = new BluetoothCodecConfig[2];
+        codecsSelectableSbcAac[0] = mCodecConfigSbc;
+        codecsSelectableSbcAac[1] = mCodecConfigAac;
+
+        BluetoothCodecStatus codecStatusSbcAndSbc = new BluetoothCodecStatus(mCodecConfigSbc,
+                Arrays.asList(codecsSelectableSbcAac), Arrays.asList(codecsSelectableSbc));
+        mA2dpStateMachine.processCodecConfigEvent(codecStatusSbcAndSbc);
+
+        mA2dpStateMachine.dump(new StringBuilder());
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
index 42b5ff7..1f63faa 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceTest.java
@@ -464,4 +464,12 @@
         assertThat(mService.setConnectionPolicy(mDevice1,
                 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isFalse();
     }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        mockDevicePriority(mDevice1, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        setupDeviceConnection(mDevice1);
+
+        mService.dump(new StringBuilder());
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/GPMWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/GPMWrapperTest.java
new file mode 100644
index 0000000..0ef8a29
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/GPMWrapperTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2023 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.audio_util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GPMWrapperTest {
+
+    private Context mContext;
+    private MediaController mMediaController;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mMediaController = mock(MediaController.class);
+    }
+
+    @Test
+    public void isMetadataSynced_whenQueueIsNull_returnsFalse() {
+        when(mMediaController.getQueue()).thenReturn(null);
+
+        GPMWrapper wrapper = new GPMWrapper(mContext, mMediaController, null);
+
+        assertThat(wrapper.isMetadataSynced()).isFalse();
+    }
+
+    @Test
+    public void isMetadataSynced_whenOutOfSync_returnsFalse() {
+        long activeQueueItemId = 3;
+        PlaybackState state = new PlaybackState.Builder()
+                .setActiveQueueItemId(activeQueueItemId).build();
+        when(mMediaController.getPlaybackState()).thenReturn(state);
+
+        List<MediaSession.QueueItem> queue = new ArrayList<>();
+        MediaDescription description = new MediaDescription.Builder()
+                .setTitle("Title from queue item")
+                .build();
+        MediaSession.QueueItem queueItem = new MediaSession.QueueItem(
+                description, activeQueueItemId);
+        queue.add(queueItem);
+        when(mMediaController.getQueue()).thenReturn(queue);
+
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_TITLE,
+                        "Different Title from MediaMetadata")
+                .build();
+        when(mMediaController.getMetadata()).thenReturn(metadata);
+
+        GPMWrapper wrapper = new GPMWrapper(mContext, mMediaController, null);
+
+        assertThat(wrapper.isMetadataSynced()).isFalse();
+    }
+
+    @Test
+    public void isMetadataSynced_whenSynced_returnsTrue() {
+        String title = "test_title";
+
+        long activeQueueItemId = 3;
+        PlaybackState state = new PlaybackState.Builder()
+                .setActiveQueueItemId(activeQueueItemId).build();
+        when(mMediaController.getPlaybackState()).thenReturn(state);
+
+        List<MediaSession.QueueItem> queue = new ArrayList<>();
+        MediaDescription description = new MediaDescription.Builder()
+                .setTitle(title)
+                .build();
+        MediaSession.QueueItem queueItem = new MediaSession.QueueItem(
+                description, activeQueueItemId);
+        queue.add(queueItem);
+        when(mMediaController.getQueue()).thenReturn(queue);
+
+        MediaMetadata metadata = new MediaMetadata.Builder()
+                .putString(MediaMetadata.METADATA_KEY_TITLE, title)
+                .build();
+        when(mMediaController.getMetadata()).thenReturn(metadata);
+
+        GPMWrapper wrapper = new GPMWrapper(mContext, mMediaController, null);
+
+        assertThat(wrapper.isMetadataSynced()).isFalse();
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
index 16ba201..2940d00 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java
@@ -16,6 +16,8 @@
 
 package com.android.bluetooth.audio_util;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.*;
 
 import android.content.Context;
@@ -717,4 +719,144 @@
         Assert.assertFalse(wrapper.getTimeoutHandler().hasMessages(MSG_TIMEOUT));
         verify(mFailHandler, never()).onTerribleFailure(any(), any(), anyBoolean());
     }
+
+    @Test
+    public void pauseCurrent() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        wrapper.pauseCurrent();
+
+        verify(transportControls).pause();
+    }
+
+    @Test
+    public void playCurrent() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        wrapper.playCurrent();
+
+        verify(transportControls).play();
+    }
+
+    @Test
+    public void playItemFromQueue() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        when(mMockController.getQueue()).thenReturn(new ArrayList<>());
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        long queueItemId = 4;
+        wrapper.playItemFromQueue(queueItemId);
+
+        verify(transportControls).skipToQueueItem(queueItemId);
+    }
+
+    @Test
+    public void rewind() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        wrapper.rewind();
+
+        verify(transportControls).rewind();
+    }
+
+    @Test
+    public void seekTo() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        long position = 50;
+        wrapper.seekTo(position);
+
+        verify(transportControls).seekTo(position);
+    }
+
+    @Test
+    public void setPlaybackSpeed() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        float speed = 2.0f;
+        wrapper.setPlaybackSpeed(speed);
+
+        verify(transportControls).setPlaybackSpeed(speed);
+    }
+
+    @Test
+    public void skipToNext() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        wrapper.skipToNext();
+
+        verify(transportControls).skipToNext();
+    }
+
+    @Test
+    public void skipToPrevious() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        wrapper.skipToPrevious();
+
+        verify(transportControls).skipToPrevious();
+    }
+
+    @Test
+    public void stopCurrent() {
+        MediaController.TransportControls transportControls
+                = mock(MediaController.TransportControls.class);
+        when(mMockController.getTransportControls()).thenReturn(transportControls);
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        wrapper.stopCurrent();
+
+        verify(transportControls).stop();
+    }
+
+    @Test
+    public void toggleRepeat_andToggleShuffle_doesNotCrash() {
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        wrapper.toggleRepeat(true);
+        wrapper.toggleRepeat(false);
+        wrapper.toggleShuffle(true);
+        wrapper.toggleShuffle(false);
+    }
+
+    @Test
+    public void toString_doesNotCrash() {
+        MediaPlayerWrapper wrapper =
+                MediaPlayerWrapperFactory.wrap(mMockContext, mMockController, mThread.getLooper());
+
+        assertThat(wrapper.toString()).isNotEmpty();
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java
index 24d35f5..f4d400b 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceTest.java
@@ -428,4 +428,10 @@
                 eq(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS),
                 eq(new ArrayList<>(Arrays.asList(items))));
     }
+
+    @Test
+    public void dump_doesNotCrash() {
+        mService.getRcPsm(REMOTE_DEVICE_ADDRESS_AS_ARRAY, 1);
+        mService.dump(new StringBuilder());
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
index ef909ac..8c5ee71 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bas/BatteryServiceTest.java
@@ -211,7 +211,7 @@
      * Test that an outgoing connection to device
      */
     @Test
-    public void testConnect() {
+    public void testConnectAndDump() {
         // Update the device policy so okToConnect() returns true
         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
         when(mDatabaseManager
@@ -222,6 +222,9 @@
                 .getRemoteUuids(any(BluetoothDevice.class));
         // Send a connect request
         Assert.assertTrue("Connect expected to succeed", mService.connect(mDevice));
+
+        // Test dump() is not crashed.
+        mService.dump(new StringBuilder());
     }
 
     /**
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
index 2a7a4f4..f1f88b8 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientStateMachineTest.java
@@ -1490,6 +1490,11 @@
         verify(mBassClientService, never()).sendBroadcast(any(Intent.class), anyString(), any());
     }
 
+    @Test
+    public void dump_doesNotCrash() {
+        mBassClientStateMachine.dump(new StringBuilder());
+    }
+
     private void initToDisconnectedState() {
         allowConnection(true);
         allowConnectGatt(true);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java
index 0b1db1c..0e6d4d1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 The Android Open Source Project
+ * Copyright 2023 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.
@@ -54,7 +54,7 @@
         mService.mAdapterProperties = mAdapterProperties;
         doReturn(true).when(mService).isAvailable();
         doReturn(mPackageManager).when(mService).getPackageManager();
-        doReturn(new String[] { "com.android.bluetooth.btservice" })
+        doReturn(new String[] { "com.android.bluetooth.btservice.test" })
                 .when(mPackageManager).getPackagesForUid(anyInt());
         mBinder = new AdapterService.AdapterServiceBinder(mService);
         mAttributionSource = new AttributionSource.Builder(0).build();
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 721b457..29414ca 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
@@ -443,6 +443,7 @@
      * Test: Turn Bluetooth on.
      * Check whether the AdapterService gets started.
      */
+    @Ignore("b/228874625")
     @Test
     public void testEnable() {
         Log.e("AdapterServiceTest", "testEnable() start");
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
index 5b2baa2..4c7cc43 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java
@@ -532,6 +532,25 @@
                 group_id, intent.getIntExtra(BluetoothCsipSetCoordinator.EXTRA_CSIS_GROUP_ID, -1));
     }
 
+    @Test
+    public void testDump_doesNotCrash() {
+        // Update the device policy so okToConnect() returns true
+        when(mDatabaseManager.getProfileConnectionPolicy(
+                mTestDevice, BluetoothProfile.CSIP_SET_COORDINATOR))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        doReturn(true).when(mCsipSetCoordinatorNativeInterface).connect(any(BluetoothDevice.class));
+        doReturn(true)
+                .when(mCsipSetCoordinatorNativeInterface)
+                .disconnect(any(BluetoothDevice.class));
+        doReturn(new ParcelUuid[] {BluetoothUuid.COORDINATED_SET})
+                .when(mAdapterService)
+                .getRemoteUuids(any(BluetoothDevice.class));
+        // add state machines for testing dump()
+        mService.connect(mTestDevice);
+
+        mService.dump(new StringBuilder());
+    }
+
     /**
      * Helper function to test ConnectionStateIntent() method
      */
diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java
index b2d99f3..8ae5f93 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorStateMachineTest.java
@@ -17,6 +17,11 @@
 
 package com.android.bluetooth.csip;
 
+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 org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -25,9 +30,10 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
-import android.content.Context;
 import android.content.Intent;
 import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
 import android.test.suitebuilder.annotation.MediumTest;
 
 import androidx.test.InstrumentationRegistry;
@@ -42,6 +48,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 @MediumTest
@@ -49,11 +56,10 @@
 public class CsipSetCoordinatorStateMachineTest {
     private final String mFlagDexmarker = System.getProperty("dexmaker.share_classloader", "false");
 
-    private Context mTargetContext;
     private BluetoothAdapter mAdapter;
     private BluetoothDevice mTestDevice;
     private HandlerThread mHandlerThread;
-    private CsipSetCoordinatorStateMachine mStateMachine;
+    private CsipSetCoordinatorStateMachineWrapper mStateMachine;
     private static final int TIMEOUT_MS = 1000;
 
     @Mock private AdapterService mAdapterService;
@@ -66,7 +72,6 @@
             System.setProperty("dexmaker.share_classloader", "true");
         }
 
-        mTargetContext = InstrumentationRegistry.getTargetContext();
         // Set up mocks and test assets
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
@@ -79,8 +84,8 @@
         // Set up thread and looper
         mHandlerThread = new HandlerThread("CsipSetCoordinatorServiceTestHandlerThread");
         mHandlerThread.start();
-        mStateMachine = new CsipSetCoordinatorStateMachine(
-                mTestDevice, mService, mNativeInterface, mHandlerThread.getLooper());
+        mStateMachine = spy(new CsipSetCoordinatorStateMachineWrapper(
+                mTestDevice, mService, mNativeInterface, mHandlerThread.getLooper()));
 
         // Override the timeout value to speed up the test
         CsipSetCoordinatorStateMachine.sConnectTimeoutMs = 1000;
@@ -103,7 +108,7 @@
     @Test
     public void testDefaultDisconnectedState() {
         Assert.assertEquals(
-                BluetoothProfile.STATE_DISCONNECTED, mStateMachine.getConnectionState());
+                STATE_DISCONNECTED, mStateMachine.getConnectionState());
     }
 
     /**
@@ -155,7 +160,7 @@
         ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
         verify(mService, timeout(TIMEOUT_MS).times(1))
                 .sendBroadcast(intentArgument1.capture(), anyString());
-        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
+        Assert.assertEquals(STATE_CONNECTING,
                 intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
 
         // Check that we are in Connecting state
@@ -196,7 +201,7 @@
         ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
         verify(mService, timeout(TIMEOUT_MS).times(1))
                 .sendBroadcast(intentArgument1.capture(), anyString());
-        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
+        Assert.assertEquals(STATE_CONNECTING,
                 intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
 
         // Check that we are in Connecting state
@@ -207,7 +212,7 @@
         ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
         verify(mService, timeout(CsipSetCoordinatorStateMachine.sConnectTimeoutMs * 2).times(2))
                 .sendBroadcast(intentArgument2.capture(), anyString());
-        Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
+        Assert.assertEquals(STATE_DISCONNECTED,
                 intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
 
         // Check that we are in Disconnected state
@@ -236,7 +241,7 @@
         ArgumentCaptor<Intent> intentArgument1 = ArgumentCaptor.forClass(Intent.class);
         verify(mService, timeout(TIMEOUT_MS).times(1))
                 .sendBroadcast(intentArgument1.capture(), anyString());
-        Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
+        Assert.assertEquals(STATE_CONNECTING,
                 intentArgument1.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
 
         // Check that we are in Connecting state
@@ -247,7 +252,7 @@
         ArgumentCaptor<Intent> intentArgument2 = ArgumentCaptor.forClass(Intent.class);
         verify(mService, timeout(CsipSetCoordinatorStateMachine.sConnectTimeoutMs * 2).times(2))
                 .sendBroadcast(intentArgument2.capture(), anyString());
-        Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
+        Assert.assertEquals(STATE_DISCONNECTED,
                 intentArgument2.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
 
         // Check that we are in Disconnected state
@@ -255,4 +260,427 @@
                 IsInstanceOf.instanceOf(CsipSetCoordinatorStateMachine.Disconnected.class));
         verify(mNativeInterface).disconnect(eq(mTestDevice));
     }
+
+    @Test
+    public void testGetDevice() {
+        Assert.assertEquals(mTestDevice, mStateMachine.getDevice());
+    }
+
+    @Test
+    public void testIsConnected() {
+        Assert.assertFalse(mStateMachine.isConnected());
+
+        initToConnectedState();
+        Assert.assertTrue(mStateMachine.isConnected());
+    }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        mStateMachine.dump(new StringBuilder());
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onDisconnectedState() {
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.DISCONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+    }
+
+    @Test
+    public void testProcessConnectMessage_onDisconnectedState() {
+        allowConnection(false);
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+
+        allowConnection(false);
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+
+        allowConnection(true);
+        doReturn(true).when(mNativeInterface).connect(any(BluetoothDevice.class));
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.CONNECT),
+                CsipSetCoordinatorStateMachine.Connecting.class);
+    }
+
+    @Test
+    public void testStackEvent_withoutStateChange_onDisconnectedState() {
+        allowConnection(false);
+
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTED;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+        verify(mNativeInterface).disconnect(mTestDevice);
+
+        Mockito.clearInvocations(mNativeInterface);
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+        verify(mNativeInterface).disconnect(mTestDevice);
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTING;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = -1;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTED, mStateMachine.getConnectionState());
+    }
+
+    @Test
+    public void testStackEvent_toConnectingState_onDisconnectedState() {
+        allowConnection(true);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Connecting.class);
+    }
+
+    @Test
+    public void testStackEvent_toConnectedState_onDisconnectedState() {
+        allowConnection(true);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Connected.class);
+    }
+
+    @Test
+    public void testProcessConnectMessage_onConnectingState() {
+        initToConnectingState();
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertTrue(mStateMachine.doesSuperHaveDeferredMessages(
+                CsipSetCoordinatorStateMachine.CONNECT));
+    }
+
+    @Test
+    public void testProcessConnectTimeoutMessage_onConnectingState() {
+        initToConnectingState();
+        Message msg = mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.CONNECT_TIMEOUT);
+        sendMessageAndVerifyTransition(msg, CsipSetCoordinatorStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onConnectingState() {
+        initToConnectingState();
+        Message msg = mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.DISCONNECT);
+        sendMessageAndVerifyTransition(msg, CsipSetCoordinatorStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testStackEvent_withoutStateChange_onConnectingState() {
+        initToConnectingState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_CONNECTING, mStateMachine.getConnectionState());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_CONNECTING, mStateMachine.getConnectionState());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = 10000;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_CONNECTING, mStateMachine.getConnectionState());
+    }
+
+    @Test
+    public void testStackEvent_toDisconnectedState_onConnectingState() {
+        initToConnectingState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTED;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testStackEvent_toConnectedState_onConnectingState() {
+        initToConnectingState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Connected.class);
+    }
+
+    @Test
+    public void testStackEvent_toDisconnectingState_onConnectingState() {
+        initToConnectingState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTING;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    public void testProcessConnectMessage_onConnectedState() {
+        initToConnectedState();
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.CONNECT);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_CONNECTED, mStateMachine.getConnectionState());
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onConnectedState() {
+        initToConnectedState();
+        doReturn(true).when(mNativeInterface).disconnect(any(BluetoothDevice.class));
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.DISCONNECT),
+                CsipSetCoordinatorStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onConnectedState_withNativeError() {
+        initToConnectedState();
+        doReturn(false).when(mNativeInterface).disconnect(any(BluetoothDevice.class));
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.DISCONNECT),
+                CsipSetCoordinatorStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testStackEvent_withoutStateChange_onConnectedState() {
+        initToConnectedState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_CONNECTED, mStateMachine.getConnectionState());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_CONNECTED, mStateMachine.getConnectionState());
+    }
+
+    @Test
+    public void testStackEvent_toDisconnectedState_onConnectedState() {
+        initToConnectedState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTED;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testStackEvent_toDisconnectingState_onConnectedState() {
+        initToConnectedState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTING;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Disconnecting.class);
+    }
+
+    @Test
+    public void testProcessConnectMessage_onDisconnectingState() {
+        initToDisconnectingState();
+        Message msg = mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.CONNECT);
+        mStateMachine.sendMessage(msg);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mStateMachine).deferMessage(msg);
+    }
+
+    @Test
+    public void testProcessConnectTimeoutMessage_onDisconnectingState() {
+        initToConnectingState();
+        Message msg = mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.CONNECT_TIMEOUT);
+        sendMessageAndVerifyTransition(msg, CsipSetCoordinatorStateMachine.Disconnected.class);
+    }
+
+    @Test
+    public void testProcessDisconnectMessage_onDisconnectingState() {
+        initToDisconnectingState();
+        Message msg = mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.DISCONNECT);
+        mStateMachine.sendMessage(msg);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mStateMachine).deferMessage(msg);
+    }
+
+    @Test
+    public void testStackEvent_withoutStateChange_onDisconnectingState() {
+        initToDisconnectingState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(-1);
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTING, mStateMachine.getConnectionState());
+
+        allowConnection(false);
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).disconnect(any());
+
+        Mockito.clearInvocations(mNativeInterface);
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).disconnect(any());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTING;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTING, mStateMachine.getConnectionState());
+
+        event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = 10000;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        Assert.assertEquals(STATE_DISCONNECTING, mStateMachine.getConnectionState());
+    }
+
+    @Test
+    public void testStackEvent_toConnectedState_onDisconnectingState() {
+        initToDisconnectingState();
+        allowConnection(true);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Connected.class);
+    }
+
+    @Test
+    public void testStackEvent_toConnectedState_butNotAllowed_onDisconnectingState() {
+        initToDisconnectingState();
+        allowConnection(false);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).disconnect(any());
+    }
+
+    @Test
+    public void testStackEvent_toConnectingState_onDisconnectingState() {
+        initToDisconnectingState();
+        allowConnection(true);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Connecting.class);
+    }
+
+    @Test
+    public void testStackEvent_toConnectingState_butNotAllowed_onDisconnectingState() {
+        initToDisconnectingState();
+        allowConnection(false);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        mStateMachine.sendMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event);
+        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
+        verify(mNativeInterface).disconnect(any());
+    }
+
+    private void initToConnectingState() {
+        allowConnection(true);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTING;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Connecting.class);
+        allowConnection(false);
+    }
+
+    private void initToConnectedState() {
+        allowConnection(true);
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_CONNECTED;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Connected.class);
+        allowConnection(false);
+    }
+
+    private void initToDisconnectingState() {
+        initToConnectingState();
+        CsipSetCoordinatorStackEvent event = new CsipSetCoordinatorStackEvent(
+                CsipSetCoordinatorStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
+        event.valueInt1 = CsipSetCoordinatorStackEvent.CONNECTION_STATE_DISCONNECTING;
+        sendMessageAndVerifyTransition(
+                mStateMachine.obtainMessage(CsipSetCoordinatorStateMachine.STACK_EVENT, event),
+                CsipSetCoordinatorStateMachine.Disconnecting.class);
+    }
+
+    private <T> void sendMessageAndVerifyTransition(Message msg, Class<T> type) {
+        Mockito.clearInvocations(mService);
+        mStateMachine.sendMessage(msg);
+        // Verify that one connection state broadcast is executed
+        verify(mService, timeout(TIMEOUT_MS)).sendBroadcast(any(Intent.class), anyString());
+        Assert.assertThat(mStateMachine.getCurrentState(), IsInstanceOf.instanceOf(type));
+    }
+
+    public static class CsipSetCoordinatorStateMachineWrapper
+            extends CsipSetCoordinatorStateMachine {
+
+        CsipSetCoordinatorStateMachineWrapper(BluetoothDevice device,
+                CsipSetCoordinatorService svc,
+                CsipSetCoordinatorNativeInterface nativeInterface, Looper looper) {
+            super(device, svc, nativeInterface, looper);
+        }
+
+        public boolean doesSuperHaveDeferredMessages(int what) {
+            return super.hasDeferredMessages(what);
+        }
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
index 90a1a56..a73d24c 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
@@ -1,8 +1,49 @@
+/*
+ * Copyright 2023 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.gatt;
 
-import static org.mockito.Mockito.*;
+import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
+import android.bluetooth.IBluetoothGattCallback;
+import android.bluetooth.IBluetoothGattServerCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.IAdvertisingSetCallback;
+import android.bluetooth.le.IPeriodicAdvertisingCallback;
+import android.bluetooth.le.IScannerCallback;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
+import android.bluetooth.le.ScanSettings;
+import android.content.AttributionSource;
 import android.content.Context;
+import android.os.Binder;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.WorkSource;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -23,18 +64,41 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
 /**
  * Test cases for {@link GattService}.
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class GattServiceTest {
+
+    private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
+
     private static final int TIMES_UP_AND_DOWN = 3;
     private Context mTargetContext;
     private GattService mService;
+    @Mock private GattService.ClientMap mClientMap;
+    @Mock private GattService.ScannerMap mScannerMap;
+    @Mock private GattService.ScannerMap.App mApp;
+    @Mock private GattService.PendingIntentInfo mPiInfo;
+    @Mock private PeriodicScanManager mPeriodicScanManager;
+    @Mock private ScanManager mScanManager;
+    @Mock private Set<String> mReliableQueue;
+    @Mock private GattService.ServerMap mServerMap;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
+    private BluetoothDevice mDevice;
+    private BluetoothAdapter mAdapter;
+    private AttributionSource mAttributionSource;
+
     @Mock private AdapterService mAdapterService;
 
     @Before
@@ -44,9 +108,21 @@
         MockitoAnnotations.initMocks(this);
         TestUtils.setAdapterService(mAdapterService);
         doReturn(true).when(mAdapterService).isStartedProfile(anyString());
+
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mAttributionSource = mAdapter.getAttributionSource();
+        mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(REMOTE_DEVICE_ADDRESS);
+
         TestUtils.startService(mServiceRule, GattService.class);
         mService = GattService.getGattService();
         Assert.assertNotNull(mService);
+
+        mService.mClientMap = mClientMap;
+        mService.mScannerMap = mScannerMap;
+        mService.mPeriodicScanManager = mPeriodicScanManager;
+        mService.mScanManager = mScanManager;
+        mService.mReliableQueue = mReliableQueue;
+        mService.mServerMap = mServerMap;
     }
 
     @After
@@ -63,7 +139,7 @@
 
     @Test
     public void testInitialize() {
-        Assert.assertNotNull(GattService.getGattService());
+        Assert.assertEquals(mService, GattService.getGattService());
     }
 
     @Test
@@ -92,4 +168,523 @@
         });
         Assert.assertEquals(99700000000L, timestampNanos);
     }
+
+    public void emptyClearServices() {
+        int serverIf = 1;
+
+        mService.clearServices(serverIf, mAttributionSource);
+    }
+
+    @Test
+    public void clientReadPhy() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.clientReadPhy(clientIf, address, mAttributionSource);
+    }
+
+    @Test
+    public void clientSetPreferredPhy() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int txPhy = 2;
+        int rxPhy = 1;
+        int phyOptions = 3;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.clientSetPreferredPhy(clientIf, address, txPhy, rxPhy, phyOptions,
+                mAttributionSource);
+    }
+
+    @Test
+    public void connectionParameterUpdate() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+
+        int connectionPriority = BluetoothGatt.CONNECTION_PRIORITY_HIGH;
+        mService.connectionParameterUpdate(clientIf, address, connectionPriority,
+                mAttributionSource);
+
+        connectionPriority = BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER;
+        mService.connectionParameterUpdate(clientIf, address, connectionPriority,
+                mAttributionSource);
+
+        connectionPriority = BluetoothGatt.CONNECTION_PRIORITY_BALANCED;;
+        mService.connectionParameterUpdate(clientIf, address, connectionPriority,
+                mAttributionSource);
+    }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        mService.dump(new StringBuilder());
+    }
+
+    @Test
+    public void continuePiStartScan() {
+        int scannerId = 1;
+
+        mPiInfo.settings = new ScanSettings.Builder().build();
+        mApp.info = mPiInfo;
+
+        AppScanStats appScanStats = mock(AppScanStats.class);
+        doReturn(appScanStats).when(mScannerMap).getAppScanStatsById(scannerId);
+
+        mService.continuePiStartScan(scannerId, mApp);
+
+        verify(appScanStats).recordScanStart(
+                mPiInfo.settings, mPiInfo.filters, false, false, scannerId);
+        verify(mScanManager).startScan(any());
+    }
+
+    @Test
+    public void onBatchScanReportsInternal_deliverBatchScan() throws RemoteException {
+        int status = 1;
+        int scannerId = 2;
+        int reportType = ScanManager.SCAN_RESULT_TYPE_FULL;
+        int numRecords = 1;
+        byte[] recordData = new byte[]{0x01, 0x02, 0x03, 0x04, 0x05,
+                0x06, 0x07, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00};
+
+        Set<ScanClient> scanClientSet = new HashSet<>();
+        ScanClient scanClient = new ScanClient(scannerId);
+        scanClient.associatedDevices = new ArrayList<>();
+        scanClient.associatedDevices.add("02:00:00:00:00:00");
+        scanClient.scannerId = scannerId;
+        scanClientSet.add(scanClient);
+        doReturn(scanClientSet).when(mScanManager).getFullBatchScanQueue();
+        doReturn(mApp).when(mScannerMap).getById(scanClient.scannerId);
+
+        mService.onBatchScanReportsInternal(status, scannerId, reportType, numRecords, recordData);
+        verify(mScanManager).callbackDone(scannerId, status);
+
+        reportType = ScanManager.SCAN_RESULT_TYPE_TRUNCATED;
+        recordData = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+                0x06, 0x04, 0x02, 0x02, 0x00, 0x00, 0x02};
+        doReturn(scanClientSet).when(mScanManager).getBatchScanQueue();
+        IScannerCallback callback = mock(IScannerCallback.class);
+        mApp.callback = callback;
+
+        mService.onBatchScanReportsInternal(status, scannerId, reportType, numRecords, recordData);
+        verify(callback).onBatchScanResults(any());
+    }
+
+    @Test
+    public void disconnectAll() {
+        Map<Integer, String> connMap = new HashMap<>();
+        int clientIf = 1;
+        String address = "02:00:00:00:00:00";
+        connMap.put(clientIf, address);
+        doReturn(connMap).when(mClientMap).getConnectedMap();
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.disconnectAll(mAttributionSource);
+    }
+
+    @Test
+    public void enforceReportDelayFloor() {
+        long reportDelayFloorHigher = GattService.DEFAULT_REPORT_DELAY_FLOOR + 1;
+        ScanSettings scanSettings = new ScanSettings.Builder()
+                .setReportDelay(reportDelayFloorHigher)
+                .build();
+
+        ScanSettings newScanSettings = mService.enforceReportDelayFloor(scanSettings);
+
+        assertThat(newScanSettings.getReportDelayMillis())
+                .isEqualTo(scanSettings.getReportDelayMillis());
+
+        ScanSettings scanSettingsFloor = new ScanSettings.Builder()
+                .setReportDelay(1)
+                .build();
+
+        ScanSettings newScanSettingsFloor = mService.enforceReportDelayFloor(scanSettingsFloor);
+
+        assertThat(newScanSettingsFloor.getReportDelayMillis())
+                .isEqualTo(GattService.DEFAULT_REPORT_DELAY_FLOOR);
+    }
+
+    @Test
+    public void setAdvertisingData() {
+        int advertiserId = 1;
+        AdvertiseData data = new AdvertiseData.Builder().build();
+
+        mService.setAdvertisingData(advertiserId, data, mAttributionSource);
+    }
+
+    @Test
+    public void setAdvertisingParameters() {
+        int advertiserId = 1;
+        AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build();
+
+        mService.setAdvertisingParameters(advertiserId, parameters, mAttributionSource);
+    }
+
+    @Test
+    public void setPeriodicAdvertisingData() {
+        int advertiserId = 1;
+        AdvertiseData data = new AdvertiseData.Builder().build();
+
+        mService.setPeriodicAdvertisingData(advertiserId, data, mAttributionSource);
+    }
+
+    @Test
+    public void setPeriodicAdvertisingEnable() {
+        int advertiserId = 1;
+        boolean enable = true;
+
+        mService.setPeriodicAdvertisingEnable(advertiserId, enable, mAttributionSource);
+    }
+
+    @Test
+    public void setPeriodicAdvertisingParameters() {
+        int advertiserId = 1;
+        PeriodicAdvertisingParameters parameters =
+                new PeriodicAdvertisingParameters.Builder().build();
+
+        mService.setPeriodicAdvertisingParameters(advertiserId, parameters, mAttributionSource);
+    }
+
+    @Test
+    public void setScanResponseData() {
+        int advertiserId = 1;
+        AdvertiseData data = new AdvertiseData.Builder().build();
+
+        mService.setScanResponseData(advertiserId, data, mAttributionSource);
+    }
+
+    @Test
+    public void getDevicesMatchingConnectionStates() {
+        int[] states = new int[] {BluetoothProfile.STATE_CONNECTED};
+
+        BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05");
+        BluetoothDevice[] bluetoothDevices = new BluetoothDevice[]{testDevice};
+        doReturn(bluetoothDevices).when(mAdapterService).getBondedDevices();
+
+        Set<String> connectedDevices = new HashSet<>();
+        String address = "02:00:00:00:00:00";
+        connectedDevices.add(address);
+        doReturn(connectedDevices).when(mClientMap).getConnectedDevices();
+
+        List<BluetoothDevice> deviceList =
+                mService.getDevicesMatchingConnectionStates(states, mAttributionSource);
+
+        int expectedSize = 1;
+        assertThat(deviceList.size()).isEqualTo(expectedSize);
+
+        BluetoothDevice bluetoothDevice = deviceList.get(0);
+        assertThat(bluetoothDevice.getAddress()).isEqualTo(address);
+    }
+
+    @Test
+    public void registerClient() {
+        UUID uuid = UUID.randomUUID();
+        IBluetoothGattCallback callback = mock(IBluetoothGattCallback.class);
+        boolean eattSupport = true;
+
+        mService.registerClient(uuid, callback, eattSupport, mAttributionSource);
+    }
+
+    @Test
+    public void unregisterClient() {
+        int clientIf = 3;
+
+        mService.unregisterClient(clientIf, mAttributionSource);
+        verify(mClientMap).remove(clientIf);
+    }
+
+    @Test
+    public void registerScanner() throws Exception {
+        IScannerCallback callback = mock(IScannerCallback.class);
+        WorkSource workSource = mock(WorkSource.class);
+
+        AppScanStats appScanStats = mock(AppScanStats.class);
+        doReturn(appScanStats).when(mScannerMap).getAppScanStatsByUid(Binder.getCallingUid());
+
+        mService.registerScanner(callback, workSource, mAttributionSource);
+        verify(mScannerMap).add(any(), eq(workSource), eq(callback), eq(null), eq(mService));
+        verify(mScanManager).registerScanner(any());
+    }
+
+    @Test
+    public void flushPendingBatchResults() {
+        int scannerId = 3;
+
+        mService.flushPendingBatchResults(scannerId, mAttributionSource);
+        verify(mScanManager).flushBatchScanResults(new ScanClient(scannerId));
+    }
+
+    @Test
+    public void readCharacteristic() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int handle = 2;
+        int authReq = 3;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.readCharacteristic(clientIf, address, handle, authReq, mAttributionSource);
+    }
+
+    @Test
+    public void readUsingCharacteristicUuid() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        UUID uuid = UUID.randomUUID();
+        int startHandle = 2;
+        int endHandle = 3;
+        int authReq = 4;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.readUsingCharacteristicUuid(clientIf, address, uuid, startHandle, endHandle,
+                authReq, mAttributionSource);
+    }
+
+    @Test
+    public void writeCharacteristic() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int handle = 2;
+        int writeType = 3;
+        int authReq = 4;
+        byte[] value = new byte[] {5, 6};
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        int writeCharacteristicResult = mService.writeCharacteristic(clientIf, address, handle,
+                writeType, authReq, value, mAttributionSource);
+        assertThat(writeCharacteristicResult)
+                .isEqualTo(BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED);
+    }
+
+    @Test
+    public void readDescriptor() throws Exception {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int handle = 2;
+        int authReq = 3;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.readDescriptor(clientIf, address, handle, authReq, mAttributionSource);
+    }
+
+    @Test
+    public void beginReliableWrite() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+
+        mService.beginReliableWrite(clientIf, address, mAttributionSource);
+        verify(mReliableQueue).add(address);
+    }
+
+    @Test
+    public void endReliableWrite() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        boolean execute = true;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.endReliableWrite(clientIf, address, execute, mAttributionSource);
+        verify(mReliableQueue).remove(address);
+    }
+
+    @Test
+    public void registerForNotification() throws Exception {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int handle = 2;
+        boolean enable = true;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.registerForNotification(clientIf, address, handle, enable, mAttributionSource);
+    }
+
+    @Test
+    public void readRemoteRssi() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+
+        mService.readRemoteRssi(clientIf, address, mAttributionSource);
+    }
+
+    @Test
+    public void configureMTU() {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int mtu = 2;
+
+        Integer connId = 1;
+        doReturn(connId).when(mClientMap).connIdByAddress(clientIf, address);
+
+        mService.configureMTU(clientIf, address, mtu, mAttributionSource);
+    }
+
+    @Test
+    public void leConnectionUpdate() throws Exception {
+        int clientIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int minInterval = 3;
+        int maxInterval = 4;
+        int peripheralLatency = 5;
+        int supervisionTimeout = 6;
+        int minConnectionEventLen = 7;
+        int maxConnectionEventLen = 8;
+
+        mService.leConnectionUpdate(clientIf, address, minInterval, maxInterval,
+                peripheralLatency, supervisionTimeout, minConnectionEventLen,
+                maxConnectionEventLen, mAttributionSource);
+    }
+
+    @Test
+    public void serverConnect() {
+        int serverIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        boolean isDirect = true;
+        int transport = 2;
+
+        mService.serverConnect(serverIf, address, isDirect, transport, mAttributionSource);
+    }
+
+    @Test
+    public void serverDisconnect() {
+        int serverIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+
+        Integer connId = 1;
+        doReturn(connId).when(mServerMap).connIdByAddress(serverIf, address);
+
+        mService.serverDisconnect(serverIf, address, mAttributionSource);
+    }
+
+    @Test
+    public void serverSetPreferredPhy() throws Exception {
+        int serverIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int txPhy = 2;
+        int rxPhy = 1;
+        int phyOptions = 3;
+
+        mService.serverSetPreferredPhy(serverIf, address, txPhy, rxPhy, phyOptions,
+                mAttributionSource);
+    }
+
+    @Test
+    public void serverReadPhy() {
+        int serverIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+
+        mService.serverReadPhy(serverIf, address, mAttributionSource);
+    }
+
+    @Test
+    public void sendNotification() throws Exception {
+        int serverIf = 1;
+        String address = REMOTE_DEVICE_ADDRESS;
+        int handle = 2;
+        boolean confirm = true;
+        byte[] value = new byte[] {5, 6};;
+
+        Integer connId = 1;
+        doReturn(connId).when(mServerMap).connIdByAddress(serverIf, address);
+
+        mService.sendNotification(serverIf, address, handle, confirm, value, mAttributionSource);
+
+        confirm = false;
+
+        mService.sendNotification(serverIf, address, handle, confirm, value, mAttributionSource);
+    }
+
+    @Test
+    public void getOwnAddress() throws Exception {
+        int advertiserId = 1;
+
+        mService.getOwnAddress(advertiserId, mAttributionSource);
+    }
+
+    @Test
+    public void enableAdvertisingSet() throws Exception {
+        int advertiserId = 1;
+        boolean enable = true;
+        int duration = 3;
+        int maxExtAdvEvents = 4;
+
+        mService.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents,
+                mAttributionSource);
+    }
+
+    @Test
+    public void registerSync() {
+        ScanResult scanResult = new ScanResult(mDevice, 1, 2, 3, 4, 5, 6, 7, null, 8);
+        int skip = 1;
+        int timeout = 2;
+        IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
+
+        mService.registerSync(scanResult, skip, timeout, callback, mAttributionSource);
+        verify(mPeriodicScanManager).startSync(scanResult, skip, timeout, callback);
+    }
+
+    @Test
+    public void transferSync() {
+        int serviceData = 1;
+        int syncHandle = 2;
+
+        mService.transferSync(mDevice, serviceData, syncHandle, mAttributionSource);
+        verify(mPeriodicScanManager).transferSync(mDevice, serviceData, syncHandle);
+    }
+
+    @Test
+    public void transferSetInfo() {
+        int serviceData = 1;
+        int advHandle = 2;
+        IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
+
+        mService.transferSetInfo(mDevice, serviceData, advHandle, callback,
+                mAttributionSource);
+        verify(mPeriodicScanManager).transferSetInfo(mDevice, serviceData, advHandle, callback);
+    }
+
+    @Test
+    public void unregisterSync() {
+        IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
+
+        mService.unregisterSync(callback, mAttributionSource);
+        verify(mPeriodicScanManager).stopSync(callback);
+    }
+
+    @Test
+    public void unregAll() throws Exception {
+        int appId = 1;
+        List<Integer> appIds = new ArrayList<>();
+        appIds.add(appId);
+        doReturn(appIds).when(mClientMap).getAllAppsIds();
+
+        mService.unregAll(mAttributionSource);
+        verify(mClientMap).remove(appId);
+    }
+
+    @Test
+    public void numHwTrackFiltersAvailable() {
+        mService.numHwTrackFiltersAvailable(mAttributionSource);
+        verify(mScanManager).getCurrentUsedTrackingAdvertisement();
+    }
+
+    @Test
+    public void cleanUp_doesNotCrash() {
+        mService.cleanup();
+    }
 }
+
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
index 1e82632..3c2819e 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
@@ -1052,6 +1052,24 @@
 
     }
 
+    @Test
+    public void testDumpDoesNotCrash() {
+        // Update the device policy so okToConnect() returns true
+        when(mDatabaseManager
+                .getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        doReturn(true).when(mNativeInterface).connectHapClient(any(BluetoothDevice.class));
+        doReturn(true).when(mNativeInterface).disconnectHapClient(any(BluetoothDevice.class));
+
+        doReturn(new ParcelUuid[]{BluetoothUuid.HAS}).when(mAdapterService)
+                .getRemoteUuids(any(BluetoothDevice.class));
+
+        // Add state machine for testing dump()
+        mService.connect(mDevice);
+
+        mService.dump(new StringBuilder());
+    }
+
     /**
      * Helper function to test device connecting
      */
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
index cf74dfd..5478dd5 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java
@@ -1113,6 +1113,20 @@
         verify(mNativeInterface).setVolume(0);
     }
 
+    @Test
+    public void dump_doesNotCrash() {
+        // Update the device priority so okToConnect() returns true
+        when(mDatabaseManager
+                .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
+                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
+
+        // Send a connect request
+        mService.connect(mSingleDevice);
+
+        mService.dump(new StringBuilder());
+    }
+
     private void connectDevice(BluetoothDevice device) {
         HearingAidStackEvent connCompletedEvent;
 
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
index a511753..e85dc17 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceTest.java
@@ -44,6 +44,7 @@
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -100,6 +101,7 @@
         Assert.assertNotNull(HeadsetClientService.getHeadsetClientService());
     }
 
+    @Ignore("b/260202548")
     @Test
     public void testSendBIEVtoStateMachineWhenBatteryChanged() {
         // Put mock state machine
@@ -136,4 +138,14 @@
                     eq(2),
                     anyInt());
     }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        // Put mock state machine
+        BluetoothDevice device =
+                BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
+        mService.getStateMachineMap().put(device, mStateMachine);
+
+        mService.dump(new StringBuilder());
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HfpNativeInterfaceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HfpNativeInterfaceTest.java
index 7c5fd6b..771e476 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HfpNativeInterfaceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HfpNativeInterfaceTest.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 
 import org.junit.After;
@@ -45,14 +46,14 @@
         MockitoAnnotations.initMocks(this);
         when(mService.isAvailable()).thenReturn(true);
         HeadsetClientService.setHeadsetClientService(mService);
-        AdapterService.setAdapterService(mAdapterService);
+        TestUtils.setAdapterService(mAdapterService);
         mNativeInterface = NativeInterface.getInstance();
     }
 
     @After
-    public void tearDown() {
+    public void tearDown() throws Exception {
         HeadsetClientService.setHeadsetClientService(null);
-        AdapterService.setAdapterService(null);
+        TestUtils.clearAdapterService(mAdapterService);
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionServiceTest.java
index 0692a68..7bf69c1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/connserv/HfpClientConnectionServiceTest.java
@@ -89,11 +89,6 @@
 
         Context targetContext = InstrumentationRegistry.getTargetContext();
 
-        // HfpClientConnectionService is only enabled for some form factors, and the tests should
-        // only be run if the service is enabled.
-        assumeTrue(targetContext.getResources()
-                .getBoolean(R.bool.hfp_client_connection_service_enabled));
-
         // Setup a mock TelecomManager so our calls don't start a real instance of this service
         doNothing().when(mMockTelecomManager).addNewIncomingCall(any(), any());
         doNothing().when(mMockTelecomManager).addNewUnknownCall(any(), any());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceNativeInterfaceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceNativeInterfaceTest.java
index 75e4f9c..fd70351 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceNativeInterfaceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceNativeInterfaceTest.java
@@ -24,6 +24,7 @@
 
 import android.bluetooth.BluetoothHidDevice;
 
+import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 
 import org.junit.After;
@@ -47,14 +48,14 @@
         MockitoAnnotations.initMocks(this);
         when(mService.isAvailable()).thenReturn(true);
         HidDeviceService.setHidDeviceService(mService);
-        AdapterService.setAdapterService(mAdapterService);
+        TestUtils.setAdapterService(mAdapterService);
         mNativeInterface = HidDeviceNativeInterface.getInstance();
     }
 
     @After
-    public void tearDown() {
+    public void tearDown() throws Exception {
         HidDeviceService.setHidDeviceService(null);
-        AdapterService.setAdapterService(null);
+        TestUtils.clearAdapterService(mAdapterService);
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
index 4122c33..a9667d0 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java
@@ -133,6 +133,11 @@
                 badBondState, badPriorityValue, false);
     }
 
+    @Test
+    public void testDumpDoesNotCrash() {
+        mService.dump(new StringBuilder());
+    }
+
     /**
      * Helper function to test okToConnect() method.
      *
@@ -155,5 +160,4 @@
         doReturn(true).when(mAdapterService).isQuietModeEnabled();
         Assert.assertEquals(false, mService.okToConnect(device));
     }
-
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
index 683146a..d2c7cd1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java
@@ -68,6 +68,7 @@
 import org.junit.After;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -1127,6 +1128,7 @@
      * Test native interface audio configuration changed message handling
      */
     @Test
+    @Ignore("b/258573934")
     public void testMessageFromNativeAudioConfChangedActiveGroup() {
         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
         connectTestDevice(mSingleDevice, testGroupId);
@@ -1312,6 +1314,7 @@
      * Test native interface group status message handling
      */
     @Test
+    @Ignore("b/258573934")
     public void testLeadGroupDeviceDisconnects() {
         int groupId = 1;
         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
@@ -1381,6 +1384,7 @@
      * Test native interface group status message handling
      */
     @Test
+    @Ignore("b/258573934")
     public void testLeadGroupDeviceReconnects() {
         int groupId = 1;
         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java
index 1eb20d5..5661492 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentObserverTest.java
@@ -18,9 +18,11 @@
 
 import static org.mockito.Mockito.*;
 
+import android.app.Activity;
 import android.content.ContentProviderClient;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.database.sqlite.SQLiteException;
@@ -29,6 +31,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.provider.Telephony;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.Sms;
 import android.telephony.TelephonyManager;
@@ -44,6 +47,8 @@
 import com.android.bluetooth.mapapi.BluetoothMapContract;
 import com.android.obex.ResponseCodes;
 
+import com.google.android.mms.pdu.PduHeaders;
+
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Assume;
@@ -66,9 +71,10 @@
 public class BluetoothMapContentObserverTest {
     static final String TEST_NUMBER_ONE = "5551212";
     static final String TEST_NUMBER_TWO = "5551234";
-    static final int TEST_MAS_ID = 1;
-    static final long TEST_HANDLE = 1;
-    static final String TEST_URI_STR = "test_uri_str";
+    static final int TEST_ID = 1;
+    static final long TEST_HANDLE_ONE = 1;
+    static final long TEST_HANDLE_TWO = 2;
+    static final String TEST_URI_STR = "http://www.google.com";
     static final int TEST_STATUS_VALUE = 1;
     static final int TEST_THREAD_ID = 1;
     static final long TEST_OLD_THREAD_ID = 2;
@@ -76,7 +82,34 @@
     static final String TEST_ADDRESS = "test_address";
     static final long TEST_DELETE_FOLDER_ID = BluetoothMapContract.FOLDER_ID_DELETED;
     static final long TEST_INBOX_FOLDER_ID = BluetoothMapContract.FOLDER_ID_INBOX;
+    static final long TEST_SENT_FOLDER_ID = BluetoothMapContract.FOLDER_ID_SENT;
+    static final long TEST_DRAFT_FOLDER_ID = BluetoothMapContract.FOLDER_ID_DRAFT;
     static final long TEST_OLD_FOLDER_ID = 6;
+    static final int TEST_READ_FLAG_ONE = 1;
+    static final int TEST_READ_FLAG_ZERO = 0;
+    static final long TEST_DATE = 1;
+    static final String TEST_SUBJECT = "subject";
+    static final int TEST_MMS_MTYPE = 1;
+    static final int TEST_MMS_TYPE_ALL = Telephony.BaseMmsColumns.MESSAGE_BOX_ALL;
+    static final int TEST_MMS_TYPE_INBOX = Telephony.BaseMmsColumns.MESSAGE_BOX_INBOX;
+    static final Uri TEST_URI = Mms.CONTENT_URI;
+    static final String TEST_AUTHORITY = "test_authority";
+
+    static final long TEST_CONVO_ID = 1;
+    static final String TEST_NAME = "col_name";
+    static final String TEST_DISPLAY_NAME = "col_nickname";
+    static final String TEST_BT_UID = "1111";
+    static final int TEST_CHAT_STATE = 1;
+    static final int TEST_CHAT_STATE_DIFFERENT = 2;
+    static final String TEST_UCI = "col_uci";
+    static final String TEST_UCI_DIFFERENT = "col_uci_different";
+    static final long TEST_LAST_ACTIVITY = 1;
+    static final int TEST_PRESENCE_STATE = 1;
+    static final int TEST_PRESENCE_STATE_DIFFERENT = 2;
+    static final String TEST_STATUS_TEXT = "col_status_text";
+    static final String TEST_STATUS_TEXT_DIFFERENT = "col_status_text_different";
+    static final int TEST_PRIORITY = 1;
+    static final int TEST_LAST_ONLINE = 1;
 
     @Mock
     private BluetoothMnsObexClient mClient;
@@ -90,6 +123,10 @@
     private Context mContext;
     @Mock
     private ContentProviderClient mProviderClient;
+    @Mock
+    private BluetoothMapAccountItem mItem;
+    @Mock
+    private Intent mIntent;
     @Spy
     private BluetoothMethodProxy mMapMethodProxy = BluetoothMethodProxy.getInstance();
 
@@ -150,9 +187,12 @@
                 .thenReturn(Context.TELEPHONY_SERVICE);
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserService);
         when(mContext.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
-        when(mInstance.getMasId()).thenReturn(TEST_MAS_ID);
+        when(mInstance.getMasId()).thenReturn(TEST_ID);
 
         mObserver = new BluetoothMapContentObserver(mContext, mClient, mInstance, null, true);
+        mObserver.mProviderClient = mProviderClient;
+        mObserver.mAccount = mItem;
+        when(mItem.getType()).thenReturn(TYPE.IM);
     }
 
     @After
@@ -213,8 +253,8 @@
         mObserver.setNotificationFilter(0);
 
         String eventType = BluetoothMapContentObserver.EVENT_TYPE_NEW;
-        BluetoothMapContentObserver.Event event = mObserver.new Event(eventType, TEST_HANDLE, null,
-                null);
+        BluetoothMapContentObserver.Event event = mObserver.new Event(eventType, TEST_HANDLE_ONE,
+                null, null);
         mObserver.sendEvent(event);
         verify(mClient, never()).sendEvent(any(), anyInt());
 
@@ -264,12 +304,12 @@
         when(mClient.isConnected()).thenReturn(true);
 
         String eventType = BluetoothMapContentObserver.EVENT_TYPE_NEW;
-        BluetoothMapContentObserver.Event event = mObserver.new Event(eventType, TEST_HANDLE, null,
-                null);
+        BluetoothMapContentObserver.Event event = mObserver.new Event(eventType, TEST_HANDLE_ONE,
+                null, null);
 
         mObserver.sendEvent(event);
 
-        verify(mClient).sendEvent(event.encode(), TEST_MAS_ID);
+        verify(mClient).sendEvent(event.encode(), TEST_ID);
     }
 
     @Test
@@ -350,12 +390,12 @@
         TYPE type = TYPE.SMS_GSM;
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListSms(map, true);
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
-        Assert.assertTrue(mObserver.setMessageStatusRead(TEST_HANDLE, type, TEST_URI_STR,
+        Assert.assertTrue(mObserver.setMessageStatusRead(TEST_HANDLE_ONE, type, TEST_URI_STR,
                 TEST_STATUS_VALUE));
 
         Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE);
@@ -366,12 +406,12 @@
         TYPE type = TYPE.MMS;
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMms(map, true);
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
-        Assert.assertTrue(mObserver.setMessageStatusRead(TEST_HANDLE, type, TEST_URI_STR,
+        Assert.assertTrue(mObserver.setMessageStatusRead(TEST_HANDLE_ONE, type, TEST_URI_STR,
                 TEST_STATUS_VALUE));
 
         Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE);
@@ -382,12 +422,12 @@
         TYPE type = TYPE.EMAIL;
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMsg(map, true);
         mObserver.mProviderClient = mProviderClient;
         when(mProviderClient.update(any(), any(), any(), any())).thenReturn(TEST_PLACEHOLDER_INT);
 
-        Assert.assertTrue(mObserver.setMessageStatusRead(TEST_HANDLE, type, TEST_URI_STR,
+        Assert.assertTrue(mObserver.setMessageStatusRead(TEST_HANDLE_ONE, type, TEST_URI_STR,
                 TEST_STATUS_VALUE));
 
         Assert.assertEquals(msg.flagRead, TEST_STATUS_VALUE);
@@ -398,7 +438,7 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMms(map, true);
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
 
@@ -409,7 +449,7 @@
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
-        Assert.assertTrue(mObserver.deleteMessageMms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.deleteMessageMms(TEST_HANDLE_ONE));
 
         Assert.assertEquals(msg.threadId, BluetoothMapContentObserver.DELETED_THREAD_ID);
     }
@@ -419,9 +459,9 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMms(map, true);
-        Assert.assertNotNull(mObserver.getMsgListMms().get(TEST_HANDLE));
+        Assert.assertNotNull(mObserver.getMsgListMms().get(TEST_HANDLE_ONE));
 
         MatrixCursor cursor = new MatrixCursor(new String[] {Mms.THREAD_ID});
         cursor.addRow(new Object[] {BluetoothMapContentObserver.DELETED_THREAD_ID});
@@ -430,9 +470,9 @@
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverDelete(any(), any(),
                 any(), any());
 
-        Assert.assertTrue(mObserver.deleteMessageMms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.deleteMessageMms(TEST_HANDLE_ONE));
 
-        Assert.assertNull(mObserver.getMsgListMms().get(TEST_HANDLE));
+        Assert.assertNull(mObserver.getMsgListMms().get(TEST_HANDLE_ONE));
     }
 
     @Test
@@ -440,7 +480,7 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListSms(map, true);
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
 
@@ -451,7 +491,7 @@
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
-        Assert.assertTrue(mObserver.deleteMessageSms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.deleteMessageSms(TEST_HANDLE_ONE));
 
         Assert.assertEquals(msg.threadId, BluetoothMapContentObserver.DELETED_THREAD_ID);
     }
@@ -461,9 +501,9 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListSms(map, true);
-        Assert.assertNotNull(mObserver.getMsgListSms().get(TEST_HANDLE));
+        Assert.assertNotNull(mObserver.getMsgListSms().get(TEST_HANDLE_ONE));
 
         MatrixCursor cursor = new MatrixCursor(new String[] {Mms.THREAD_ID});
         cursor.addRow(new Object[] {BluetoothMapContentObserver.DELETED_THREAD_ID});
@@ -472,9 +512,9 @@
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverDelete(any(), any(),
                 any(), any());
 
-        Assert.assertTrue(mObserver.deleteMessageSms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.deleteMessageSms(TEST_HANDLE_ONE));
 
-        Assert.assertNull(mObserver.getMsgListSms().get(TEST_HANDLE));
+        Assert.assertNull(mObserver.getMsgListSms().get(TEST_HANDLE_ONE));
     }
 
     @Test
@@ -482,7 +522,7 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMms(map, true);
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
         Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_ALL);
@@ -498,7 +538,7 @@
         doReturn(TEST_OLD_THREAD_ID).when(mMapMethodProxy).telephonyGetOrCreateThreadId(any(),
                 any());
 
-        Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE));
 
         Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID);
         Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_INBOX);
@@ -509,7 +549,7 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMms(map, true);
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
         Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_ALL);
@@ -525,7 +565,7 @@
         doReturn(TEST_OLD_THREAD_ID).when(mMapMethodProxy).telephonyGetOrCreateThreadId(any(),
                 any());
 
-        Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE));
 
         Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID);
         Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_INBOX);
@@ -536,7 +576,7 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Mms.MESSAGE_BOX_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMms(map, true);
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
         Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_ALL);
@@ -549,7 +589,7 @@
         doReturn(TEST_OLD_THREAD_ID).when(mMapMethodProxy).telephonyGetOrCreateThreadId(any(),
                 any());
 
-        Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.unDeleteMessageMms(TEST_HANDLE_ONE));
 
         // Nothing changes when thread id is not BluetoothMapContentObserver.DELETED_THREAD_ID
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
@@ -561,7 +601,7 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListSms(map, true);
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
         Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_ALL);
@@ -576,7 +616,7 @@
         doReturn(TEST_OLD_THREAD_ID).when(mMapMethodProxy).telephonyGetOrCreateThreadId(any(),
                 any());
 
-        Assert.assertTrue(mObserver.unDeleteMessageSms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE));
 
         Assert.assertEquals(msg.threadId, TEST_OLD_THREAD_ID);
         Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_INBOX);
@@ -587,7 +627,7 @@
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createMsgWithTypeAndThreadId(Sms.MESSAGE_TYPE_ALL,
                 TEST_THREAD_ID);
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListSms(map, true);
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
         Assert.assertEquals(msg.type, Sms.MESSAGE_TYPE_ALL);
@@ -600,7 +640,7 @@
         doReturn(TEST_OLD_THREAD_ID).when(mMapMethodProxy).telephonyGetOrCreateThreadId(any(),
                 any());
 
-        Assert.assertTrue(mObserver.unDeleteMessageSms(TEST_HANDLE));
+        Assert.assertTrue(mObserver.unDeleteMessageSms(TEST_HANDLE_ONE));
 
         // Nothing changes when thread id is not BluetoothMapContentObserver.DELETED_THREAD_ID
         Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
@@ -633,13 +673,13 @@
 
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMsg(map, true);
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
         Assert.assertTrue(mObserver.setEmailMessageStatusDelete(mCurrentFolder, TEST_URI_STR,
-                TEST_HANDLE, BluetoothMapAppParams.STATUS_VALUE_YES));
+                TEST_HANDLE_ONE, BluetoothMapAppParams.STATUS_VALUE_YES));
         Assert.assertEquals(msg.folderId, TEST_DELETE_FOLDER_ID);
     }
 
@@ -651,13 +691,13 @@
 
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMsg(map, true);
         doReturn(0).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
         Assert.assertFalse(mObserver.setEmailMessageStatusDelete(mCurrentFolder, TEST_URI_STR,
-                TEST_HANDLE, BluetoothMapAppParams.STATUS_VALUE_YES));
+                TEST_HANDLE_ONE, BluetoothMapAppParams.STATUS_VALUE_YES));
     }
 
     @Test
@@ -672,13 +712,13 @@
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
         msg.oldFolderId = TEST_OLD_FOLDER_ID;
         msg.folderId = TEST_DELETE_FOLDER_ID;
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMsg(map, true);
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
         Assert.assertTrue(mObserver.setEmailMessageStatusDelete(mCurrentFolder, TEST_URI_STR,
-                TEST_HANDLE, BluetoothMapAppParams.STATUS_VALUE_NO));
+                TEST_HANDLE_ONE, BluetoothMapAppParams.STATUS_VALUE_NO));
         Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID);
     }
 
@@ -695,13 +735,13 @@
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
         msg.oldFolderId = oldFolderId;
         msg.folderId = TEST_DELETE_FOLDER_ID;
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMsg(map, true);
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
         Assert.assertTrue(mObserver.setEmailMessageStatusDelete(mCurrentFolder, TEST_URI_STR,
-                TEST_HANDLE, BluetoothMapAppParams.STATUS_VALUE_NO));
+                TEST_HANDLE_ONE, BluetoothMapAppParams.STATUS_VALUE_NO));
         Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID);
     }
 
@@ -718,13 +758,13 @@
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
         msg.oldFolderId = TEST_OLD_FOLDER_ID;
         msg.folderId = TEST_DELETE_FOLDER_ID;
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMsg(map, true);
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
         Assert.assertTrue(mObserver.setEmailMessageStatusDelete(mCurrentFolder, TEST_URI_STR,
-                TEST_HANDLE, BluetoothMapAppParams.STATUS_VALUE_NO));
+                TEST_HANDLE_ONE, BluetoothMapAppParams.STATUS_VALUE_NO));
         Assert.assertEquals(msg.folderId, TEST_OLD_FOLDER_ID);
     }
 
@@ -736,19 +776,19 @@
 
         Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
-        map.put(TEST_HANDLE, msg);
+        map.put(TEST_HANDLE_ONE, msg);
         mObserver.setMsgListMsg(map, true);
         doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
                 any(), any(), any());
 
-        Assert.assertTrue(mObserver.setMessageStatusDeleted(TEST_HANDLE, TYPE.EMAIL, mCurrentFolder,
-                TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_YES));
+        Assert.assertTrue(mObserver.setMessageStatusDeleted(TEST_HANDLE_ONE, TYPE.EMAIL,
+                mCurrentFolder, TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_YES));
     }
 
     @Test
     public void setMessageStatusDeleted_withTypeIm() {
-        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE, TYPE.IM, mCurrentFolder,
-                TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_YES));
+        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE_ONE, TYPE.IM,
+                mCurrentFolder, TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_YES));
     }
 
     @Test
@@ -760,9 +800,9 @@
 
         // setMessageStatusDeleted with type Gsm or Mms calls either deleteMessage() or
         // unDeleteMessage(), which returns false when no cursor is set with BluetoothMethodProxy.
-        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE, TYPE.MMS, mCurrentFolder,
-                TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_NO));
-        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE, TYPE.SMS_GSM,
+        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE_ONE, TYPE.MMS,
+                mCurrentFolder, TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_NO));
+        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE_ONE, TYPE.SMS_GSM,
                 mCurrentFolder, TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_NO));
     }
 
@@ -775,25 +815,691 @@
 
         // setMessageStatusDeleted with type Gsm or Mms calls either deleteMessage() or
         // unDeleteMessage(), which returns false when no cursor is set with BluetoothMethodProxy.
-        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE, TYPE.MMS, mCurrentFolder,
-                TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_YES));
-        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE, TYPE.SMS_GSM,
+        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE_ONE, TYPE.MMS,
+                mCurrentFolder, TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_YES));
+        Assert.assertFalse(mObserver.setMessageStatusDeleted(TEST_HANDLE_ONE, TYPE.SMS_GSM,
                 mCurrentFolder, TEST_URI_STR, BluetoothMapAppParams.STATUS_VALUE_YES));
     }
 
     @Test
     public void initContactsList() throws Exception {
-        long convoId = 1;
-        String name = "col_name";
-        String displayName = "col_nickname";
-        String btUid = "1111";
-        int chatState = 1;
-        String uci = "col_uci";
-        long lastActivity = 1;
-        int presenceState = 1;
-        String statusText = "col_status_text";
-        int priority = 1;
-        int lastOnline = 1;
+        MatrixCursor cursor = new MatrixCursor(
+                new String[]{BluetoothMapContract.ConvoContactColumns.CONVO_ID,
+                        BluetoothMapContract.ConvoContactColumns.NAME,
+                        BluetoothMapContract.ConvoContactColumns.NICKNAME,
+                        BluetoothMapContract.ConvoContactColumns.X_BT_UID,
+                        BluetoothMapContract.ConvoContactColumns.CHAT_STATE,
+                        BluetoothMapContract.ConvoContactColumns.UCI,
+                        BluetoothMapContract.ConvoContactColumns.LAST_ACTIVE,
+                        BluetoothMapContract.ConvoContactColumns.PRESENCE_STATE,
+                        BluetoothMapContract.ConvoContactColumns.STATUS_TEXT,
+                        BluetoothMapContract.ConvoContactColumns.PRIORITY,
+                        BluetoothMapContract.ConvoContactColumns.LAST_ONLINE});
+        cursor.addRow(new Object[] {TEST_CONVO_ID, TEST_NAME, TEST_DISPLAY_NAME, TEST_BT_UID,
+                TEST_CHAT_STATE, TEST_UCI, TEST_LAST_ACTIVITY, TEST_PRESENCE_STATE,
+                TEST_STATUS_TEXT, TEST_PRIORITY, TEST_LAST_ONLINE});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mObserver.mContactUri = mock(Uri.class);
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<String, BluetoothMapConvoContactElement> map = new HashMap<>();
+        mObserver.setContactList(map, true);
+        mObserver.initContactsList();
+        BluetoothMapConvoContactElement contactElement = mObserver.getContactList().get(TEST_UCI);
+
+        final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
+        Assert.assertEquals(contactElement.getContactId(), TEST_UCI);
+        Assert.assertEquals(contactElement.getName(), TEST_NAME);
+        Assert.assertEquals(contactElement.getDisplayName(), TEST_DISPLAY_NAME);
+        Assert.assertEquals(contactElement.getBtUid(), TEST_BT_UID);
+        Assert.assertEquals(contactElement.getChatState(), TEST_CHAT_STATE);
+        Assert.assertEquals(contactElement.getPresenceStatus(), TEST_STATUS_TEXT);
+        Assert.assertEquals(contactElement.getPresenceAvailability(), TEST_PRESENCE_STATE);
+        Assert.assertEquals(contactElement.getLastActivityString(), format.format(
+                TEST_LAST_ACTIVITY));
+        Assert.assertEquals(contactElement.getPriority(), TEST_PRIORITY);
+    }
+
+    @Test
+    public void handleMsgListChangesMsg_withNonExistingMessage_andVersion11() throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.DATE,
+                BluetoothMapContract.MessageColumns.SUBJECT,
+                BluetoothMapContract.MessageColumns.FROM_LIST,
+                BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE,
+                TEST_DATE, TEST_SUBJECT, TEST_ADDRESS, 1});
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving a different handle for msg below and cursor above makes handleMsgListChangesMsg()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE);
+        msg.localInitiatedSend = true;
+        msg.transparent = true;
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListMsg(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+        mFolders.setFolderId(TEST_INBOX_FOLDER_ID);
+        mObserver.setFolderStructure(mFolders);
+
+        mObserver.handleMsgListChangesMsg(TEST_URI);
+
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type,
+                TEST_INBOX_FOLDER_ID);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMsg_withNonExistingMessage_andVersion12() throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.DATE,
+                BluetoothMapContract.MessageColumns.SUBJECT,
+                BluetoothMapContract.MessageColumns.FROM_LIST,
+                BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY,
+                BluetoothMapContract.MessageColumns.THREAD_ID,
+                BluetoothMapContract.MessageColumns.THREAD_NAME});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE,
+                TEST_DATE, TEST_SUBJECT, TEST_ADDRESS, 1, 1, "threadName"});
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving a different handle for msg below and cursor above makes handleMsgListChangesMsg()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE);
+        msg.localInitiatedSend = false;
+        msg.transparent = false;
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListMsg(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesMsg(TEST_URI);
+
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type,
+                TEST_INBOX_FOLDER_ID);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMsg_withNonExistingMessage_andVersion10() throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE});
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving a different handle for msg below and cursor above makes handleMsgListChangesMsg()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE);
+        msg.localInitiatedSend = false;
+        msg.transparent = false;
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListMsg(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V10;
+        mFolders.setFolderId(TEST_HANDLE_TWO);
+        mObserver.setFolderStructure(mFolders);
+
+        mObserver.handleMsgListChangesMsg(TEST_URI);
+
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).type,
+                TEST_INBOX_FOLDER_ID);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMsg_withExistingMessage_andNonNullDeletedFolder()
+            throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_DELETE_FOLDER_ID, TEST_READ_FLAG_ONE});
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMsg()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        mObserver.setMsgListMsg(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+        setFolderStructureWithTelecomAndMsg(mFolders, BluetoothMapContract.FOLDER_NAME_DELETED,
+                TEST_DELETE_FOLDER_ID);
+        mObserver.setFolderStructure(mFolders);
+
+        mObserver.handleMsgListChangesMsg(TEST_URI);
+
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId,
+                TEST_DELETE_FOLDER_ID);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMsg_withExistingMessage_andNonNullSentFolder()
+            throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SENT_FOLDER_ID, TEST_READ_FLAG_ONE});
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMsg()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ZERO);
+        msg.localInitiatedSend = true;
+        msg.transparent = false;
+        map.put(TEST_HANDLE_ONE, msg);
+        mObserver.setMsgListMsg(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+        setFolderStructureWithTelecomAndMsg(mFolders, BluetoothMapContract.FOLDER_NAME_SENT,
+                TEST_SENT_FOLDER_ID);
+        mObserver.setFolderStructure(mFolders);
+
+        mObserver.handleMsgListChangesMsg(TEST_URI);
+
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId,
+                TEST_SENT_FOLDER_ID);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMsg_withExistingMessage_andNonNullTransparentSentFolder()
+            throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SENT_FOLDER_ID, TEST_READ_FLAG_ONE});
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMsg()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ZERO);
+        msg.localInitiatedSend = true;
+        msg.transparent = true;
+        map.put(TEST_HANDLE_ONE, msg);
+        mObserver.setMsgListMsg(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+        doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverDelete(any(), any(),
+                any(), any());
+        setFolderStructureWithTelecomAndMsg(mFolders, BluetoothMapContract.FOLDER_NAME_SENT,
+                TEST_SENT_FOLDER_ID);
+        mObserver.setFolderStructure(mFolders);
+        mObserver.mMessageUri = Mms.CONTENT_URI;
+
+        mObserver.handleMsgListChangesMsg(TEST_URI);
+
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId,
+                TEST_SENT_FOLDER_ID);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMsg_withExistingMessage_andUnknownOldFolder()
+            throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE});
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMsg()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_SENT_FOLDER_ID, TEST_READ_FLAG_ZERO);
+        msg.localInitiatedSend = true;
+        msg.transparent = false;
+        map.put(TEST_HANDLE_ONE, msg);
+        mObserver.setMsgListMsg(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+        setFolderStructureWithTelecomAndMsg(mFolders, BluetoothMapContract.FOLDER_NAME_DRAFT,
+                TEST_DRAFT_FOLDER_ID);
+        mObserver.setFolderStructure(mFolders);
+
+        mObserver.handleMsgListChangesMsg(TEST_URI);
+
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).folderId,
+                TEST_INBOX_FOLDER_ID);
+        Assert.assertEquals(mObserver.getMsgListMsg().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMms_withNonExistingMessage_andVersion11() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.MESSAGE_TYPE, Mms.THREAD_ID, Mms.READ, Mms.DATE, Mms.SUBJECT,
+        Mms.PRIORITY, Mms.Addr.ADDRESS});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_MMS_TYPE_ALL, TEST_MMS_MTYPE,
+                TEST_THREAD_ID, TEST_READ_FLAG_ONE, TEST_DATE, TEST_SUBJECT,
+                PduHeaders.PRIORITY_HIGH, null});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving a different handle for msg below and cursor above makes handleMsgListChangesMms()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE);
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListMms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+
+        mObserver.handleMsgListChangesMms();
+
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMms_withNonExistingMessage_andVersion12() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.MESSAGE_TYPE, Mms.THREAD_ID, Mms.READ, Mms.DATE, Mms.SUBJECT,
+                Mms.PRIORITY, Mms.Addr.ADDRESS});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_MMS_TYPE_ALL, TEST_MMS_MTYPE,
+                TEST_THREAD_ID, TEST_READ_FLAG_ONE, TEST_DATE, TEST_SUBJECT,
+                PduHeaders.PRIORITY_HIGH, null});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving a different handle for msg below and cursor above makes handleMsgListChangesMms()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE);
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListMms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesMms();
+
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMms_withNonExistingMessage_andVersion10() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.MESSAGE_TYPE, Mms.THREAD_ID, Mms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_MMS_TYPE_ALL, TEST_MMS_MTYPE,
+                TEST_THREAD_ID, TEST_READ_FLAG_ONE});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving a different handle for msg below and cursor above makes handleMsgListChangesMms()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE);
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListMms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V10;
+
+        mObserver.handleMsgListChangesMms();
+
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMms_withExistingMessage_withNonEqualType_andLocalSendFalse() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.MESSAGE_TYPE, Mms.THREAD_ID, Mms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_MMS_TYPE_ALL, TEST_MMS_MTYPE,
+                TEST_THREAD_ID, TEST_READ_FLAG_ONE});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMms()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_MMS_TYPE_INBOX, TEST_THREAD_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        msg.localInitiatedSend = false;
+        mObserver.setMsgListMms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesMms();
+
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMms_withExistingMessage_withNonEqualType_andLocalSendTrue() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.MESSAGE_TYPE, Mms.THREAD_ID, Mms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_MMS_TYPE_ALL, TEST_MMS_MTYPE,
+                TEST_THREAD_ID, TEST_READ_FLAG_ONE});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMms()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_MMS_TYPE_INBOX, TEST_THREAD_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        msg.localInitiatedSend = true;
+        mObserver.setMsgListMms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesMms();
+
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMms_withExistingMessage_withDeletedThreadId() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.MESSAGE_TYPE, Mms.THREAD_ID, Mms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_MMS_TYPE_ALL, TEST_MMS_MTYPE,
+                BluetoothMapContentObserver.DELETED_THREAD_ID, TEST_READ_FLAG_ONE});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMms()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_MMS_TYPE_ALL, TEST_THREAD_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        msg.localInitiatedSend = true;
+        mObserver.setMsgListMms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesMms();
+
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
+                BluetoothMapContentObserver.DELETED_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesMms_withExistingMessage_withUndeletedThreadId() {
+        int undeletedThreadId = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.MESSAGE_TYPE, Mms.THREAD_ID, Mms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_MMS_TYPE_ALL, TEST_MMS_MTYPE,
+                undeletedThreadId, TEST_READ_FLAG_ONE});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        // Giving the same handle for msg below and cursor above makes handleMsgListChangesMms()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_MMS_TYPE_ALL, TEST_THREAD_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        msg.localInitiatedSend = true;
+        mObserver.setMsgListMms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesMms();
+
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).threadId,
+                undeletedThreadId);
+        Assert.assertEquals(mObserver.getMsgListMms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMmsSendIntent_withMnsClientNotConnected() {
+        when(mClient.isConnected()).thenReturn(false);
+
+        Assert.assertFalse(mObserver.handleMmsSendIntent(mContext, mIntent));
+    }
+
+    @Test
+    public void handleMmsSendIntent_withInvalidHandle() {
+        when(mClient.isConnected()).thenReturn(true);
+        doReturn((long) -1).when(mIntent).getLongExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1);
+
+        Assert.assertTrue(mObserver.handleMmsSendIntent(mContext, mIntent));
+    }
+
+    @Test
+    public void handleMmsSendIntent_withActivityResultOk() {
+        when(mClient.isConnected()).thenReturn(true);
+        doReturn(TEST_HANDLE_ONE).when(mIntent).getLongExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1);
+        doReturn(Activity.RESULT_OK).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_RESULT, Activity.RESULT_CANCELED);
+        doReturn(0).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        mObserver.mObserverRegistered = true;
+
+        Assert.assertTrue(mObserver.handleMmsSendIntent(mContext, mIntent));
+    }
+
+    @Test
+    public void handleMmsSendIntent_withActivityResultFirstUser() {
+        when(mClient.isConnected()).thenReturn(true);
+        doReturn(TEST_HANDLE_ONE).when(mIntent).getLongExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1);
+        doReturn(Activity.RESULT_FIRST_USER).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_RESULT, Activity.RESULT_CANCELED);
+        mObserver.mObserverRegistered = true;
+        doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverDelete(any(), any(),
+                any(), any());
+
+        Assert.assertTrue(mObserver.handleMmsSendIntent(mContext, mIntent));
+    }
+
+    @Test
+    public void actionMmsSent_withInvalidHandle() {
+        Map<Long, BluetoothMapContentObserver.Msg> mmsMsgList = new HashMap<>();
+        BluetoothMapContentObserver.Msg msg = createSimpleMsg();
+        mmsMsgList.put(TEST_HANDLE_ONE, msg);
+        doReturn(1).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn((long) -1).when(mIntent).getLongExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1);
+
+        mObserver.actionMmsSent(mContext, mIntent, 1, mmsMsgList);
+
+        Assert.assertTrue(mmsMsgList.containsKey(TEST_HANDLE_ONE));
+    }
+
+    @Test
+    public void actionMmsSent_withTransparency() {
+        Map<Long, BluetoothMapContentObserver.Msg> mmsMsgList = new HashMap<>();
+        BluetoothMapContentObserver.Msg msg = createSimpleMsg();
+        mmsMsgList.put(TEST_HANDLE_ONE, msg);
+        // This mock turns on the transparent flag
+        doReturn(1).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn(TEST_HANDLE_ONE).when(mIntent).getLongExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1);
+        doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverDelete(any(), any(),
+                any(), any());
+
+        mObserver.actionMmsSent(mContext, mIntent, 1, mmsMsgList);
+
+        Assert.assertFalse(mmsMsgList.containsKey(TEST_HANDLE_ONE));
+    }
+
+    @Test
+    public void actionMmsSent_withActivityResultOk() {
+        Map<Long, BluetoothMapContentObserver.Msg> mmsMsgList = new HashMap<>();
+        BluetoothMapContentObserver.Msg msg = createSimpleMsg();
+        mmsMsgList.put(TEST_HANDLE_ONE, msg);
+        // This mock turns off the transparent flag
+        doReturn(0).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn(TEST_HANDLE_ONE).when(mIntent).getLongExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1);
+
+        MatrixCursor cursor = new MatrixCursor(new String[] {});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+        doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
+                any(), any(), any());
+
+        mObserver.actionMmsSent(mContext, mIntent, Activity.RESULT_OK, mmsMsgList);
+
+        Assert.assertTrue(mmsMsgList.containsKey(TEST_HANDLE_ONE));
+    }
+
+    @Test
+    public void actionMmsSent_withActivityResultFirstUser() {
+        Map<Long, BluetoothMapContentObserver.Msg> mmsMsgList = new HashMap<>();
+        BluetoothMapContentObserver.Msg msg = createSimpleMsg();
+        mmsMsgList.put(TEST_HANDLE_ONE, msg);
+        // This mock turns off the transparent flag
+        doReturn(0).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn(TEST_HANDLE_ONE).when(mIntent).getLongExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_HANDLE, -1);
+
+        mObserver.actionMmsSent(mContext, mIntent, Activity.RESULT_FIRST_USER, mmsMsgList);
+
+        Assert.assertEquals(msg.type, Mms.MESSAGE_BOX_OUTBOX);
+    }
+
+    @Test
+    public void actionSmsSentDisconnected_withNullUriString() {
+        // This sets to null uriString
+        doReturn(null).when(mIntent).getStringExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_URI);
+        doReturn(1).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+
+        clearInvocations(mContext);
+        mObserver.actionSmsSentDisconnected(mContext, mIntent, Activity.RESULT_FIRST_USER);
+
+        verify(mContext, never()).getContentResolver();
+    }
+
+    @Test
+    public void actionSmsSentDisconnected_withActivityResultOk_andTransparentOff() {
+        doReturn(TEST_URI_STR).when(mIntent).getStringExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_URI);
+        // This mock turns off the transparent flag
+        doReturn(0).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
+                any(), any(), any());
+
+        clearInvocations(mContext);
+        mObserver.actionSmsSentDisconnected(mContext, mIntent, Activity.RESULT_OK);
+
+        verify(mContext).getContentResolver();
+    }
+
+    @Test
+    public void actionSmsSentDisconnected_withActivityResultOk_andTransparentOn() {
+        doReturn(TEST_URI_STR).when(mIntent).getStringExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_URI);
+        // This mock turns on the transparent flag
+        doReturn(1).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverDelete(any(), any(),
+                any(), any());
+
+        clearInvocations(mContext);
+        mObserver.actionSmsSentDisconnected(mContext, mIntent, Activity.RESULT_OK);
+
+        verify(mContext).getContentResolver();
+    }
+
+    @Test
+    public void actionSmsSentDisconnected_withActivityResultFirstUser_andTransparentOff() {
+        doReturn(TEST_URI_STR).when(mIntent).getStringExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_URI);
+        // This mock turns off the transparent flag
+        doReturn(0).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn(TEST_PLACEHOLDER_INT).when(mMapMethodProxy).contentResolverUpdate(any(), any(),
+                any(), any(), any());
+
+        clearInvocations(mContext);
+        mObserver.actionSmsSentDisconnected(mContext, mIntent, Activity.RESULT_OK);
+
+        verify(mContext).getContentResolver();
+    }
+
+    @Test
+    public void actionSmsSentDisconnected_withActivityResultFirstUser_andTransparentOn() {
+        doReturn(TEST_URI_STR).when(mIntent).getStringExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_URI);
+        // This mock turns on the transparent flag
+        doReturn(1).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_TRANSPARENT, 0);
+        doReturn(null).when(mContext).getContentResolver();
+
+        clearInvocations(mContext);
+        mObserver.actionSmsSentDisconnected(mContext, mIntent, Activity.RESULT_OK);
+
+        verify(mContext).getContentResolver();
+    }
+
+    @Test
+    public void handleContactListChanges_withNullContactForUci() throws Exception {
+        Uri uri = mock(Uri.class);
+        mObserver.mAuthority = TEST_AUTHORITY;
+        when(uri.getAuthority()).thenReturn(TEST_AUTHORITY);
 
         MatrixCursor cursor = new MatrixCursor(
                 new String[]{BluetoothMapContract.ConvoContactColumns.CONVO_ID,
@@ -807,32 +1513,92 @@
                         BluetoothMapContract.ConvoContactColumns.STATUS_TEXT,
                         BluetoothMapContract.ConvoContactColumns.PRIORITY,
                         BluetoothMapContract.ConvoContactColumns.LAST_ONLINE});
-        cursor.addRow(new Object[] {convoId, name, displayName, btUid, chatState, uci, lastActivity,
-        presenceState, statusText, priority, lastOnline});
-        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
-                any(), any());
-
-        mObserver.mContactUri = mock(Uri.class);
-        mObserver.mProviderClient = mProviderClient;
-        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+        cursor.addRow(new Object[] {TEST_CONVO_ID, TEST_NAME, TEST_DISPLAY_NAME, TEST_BT_UID,
+                TEST_CHAT_STATE, TEST_UCI, TEST_LAST_ACTIVITY, TEST_PRESENCE_STATE,
+                TEST_STATUS_TEXT, TEST_PRIORITY, TEST_LAST_ONLINE});
+        doReturn(cursor).when(mProviderClient).query(any(), any(), any(), any(), any());
 
         Map<String, BluetoothMapConvoContactElement> map = new HashMap<>();
+        map.put(TEST_UCI_DIFFERENT, null);
         mObserver.setContactList(map, true);
-        mObserver.initContactsList();
-        BluetoothMapConvoContactElement contactElement = mObserver.getContactList().get(uci);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
 
+        mObserver.handleContactListChanges(uri);
+
+        BluetoothMapConvoContactElement contactElement = mObserver.getContactList().get(TEST_UCI);
         final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
-        Assert.assertEquals(contactElement.getContactId(), uci);
-        Assert.assertEquals(contactElement.getName(), name);
-        Assert.assertEquals(contactElement.getDisplayName(), displayName);
-        Assert.assertEquals(contactElement.getBtUid(), btUid);
-        Assert.assertEquals(contactElement.getChatState(), chatState);
-        Assert.assertEquals(contactElement.getPresenceStatus(), statusText);
-        Assert.assertEquals(contactElement.getPresenceAvailability(), presenceState);
-        Assert.assertEquals(contactElement.getLastActivityString(), format.format(lastActivity));
-        Assert.assertEquals(contactElement.getPriority(), priority);
+        Assert.assertEquals(contactElement.getContactId(), TEST_UCI);
+        Assert.assertEquals(contactElement.getName(), TEST_NAME);
+        Assert.assertEquals(contactElement.getDisplayName(), TEST_DISPLAY_NAME);
+        Assert.assertEquals(contactElement.getBtUid(), TEST_BT_UID);
+        Assert.assertEquals(contactElement.getChatState(), TEST_CHAT_STATE);
+        Assert.assertEquals(contactElement.getPresenceStatus(), TEST_STATUS_TEXT);
+        Assert.assertEquals(contactElement.getPresenceAvailability(), TEST_PRESENCE_STATE);
+        Assert.assertEquals(contactElement.getLastActivityString(), format.format(
+                TEST_LAST_ACTIVITY));
+        Assert.assertEquals(contactElement.getPriority(), TEST_PRIORITY);
     }
 
+    @Test
+    public void handleContactListChanges_withNonNullContactForUci() throws Exception {
+        Uri uri = mock(Uri.class);
+        mObserver.mAuthority = TEST_AUTHORITY;
+        when(uri.getAuthority()).thenReturn(TEST_AUTHORITY);
+
+        MatrixCursor cursor = new MatrixCursor(
+                new String[]{BluetoothMapContract.ConvoContactColumns.CONVO_ID,
+                        BluetoothMapContract.ConvoContactColumns.NAME,
+                        BluetoothMapContract.ConvoContactColumns.NICKNAME,
+                        BluetoothMapContract.ConvoContactColumns.X_BT_UID,
+                        BluetoothMapContract.ConvoContactColumns.CHAT_STATE,
+                        BluetoothMapContract.ConvoContactColumns.UCI,
+                        BluetoothMapContract.ConvoContactColumns.LAST_ACTIVE,
+                        BluetoothMapContract.ConvoContactColumns.PRESENCE_STATE,
+                        BluetoothMapContract.ConvoContactColumns.STATUS_TEXT,
+                        BluetoothMapContract.ConvoContactColumns.PRIORITY,
+                        BluetoothMapContract.ConvoContactColumns.LAST_ONLINE});
+        cursor.addRow(new Object[] {TEST_CONVO_ID, TEST_NAME, TEST_DISPLAY_NAME, TEST_BT_UID,
+                TEST_CHAT_STATE, TEST_UCI, TEST_LAST_ACTIVITY, TEST_PRESENCE_STATE,
+                TEST_STATUS_TEXT, TEST_PRIORITY, TEST_LAST_ONLINE});
+        doReturn(cursor).when(mProviderClient).query(any(), any(), any(), any(), any());
+
+        Map<String, BluetoothMapConvoContactElement> map = new HashMap<>();
+        map.put(TEST_UCI_DIFFERENT, null);
+        BluetoothMapConvoContactElement contact = new BluetoothMapConvoContactElement(TEST_UCI,
+                TEST_NAME, TEST_DISPLAY_NAME, TEST_STATUS_TEXT_DIFFERENT,
+                TEST_PRESENCE_STATE_DIFFERENT, TEST_LAST_ACTIVITY, TEST_CHAT_STATE_DIFFERENT,
+                TEST_PRIORITY, TEST_BT_UID);
+        map.put(TEST_UCI, contact);
+        mObserver.setContactList(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleContactListChanges(uri);
+
+        BluetoothMapConvoContactElement contactElement = mObserver.getContactList().get(TEST_UCI);
+        final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
+        Assert.assertEquals(contactElement.getContactId(), TEST_UCI);
+        Assert.assertEquals(contactElement.getName(), TEST_NAME);
+        Assert.assertEquals(contactElement.getDisplayName(), TEST_DISPLAY_NAME);
+        Assert.assertEquals(contactElement.getBtUid(), TEST_BT_UID);
+        Assert.assertEquals(contactElement.getChatState(), TEST_CHAT_STATE);
+        Assert.assertEquals(contactElement.getPresenceStatus(), TEST_STATUS_TEXT);
+        Assert.assertEquals(contactElement.getPresenceAvailability(), TEST_PRESENCE_STATE);
+        Assert.assertEquals(contactElement.getLastActivityString(), format.format(
+                TEST_LAST_ACTIVITY));
+        Assert.assertEquals(contactElement.getPriority(), TEST_PRIORITY);
+    }
+
+    @Test
+    public void handleContactListChanges_withMapEventReportVersion11() throws Exception {
+        Uri uri = mock(Uri.class);
+        mObserver.mAuthority = TEST_AUTHORITY;
+        when(uri.getAuthority()).thenReturn(TEST_AUTHORITY);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+
+        mObserver.handleContactListChanges(uri);
+
+        verify(mProviderClient, never()).query(any(), any(), any(), any(), any(), any());
+    }
 
     private BluetoothMapContentObserver.Msg createSimpleMsg() {
         return new BluetoothMapContentObserver.Msg(1, 1L, 1);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java
index 42cb57f..6824582 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapContentTest.java
@@ -18,18 +18,25 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.Cursor;
 import android.database.MatrixCursor;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
 import android.provider.ContactsContract;
 import android.provider.Telephony;
 import android.provider.Telephony.Threads;
+import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
 import android.text.util.Rfc822Tokenizer;
 
@@ -38,6 +45,8 @@
 import com.android.bluetooth.BluetoothMethodProxy;
 import com.android.bluetooth.SignedLongLong;
 import com.android.bluetooth.map.BluetoothMapContent.FilterInfo;
+import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
+import com.android.bluetooth.mapapi.BluetoothMapContract;
 
 import org.junit.After;
 import org.junit.Before;
@@ -47,12 +56,37 @@
 import org.mockito.MockitoAnnotations;
 import org.mockito.Spy;
 
+import java.io.ByteArrayInputStream;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.HashMap;
+
 @RunWith(AndroidJUnit4.class)
 public class BluetoothMapContentTest {
     private static final String TEST_TEXT = "text";
     private static final String TEST_TO_ADDRESS = "toName (toAddress) <to@google.com>";
     private static final String TEST_CC_ADDRESS = "ccName (ccAddress) <cc@google.com>";
     private static final String TEST_BCC_ADDRESS = "bccName (bccAddress) <bcc@google.com>";
+    private static final String TEST_FROM_ADDRESS = "fromName (fromAddress) <from@google.com>";
+    private static final String TEST_ADDRESS = "111-1111-1111";
+    private static final long TEST_DATE_SMS = 1;
+    private static final long TEST_DATE_EMAIL = 2;
+    private static final String TEST_NAME = "test_name";
+    private static final String TEST_FORMATTED_NAME = "test_formatted_name";
+    private static final String TEST_PHONE = "test_phone";
+    private static final String TEST_PHONE_NAME = "test_phone_name";
+    private static final long TEST_ID = 1;
+    private static final long TEST_INBOX_FOLDER_ID = BluetoothMapContract.FOLDER_ID_INBOX;
+    private static final long TEST_SENT_FOLDER_ID = BluetoothMapContract.FOLDER_ID_SENT;
+    private static final String TEST_SUBJECT = "subject";
+    private static final long TEST_DATE = 1;
+    private static final String TEST_MESSAGE_ID = "test_message_id";
+    private static final String TEST_FIRST_BT_UID = "1111";
+    private static final String TEST_FIRST_BT_UCI_RECIPIENT = "test_first_bt_uci_recipient";
+    private static final String TEST_FIRST_BT_UCI_ORIGINATOR = "test_first_bt_uci_originator";
+    private static final int TEST_NO_FILTER = 0;
+    private static final String TEST_CONTACT_NAME_FILTER = "test_contact_name_filter";
 
     @Mock
     private BluetoothMapAccountItem mAccountItem;
@@ -71,7 +105,9 @@
 
     private BluetoothMapContent mContent;
     private FilterInfo mInfo;
-    private BluetoothMapMessageListingElement mElement;
+    private BluetoothMapMessageListingElement mMessageListingElement;
+    private BluetoothMapConvoListingElement mConvoListingElement;
+    private BluetoothMapFolderElement mCurrentFolder;
 
     @Before
     public void setUp() {
@@ -80,7 +116,9 @@
 
         mContent = new BluetoothMapContent(mContext, mAccountItem, mMasInstance);
         mInfo = new FilterInfo();
-        mElement = new BluetoothMapMessageListingElement();
+        mMessageListingElement = new BluetoothMapMessageListingElement();
+        mConvoListingElement = new BluetoothMapConvoListingElement();
+        mCurrentFolder = new BluetoothMapFolderElement("current", null);
     }
 
     @After
@@ -176,9 +214,9 @@
         cursor.addRow(new Object[]{0, -1});
         cursor.moveToFirst();
 
-        mContent.setAttachment(mElement, cursor, mInfo, mParams);
+        mContent.setAttachment(mMessageListingElement, cursor, mInfo, mParams);
 
-        assertThat(mElement.getAttachmentSize()).isEqualTo(1);
+        assertThat(mMessageListingElement.getAttachmentSize()).isEqualTo(1);
     }
 
     @Test
@@ -193,9 +231,9 @@
         cursor.addRow(new Object[]{1, 0});
         cursor.moveToFirst();
 
-        mContent.setAttachment(mElement, cursor, mInfo, mParams);
+        mContent.setAttachment(mMessageListingElement, cursor, mInfo, mParams);
 
-        assertThat(mElement.getAttachmentSize()).isEqualTo(1);
+        assertThat(mMessageListingElement.getAttachmentSize()).isEqualTo(1);
     }
 
     @Test
@@ -214,10 +252,10 @@
         cursor.moveToFirst();
 
         mContent.setRemoteFeatureMask(featureMask);
-        mContent.setAttachment(mElement, cursor, mInfo, mParams);
+        mContent.setAttachment(mMessageListingElement, cursor, mInfo, mParams);
 
-        assertThat(mElement.getAttachmentSize()).isEqualTo(1);
-        assertThat(mElement.getAttachmentMimeTypes()).isEqualTo("test_mime_type");
+        assertThat(mMessageListingElement.getAttachmentSize()).isEqualTo(1);
+        assertThat(mMessageListingElement.getAttachmentMimeTypes()).isEqualTo("test_mime_type");
     }
 
     @Test
@@ -271,9 +309,9 @@
         cursor.addRow(new Object[]{2L});
         cursor.moveToFirst();
 
-        mContent.setDateTime(mElement, cursor, mInfo, mParams);
+        mContent.setDateTime(mMessageListingElement, cursor, mInfo, mParams);
 
-        assertThat(mElement.getDateTime()).isEqualTo(2L);
+        assertThat(mMessageListingElement.getDateTime()).isEqualTo(2L);
     }
 
     @Test
@@ -285,9 +323,9 @@
         cursor.addRow(new Object[]{2L});
         cursor.moveToFirst();
 
-        mContent.setDateTime(mElement, cursor, mInfo, mParams);
+        mContent.setDateTime(mMessageListingElement, cursor, mInfo, mParams);
 
-        assertThat(mElement.getDateTime()).isEqualTo(2L * 1000L);
+        assertThat(mMessageListingElement.getDateTime()).isEqualTo(2L * 1000L);
     }
 
     @Test
@@ -299,9 +337,9 @@
         cursor.addRow(new Object[]{2L});
         cursor.moveToFirst();
 
-        mContent.setDateTime(mElement, cursor, mInfo, mParams);
+        mContent.setDateTime(mMessageListingElement, cursor, mInfo, mParams);
 
-        assertThat(mElement.getDateTime()).isEqualTo(2L);
+        assertThat(mMessageListingElement.getDateTime()).isEqualTo(2L);
     }
 
     @Test
@@ -314,9 +352,9 @@
         cursor.addRow(new Object[]{"test_delivery_status"});
         cursor.moveToFirst();
 
-        mContent.setDeliveryStatus(mElement, cursor, mInfo, mParams);
+        mContent.setDeliveryStatus(mMessageListingElement, cursor, mInfo, mParams);
 
-        assertThat(mElement.getDeliveryStatus()).isEqualTo("test_delivery_status");
+        assertThat(mMessageListingElement.getDeliveryStatus()).isEqualTo("test_delivery_status");
     }
 
     @Test
@@ -341,8 +379,7 @@
 
     @Test
     public void smsSelected_withNoFilter() {
-        int noFilter = 0;
-        when(mParams.getFilterMessageType()).thenReturn(noFilter);
+        when(mParams.getFilterMessageType()).thenReturn(TEST_NO_FILTER);
 
         assertThat(mContent.smsSelected(mInfo, mParams)).isTrue();
     }
@@ -388,8 +425,7 @@
 
     @Test
     public void mmsSelected_withNoFilter() {
-        int noFilter = 0;
-        when(mParams.getFilterMessageType()).thenReturn(noFilter);
+        when(mParams.getFilterMessageType()).thenReturn(TEST_NO_FILTER);
 
         assertThat(mContent.mmsSelected(mParams)).isTrue();
     }
@@ -442,4 +478,693 @@
         assertThat(mContent.getRecipientAddressingEmail(cursor, mInfo)).isEqualTo(
                 expected.toString());
     }
+
+    @Test
+    public void setRecipientAddressing_withFilterMsgTypeSms_andSmsMsgTypeInbox() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_RECIPIENT_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_SMS;
+        mInfo.mPhoneNum = TEST_ADDRESS;
+        mInfo.mSmsColType = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"SmsColType"});
+        cursor.addRow(new Object[] {Telephony.Sms.MESSAGE_TYPE_INBOX});
+        cursor.moveToFirst();
+
+        mContent.setRecipientAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getRecipientAddressing()).isEqualTo(TEST_ADDRESS);
+    }
+
+    @Test
+    public void setRecipientAddressing_withFilterMsgTypeSms_andSmsMsgTypeDraft() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_RECIPIENT_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_SMS;
+        mInfo.mSmsColType = 2;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"RecipientIds",
+                Telephony.CanonicalAddressesColumns.ADDRESS, "SmsColType",
+                Telephony.Sms.ADDRESS, Telephony.Sms.THREAD_ID});
+        cursor.addRow(new Object[] {"recipientIdOne recipientIdTwo", "recipientAddress",
+                Telephony.Sms.MESSAGE_TYPE_DRAFT, null, "0"});
+        cursor.moveToFirst();
+
+        mContent.setRecipientAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getRecipientAddressing()).isEqualTo("recipientAddress");
+    }
+
+    @Test
+    public void setRecipientAddressing_withFilterMsgTypeMms() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_RECIPIENT_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_MMS;
+        MatrixCursor cursor = new MatrixCursor(
+                new String[]{BaseColumns._ID, Telephony.Mms.Addr.ADDRESS});
+        cursor.addRow(new Object[] {Telephony.Sms.MESSAGE_TYPE_INBOX, null});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setRecipientAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getRecipientAddressing()).isEqualTo("");
+    }
+
+    @Test
+    public void setRecipientAddressing_withFilterMsgTypeEmail() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_RECIPIENT_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_EMAIL;
+        mInfo.mMessageColToAddress = 0;
+        mInfo.mMessageColCcAddress = 1;
+        mInfo.mMessageColBccAddress = 2;
+        MatrixCursor cursor = new MatrixCursor(
+                new String[]{"MessageColToAddress", "MessageColCcAddress", "MessageColBccAddress"});
+        cursor.addRow(new Object[]{TEST_TO_ADDRESS, TEST_CC_ADDRESS, TEST_BCC_ADDRESS});
+        cursor.moveToFirst();
+
+        mContent.setRecipientAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        StringBuilder expected = new StringBuilder();
+        expected.append(Rfc822Tokenizer.tokenize(TEST_TO_ADDRESS)[0].getAddress());
+        expected.append("; ");
+        expected.append(Rfc822Tokenizer.tokenize(TEST_CC_ADDRESS)[0].getAddress());
+        expected.append("; ");
+        expected.append(Rfc822Tokenizer.tokenize(TEST_BCC_ADDRESS)[0].getAddress());
+        assertThat(mMessageListingElement.getRecipientAddressing()).isEqualTo(expected.toString());
+    }
+
+    @Test
+    public void setSenderAddressing_withFilterMsgTypeSms_andSmsMsgTypeInbox() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_SENDER_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_SMS;
+        mInfo.mSmsColType = 0;
+        mInfo.mSmsColAddress = 1;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"SmsColType", "SmsColAddress"});
+        cursor.addRow(new Object[] {Telephony.Sms.MESSAGE_TYPE_INBOX, TEST_ADDRESS});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderAddressing()).isEqualTo(
+                PhoneNumberUtils.extractNetworkPortion(TEST_ADDRESS));
+    }
+
+    @Test
+    public void setSenderAddressing_withFilterMSgTypeSms_andSmsMsgTypeDraft() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_SENDER_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_SMS;
+        mInfo.mPhoneNum = null;
+        mInfo.mSmsColType = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"SmsColType"});
+        cursor.addRow(new Object[] {Telephony.Sms.MESSAGE_TYPE_DRAFT});
+        cursor.moveToFirst();
+
+        mContent.setSenderAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderAddressing()).isEqualTo("");
+    }
+
+    @Test
+    public void setSenderAddressing_withFilterMsgTypeMms() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_SENDER_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_MMS;
+        mInfo.mMmsColId = 0;
+        MatrixCursor cursor = new MatrixCursor(
+                new String[]{"MmsColId", Telephony.Mms.Addr.ADDRESS});
+        cursor.addRow(new Object[] {0, ""});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderAddressing()).isEqualTo("");
+    }
+
+    @Test
+    public void setSenderAddressing_withFilterTypeEmail() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_SENDER_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_EMAIL;
+        mInfo.mMessageColFromAddress = 0;
+        MatrixCursor cursor = new MatrixCursor(
+                new String[]{"MessageColFromAddress"});
+        cursor.addRow(new Object[]{TEST_FROM_ADDRESS});
+        cursor.moveToFirst();
+
+        mContent.setSenderAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        StringBuilder expected = new StringBuilder();
+        expected.append(Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getAddress());
+        assertThat(mMessageListingElement.getSenderAddressing()).isEqualTo(expected.toString());
+    }
+
+    @Test
+    public void setSenderAddressing_withFilterTypeIm() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapContent.MASK_SENDER_ADDRESSING);
+        mInfo.mMsgType = FilterInfo.TYPE_IM;
+        mInfo.mMessageColFromAddress = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"MessageColFromAddress",
+                BluetoothMapContract.ConvoContactColumns.UCI});
+        cursor.addRow(new Object[] {(long) 1, TEST_ADDRESS});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderAddressing(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderAddressing()).isEqualTo(TEST_ADDRESS);
+    }
+
+    @Test
+    public void setSenderName_withFilterTypeSms_andSmsMsgTypeInbox() {
+        when(mParams.getParameterMask()).thenReturn((long) BluetoothMapContent.MASK_SENDER_NAME);
+        mInfo.mMsgType = FilterInfo.TYPE_SMS;
+        mInfo.mSmsColAddress = 1;
+        MatrixCursor cursor = new MatrixCursor(new String[] {Telephony.Sms.TYPE, "SmsColAddress",
+                ContactsContract.Contacts.DISPLAY_NAME});
+        cursor.addRow(new Object[] {Telephony.Sms.MESSAGE_TYPE_INBOX, TEST_PHONE, TEST_PHONE_NAME});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderName(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderName()).isEqualTo(TEST_PHONE_NAME);
+    }
+
+    @Test
+    public void setSenderName_withFilterTypeSms_andSmsMsgTypeDraft() {
+        when(mParams.getParameterMask()).thenReturn((long) BluetoothMapContent.MASK_SENDER_NAME);
+        mInfo.mMsgType = FilterInfo.TYPE_SMS;
+        mInfo.mPhoneAlphaTag = TEST_NAME;
+        MatrixCursor cursor = new MatrixCursor(new String[] {Telephony.Sms.TYPE});
+        cursor.addRow(new Object[] {Telephony.Sms.MESSAGE_TYPE_DRAFT});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderName(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderName()).isEqualTo(TEST_NAME);
+    }
+
+    @Test
+    public void setSenderName_withFilterTypeMms_withNonNullSenderAddressing() {
+        when(mParams.getParameterMask()).thenReturn((long) BluetoothMapContent.MASK_SENDER_NAME);
+        mInfo.mMsgType = FilterInfo.TYPE_MMS;
+        mInfo.mMmsColId = 0;
+        mMessageListingElement.setSenderAddressing(TEST_ADDRESS);
+        MatrixCursor cursor = new MatrixCursor(new String[] {"MmsColId", Telephony.Mms.Addr.ADDRESS,
+                ContactsContract.Contacts.DISPLAY_NAME});
+        cursor.addRow(new Object[] {0, TEST_PHONE, TEST_PHONE_NAME});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderName(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderName()).isEqualTo(TEST_PHONE_NAME);
+    }
+
+    @Test
+    public void setSenderName_withFilterTypeMms_withNullSenderAddressing() {
+        when(mParams.getParameterMask()).thenReturn((long) BluetoothMapContent.MASK_SENDER_NAME);
+        mInfo.mMsgType = FilterInfo.TYPE_MMS;
+        mInfo.mMmsColId = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"MmsColId"});
+        cursor.addRow(new Object[] {0});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderName(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderName()).isEqualTo("");
+    }
+
+    @Test
+    public void setSenderName_withFilterTypeEmail() {
+        when(mParams.getParameterMask()).thenReturn((long) BluetoothMapContent.MASK_SENDER_NAME);
+        mInfo.mMsgType = FilterInfo.TYPE_EMAIL;
+        mInfo.mMessageColFromAddress = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"MessageColFromAddress"});
+        cursor.addRow(new Object[] {TEST_FROM_ADDRESS});
+        cursor.moveToFirst();
+
+        mContent.setSenderName(mMessageListingElement, cursor, mInfo, mParams);
+
+        StringBuilder expected = new StringBuilder();
+        expected.append(Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getName());
+        assertThat(mMessageListingElement.getSenderName()).isEqualTo(expected.toString());
+    }
+
+    @Test
+    public void setSenderName_withFilterTypeIm() {
+        when(mParams.getParameterMask()).thenReturn((long) BluetoothMapContent.MASK_SENDER_NAME);
+        mInfo.mMsgType = FilterInfo.TYPE_IM;
+        mInfo.mMessageColFromAddress = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"MessageColFromAddress",
+                BluetoothMapContract.ConvoContactColumns.NAME});
+        cursor.addRow(new Object[] {(long) 1, TEST_NAME});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mContent.setSenderName(mMessageListingElement, cursor, mInfo, mParams);
+
+        assertThat(mMessageListingElement.getSenderName()).isEqualTo(TEST_NAME);
+    }
+
+    @Test
+    public void setters_withConvoList() {
+        BluetoothMapMasInstance instance = spy(BluetoothMapMasInstance.class);
+        BluetoothMapContent content = new BluetoothMapContent(mContext, mAccountItem, instance);
+        HashMap<Long, BluetoothMapConvoListingElement> emailMap =
+                new HashMap<Long, BluetoothMapConvoListingElement>();
+        HashMap<Long, BluetoothMapConvoListingElement> smsMap =
+                new HashMap<Long, BluetoothMapConvoListingElement>();
+
+        content.setImEmailConvoList(emailMap);
+        content.setSmsMmsConvoList(smsMap);
+
+        assertThat(content.getImEmailConvoList()).isEqualTo(emailMap);
+        assertThat(content.getSmsMmsConvoList()).isEqualTo(smsMap);
+    }
+
+    @Test
+    public void setLastActivity_withFilterTypeSms() {
+        mInfo.mMsgType = FilterInfo.TYPE_SMS;
+        mInfo.mConvoColLastActivity = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"ConvoColLastActivity",
+                "MmsSmsThreadColDate"});
+        cursor.addRow(new Object[] {TEST_DATE_EMAIL, TEST_DATE_SMS});
+        cursor.moveToFirst();
+
+        mContent.setLastActivity(mConvoListingElement, cursor, mInfo);
+
+        assertThat(mConvoListingElement.getLastActivity()).isEqualTo(TEST_DATE_SMS);
+    }
+
+    @Test
+    public void setLastActivity_withFilterTypeEmail() {
+        mInfo.mMsgType = FilterInfo.TYPE_EMAIL;
+        mInfo.mConvoColLastActivity = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {"ConvoColLastActivity",
+                "MmsSmsThreadColDate"});
+        cursor.addRow(new Object[] {TEST_DATE_EMAIL, TEST_DATE_SMS});
+        cursor.moveToFirst();
+
+        mContent.setLastActivity(mConvoListingElement, cursor, mInfo);
+
+        assertThat(mConvoListingElement.getLastActivity()).isEqualTo(TEST_DATE_EMAIL);
+    }
+
+    @Test
+    public void getEmailMessage_withCharsetNative() {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_NATIVE);
+
+        assertThrows(IllegalArgumentException.class, () -> mContent.getEmailMessage(TEST_ID,
+                mParams, mCurrentFolder));
+    }
+
+    @Test
+    public void getEmailMessage_withEmptyCursor() {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_UTF8);
+        MatrixCursor cursor = new MatrixCursor(new String[] {});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        assertThrows(IllegalArgumentException.class, () -> mContent.getEmailMessage(TEST_ID,
+                mParams, mCurrentFolder));
+    }
+
+    @Test
+    public void getEmailMessage_withFileNotFoundExceptionForEmailBodyAccess() throws Exception {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_UTF8);
+        when(mParams.getFractionRequest()).thenReturn(BluetoothMapAppParams.FRACTION_REQUEST_FIRST);
+        when(mParams.getAttachment()).thenReturn(0);
+
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns.RECEPTION_STATE,
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.TO_LIST,
+                BluetoothMapContract.MessageColumns.FROM_LIST
+        });
+        cursor.addRow(new Object[] {BluetoothMapContract.RECEPTION_STATE_FRACTIONED, "1",
+        TEST_INBOX_FOLDER_ID, TEST_TO_ADDRESS, TEST_FROM_ADDRESS});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mCurrentFolder.setFolderId(TEST_INBOX_FOLDER_ID);
+        // This mock sets up FileNotFoundException during email body access
+        doThrow(FileNotFoundException.class).when(
+                mMapMethodProxy).contentResolverOpenFileDescriptor(any(), any(), any());
+
+        byte[] encodedMessageEmail = mContent.getEmailMessage(TEST_ID, mParams, mCurrentFolder);
+        InputStream inputStream = new ByteArrayInputStream(encodedMessageEmail);
+        BluetoothMapbMessage messageParsed = BluetoothMapbMessage.parse(inputStream,
+                BluetoothMapAppParams.CHARSET_UTF8);
+
+        assertThat(messageParsed.getType()).isEqualTo(TYPE.EMAIL);
+        assertThat(messageParsed.getVersionString()).isEqualTo("VERSION:" +
+                mContent.mMessageVersion);
+        assertThat(messageParsed.getFolder()).isEqualTo(mCurrentFolder.getFullPath());
+        assertThat(messageParsed.getRecipients().get(0).getName()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_TO_ADDRESS)[0].getName());
+        assertThat(messageParsed.getRecipients().get(0).getFirstEmail()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_TO_ADDRESS)[0].getAddress());
+        assertThat(messageParsed.getOriginators().get(0).getName()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getName());
+        assertThat(messageParsed.getOriginators().get(0).getFirstEmail()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getAddress());
+    }
+
+    @Test
+    public void getEmailMessage_withNullPointerExceptionForEmailBodyAccess() throws Exception {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_UTF8);
+        when(mParams.getFractionRequest()).thenReturn(BluetoothMapAppParams.FRACTION_REQUEST_FIRST);
+        when(mParams.getAttachment()).thenReturn(0);
+
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns.RECEPTION_STATE,
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.TO_LIST,
+                BluetoothMapContract.MessageColumns.FROM_LIST
+        });
+        cursor.addRow(new Object[] {BluetoothMapContract.RECEPTION_STATE_FRACTIONED, null,
+                TEST_INBOX_FOLDER_ID, TEST_TO_ADDRESS, TEST_FROM_ADDRESS});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mCurrentFolder.setFolderId(TEST_INBOX_FOLDER_ID);
+        // This mock sets up NullPointerException during email body access
+        doThrow(NullPointerException.class).when(
+                mMapMethodProxy).contentResolverOpenFileDescriptor(any(), any(), any());
+
+        byte[] encodedMessageEmail = mContent.getEmailMessage(TEST_ID, mParams, mCurrentFolder);
+        InputStream inputStream = new ByteArrayInputStream(encodedMessageEmail);
+        BluetoothMapbMessage messageParsed = BluetoothMapbMessage.parse(inputStream,
+                BluetoothMapAppParams.CHARSET_UTF8);
+
+        assertThat(messageParsed.getType()).isEqualTo(TYPE.EMAIL);
+        assertThat(messageParsed.getVersionString()).isEqualTo("VERSION:" +
+                mContent.mMessageVersion);
+        assertThat(messageParsed.getFolder()).isEqualTo(mCurrentFolder.getFullPath());
+        assertThat(messageParsed.getRecipients().get(0).getName()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_TO_ADDRESS)[0].getName());
+        assertThat(messageParsed.getRecipients().get(0).getFirstEmail()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_TO_ADDRESS)[0].getAddress());
+        assertThat(messageParsed.getOriginators().get(0).getName()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getName());
+        assertThat(messageParsed.getOriginators().get(0).getFirstEmail()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getAddress());
+    }
+
+    @Test
+    public void getEmailMessage() throws Exception {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_UTF8);
+        when(mParams.getFractionRequest()).thenReturn(BluetoothMapAppParams.FRACTION_REQUEST_FIRST);
+        when(mParams.getAttachment()).thenReturn(0);
+
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns.RECEPTION_STATE,
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.TO_LIST,
+                BluetoothMapContract.MessageColumns.FROM_LIST
+        });
+        cursor.addRow(new Object[] {BluetoothMapContract.RECEPTION_STATE_FRACTIONED, "1",
+                TEST_INBOX_FOLDER_ID, TEST_TO_ADDRESS, TEST_FROM_ADDRESS});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mCurrentFolder.setFolderId(TEST_INBOX_FOLDER_ID);
+        FileDescriptor fd = new FileDescriptor();
+        ParcelFileDescriptor pfd = mock(ParcelFileDescriptor.class);
+        doReturn(fd).when(pfd).getFileDescriptor();
+        doReturn(pfd).when(mMapMethodProxy).contentResolverOpenFileDescriptor(any(), any(), any());
+
+        byte[] encodedMessageEmail = mContent.getEmailMessage(TEST_ID, mParams, mCurrentFolder);
+        InputStream inputStream = new ByteArrayInputStream(encodedMessageEmail);
+        BluetoothMapbMessage messageParsed = BluetoothMapbMessage.parse(inputStream,
+                BluetoothMapAppParams.CHARSET_UTF8);
+
+        assertThat(messageParsed.getType()).isEqualTo(TYPE.EMAIL);
+        assertThat(messageParsed.getVersionString()).isEqualTo("VERSION:" +
+                mContent.mMessageVersion);
+        assertThat(messageParsed.getFolder()).isEqualTo(mCurrentFolder.getFullPath());
+        assertThat(messageParsed.getRecipients().get(0).getName()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_TO_ADDRESS)[0].getName());
+        assertThat(messageParsed.getRecipients().get(0).getFirstEmail()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_TO_ADDRESS)[0].getAddress());
+        assertThat(messageParsed.getOriginators().get(0).getName()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getName());
+        assertThat(messageParsed.getOriginators().get(0).getFirstEmail()).isEqualTo(
+                Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getAddress());
+    }
+
+    @Test
+    public void getIMMessage_withCharsetNative() {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_NATIVE);
+
+        assertThrows(IllegalArgumentException.class, () -> mContent.getIMMessage(TEST_ID,
+                mParams, mCurrentFolder));
+    }
+
+    @Test
+    public void getIMMessage_withEmptyCursor() {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_UTF8);
+        MatrixCursor cursor = new MatrixCursor(new String[] {});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        assertThrows(IllegalArgumentException.class, () -> mContent.getIMMessage(TEST_ID,
+                mParams, mCurrentFolder));
+    }
+
+    @Test
+    public void getIMMessage_withSentFolderId() throws Exception {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_UTF8);
+        when(mParams.getAttachment()).thenReturn(1);
+
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.THREAD_ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.SUBJECT,
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.DATE,
+                BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE,
+                BluetoothMapContract.MessageColumns.BODY,
+                BluetoothMapContract.ConvoContactColumns.NAME,
+                BluetoothMapContract.ConvoContactColumns.X_BT_UID,
+                BluetoothMapContract.ConvoContactColumns.NICKNAME,
+                BluetoothMapContract.ConvoContactColumns.UCI,
+        });
+        cursor.addRow(new Object[] {1, 1, TEST_SENT_FOLDER_ID, TEST_SUBJECT, TEST_MESSAGE_ID,
+                TEST_DATE, 0, "body", TEST_NAME, TEST_FIRST_BT_UID, TEST_FORMATTED_NAME,
+                TEST_FIRST_BT_UCI_RECIPIENT});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mCurrentFolder.setFolderId(TEST_SENT_FOLDER_ID);
+        when(mAccountItem.getUciFull()).thenReturn(TEST_FIRST_BT_UCI_ORIGINATOR);
+
+        byte[] encodedMessageMime = mContent.getIMMessage(TEST_ID, mParams, mCurrentFolder);
+        InputStream inputStream = new ByteArrayInputStream(encodedMessageMime);
+        BluetoothMapbMessage messageMimeParsed = BluetoothMapbMessage.parse(inputStream, 1);
+
+        assertThat(messageMimeParsed.mAppParamCharset).isEqualTo(1);
+        assertThat(messageMimeParsed.getType()).isEqualTo(TYPE.IM);
+        assertThat(messageMimeParsed.getVersionString()).isEqualTo("VERSION:" +
+                mContent.mMessageVersion);
+        assertThat(messageMimeParsed.getFolder()).isEqualTo(mCurrentFolder.getFullPath());
+        assertThat(messageMimeParsed.getRecipients().size()).isEqualTo(1);
+        assertThat(messageMimeParsed.getOriginators().size()).isEqualTo(1);
+        assertThat(messageMimeParsed.getOriginators().get(0).getName()).isEmpty();
+        assertThat(messageMimeParsed.getRecipients().get(0).getName()).isEqualTo(
+                TEST_FORMATTED_NAME);
+    }
+
+    @Test
+    public void getIMMessage_withInboxFolderId() throws Exception {
+        when(mParams.getCharset()).thenReturn(BluetoothMapContent.MAP_MESSAGE_CHARSET_UTF8);
+        when(mParams.getAttachment()).thenReturn(1);
+
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.THREAD_ID,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.SUBJECT,
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.DATE,
+                BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE,
+                BluetoothMapContract.MessageColumns.BODY,
+                BluetoothMapContract.ConvoContactColumns.NAME,
+                BluetoothMapContract.ConvoContactColumns.X_BT_UID,
+                BluetoothMapContract.ConvoContactColumns.NICKNAME,
+                BluetoothMapContract.ConvoContactColumns.UCI,
+        });
+        cursor.addRow(new Object[] {0, 1, TEST_INBOX_FOLDER_ID, TEST_SUBJECT, TEST_MESSAGE_ID,
+                TEST_DATE, 0, "body", TEST_NAME, TEST_FIRST_BT_UID, TEST_FORMATTED_NAME,
+                TEST_FIRST_BT_UCI_ORIGINATOR});
+        cursor.moveToFirst();
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(), any(), any(),
+                any(), any());
+
+        mCurrentFolder.setFolderId(TEST_INBOX_FOLDER_ID);
+        when(mAccountItem.getUciFull()).thenReturn(TEST_FIRST_BT_UCI_RECIPIENT);
+
+        byte[] encodedMessageMime = mContent.getIMMessage(TEST_ID, mParams, mCurrentFolder);
+        InputStream inputStream = new ByteArrayInputStream(encodedMessageMime);
+        BluetoothMapbMessage messageMimeParsed = BluetoothMapbMessage.parse(inputStream, 1);
+
+        assertThat(messageMimeParsed.mAppParamCharset).isEqualTo(1);
+        assertThat(messageMimeParsed.getType()).isEqualTo(TYPE.IM);
+        assertThat(messageMimeParsed.getVersionString()).isEqualTo("VERSION:" +
+                mContent.mMessageVersion);
+        assertThat(messageMimeParsed.getFolder()).isEqualTo(mCurrentFolder.getFullPath());
+        assertThat(messageMimeParsed.getRecipients().size()).isEqualTo(1);
+        assertThat(messageMimeParsed.getOriginators().size()).isEqualTo(1);
+        assertThat(messageMimeParsed.getOriginators().get(0).getName()).isEqualTo(
+                TEST_FORMATTED_NAME);
+        assertThat(messageMimeParsed.getRecipients().get(0).getName()).isEmpty();
+    }
+
+    @Test
+    public void convoListing_withNullFilterRecipient() {
+        when(mParams.getConvoParameterMask()).thenReturn(
+                (long) BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        when(mParams.getFilterMessageType()).thenReturn(TEST_NO_FILTER);
+        when(mParams.getMaxListCount()).thenReturn(2);
+        when(mParams.getStartOffset()).thenReturn(0);
+        // This mock sets filter recipient to null
+        when(mParams.getFilterRecipient()).thenReturn(null);
+
+        MatrixCursor smsMmsCursor = new MatrixCursor(new String[] {"MmsSmsThreadColId",
+                "MmsSmsThreadColDate", "MmsSmsThreadColSnippet", "MmsSmsThreadSnippetCharset",
+                "MmsSmsThreadColRead", "MmsSmsThreadColRecipientIds"});
+        smsMmsCursor.addRow(new Object[] {TEST_ID, TEST_DATE_SMS, "test_col_snippet",
+                "test_col_snippet_cs", 1, "test_recipient_ids"});
+        smsMmsCursor.moveToFirst();
+        doReturn(smsMmsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.MMS_SMS_THREAD_PROJECTION), any(), any(), any());
+
+        MatrixCursor imEmailCursor = new MatrixCursor(
+                new String[] {BluetoothMapContract.ConversationColumns.THREAD_ID,
+                        BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY,
+                        BluetoothMapContract.ConversationColumns.THREAD_NAME,
+                        BluetoothMapContract.ConversationColumns.READ_STATUS,
+                        BluetoothMapContract.ConversationColumns.VERSION_COUNTER,
+                        BluetoothMapContract.ConversationColumns.SUMMARY,
+                        BluetoothMapContract.ConvoContactColumns.X_BT_UID,
+                        BluetoothMapContract.ConvoContactColumns.CHAT_STATE,
+                        BluetoothMapContract.ConvoContactColumns.UCI,
+                        BluetoothMapContract.ConvoContactColumns.NICKNAME,
+                        BluetoothMapContract.ConvoContactColumns.LAST_ACTIVE,
+                        BluetoothMapContract.ConvoContactColumns.NAME,
+                        BluetoothMapContract.ConvoContactColumns.PRESENCE_STATE,
+                        BluetoothMapContract.ConvoContactColumns.STATUS_TEXT,
+                        BluetoothMapContract.ConvoContactColumns.PRIORITY});
+        imEmailCursor.addRow(new Object[] {TEST_ID, TEST_DATE_EMAIL, TEST_NAME, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0});
+        doReturn(imEmailCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_CONVERSATION_PROJECTION), any(), any(), any());
+
+        BluetoothMapConvoListing listing = mContent.convoListing(mParams, false);
+
+        assertThat(listing.getCount()).isEqualTo(2);
+        BluetoothMapConvoListingElement emailElement = listing.getList().get(0);
+        assertThat(emailElement.getType()).isEqualTo(TYPE.EMAIL);
+        assertThat(emailElement.getLastActivity()).isEqualTo(TEST_DATE_EMAIL);
+        assertThat(emailElement.getName()).isEqualTo(TEST_NAME);
+        assertThat(emailElement.getReadBool()).isFalse();
+        BluetoothMapConvoListingElement smsElement = listing.getList().get(1);
+        assertThat(smsElement.getType()).isEqualTo(TYPE.SMS_GSM);
+        assertThat(smsElement.getLastActivity()).isEqualTo(TEST_DATE_SMS);
+        assertThat(smsElement.getName()).isEqualTo("");
+        assertThat(smsElement.getReadBool()).isTrue();
+    }
+
+    @Test
+    public void convoListing_withNonNullFilterRecipient() {
+        when(mParams.getConvoParameterMask()).thenReturn(
+                (long) BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        when(mParams.getFilterMessageType()).thenReturn(BluetoothMapAppParams.FILTER_NO_EMAIL);
+        when(mParams.getMaxListCount()).thenReturn(2);
+        when(mParams.getStartOffset()).thenReturn(0);
+        // This mock sets filter recipient to non null
+        when(mParams.getFilterRecipient()).thenReturn(TEST_CONTACT_NAME_FILTER);
+
+        MatrixCursor smsMmsCursor = new MatrixCursor(new String[] {"MmsSmsThreadColId",
+                "MmsSmsThreadColDate", "MmsSmsThreadColSnippet", "MmsSmsThreadSnippetCharset",
+                "MmsSmsThreadColRead", "MmsSmsThreadColRecipientIds"});
+        smsMmsCursor.addRow(new Object[] {TEST_ID, TEST_DATE_SMS, "test_col_snippet",
+                "test_col_snippet_cs", 1, String.valueOf(TEST_ID)});
+        smsMmsCursor.moveToFirst();
+        doReturn(smsMmsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.MMS_SMS_THREAD_PROJECTION), any(), any(), any());
+
+        MatrixCursor addressCursor = new MatrixCursor(new String[] {"COL_ADDR_ID",
+                "COL_ADDR_ADDR"});
+        addressCursor.addRow(new Object[]{TEST_ID, TEST_ADDRESS});
+        doReturn(addressCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(SmsMmsContacts.ADDRESS_PROJECTION), any(), any(), any());
+
+        MatrixCursor contactCursor = new MatrixCursor(new String[] {"COL_CONTACT_ID",
+                "COL_CONTACT_NAME"});
+        contactCursor.addRow(new Object[]{TEST_ID, TEST_NAME});
+        doReturn(contactCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(SmsMmsContacts.CONTACT_PROJECTION), any(), any(), any());
+
+        MatrixCursor imEmailCursor = new MatrixCursor(
+                new String[] {BluetoothMapContract.ConversationColumns.THREAD_ID,
+                        BluetoothMapContract.ConversationColumns.LAST_THREAD_ACTIVITY,
+                        BluetoothMapContract.ConversationColumns.THREAD_NAME,
+                        BluetoothMapContract.ConversationColumns.READ_STATUS,
+                        BluetoothMapContract.ConversationColumns.VERSION_COUNTER,
+                        BluetoothMapContract.ConversationColumns.SUMMARY,
+                        BluetoothMapContract.ConvoContactColumns.X_BT_UID,
+                        BluetoothMapContract.ConvoContactColumns.CHAT_STATE,
+                        BluetoothMapContract.ConvoContactColumns.UCI,
+                        BluetoothMapContract.ConvoContactColumns.NICKNAME,
+                        BluetoothMapContract.ConvoContactColumns.LAST_ACTIVE,
+                        BluetoothMapContract.ConvoContactColumns.NAME,
+                        BluetoothMapContract.ConvoContactColumns.PRESENCE_STATE,
+                        BluetoothMapContract.ConvoContactColumns.STATUS_TEXT,
+                        BluetoothMapContract.ConvoContactColumns.PRIORITY});
+        imEmailCursor.addRow(new Object[] {TEST_ID, TEST_DATE_EMAIL, TEST_NAME, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0});
+        doReturn(imEmailCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_CONVERSATION_PROJECTION), any(), any(), any());
+
+        BluetoothMapConvoListing listing = mContent.convoListing(mParams, false);
+
+        assertThat(listing.getCount()).isEqualTo(2);
+        BluetoothMapConvoListingElement imElement = listing.getList().get(0);
+        assertThat(imElement.getType()).isEqualTo(TYPE.IM);
+        assertThat(imElement.getLastActivity()).isEqualTo(TEST_DATE_EMAIL);
+        assertThat(imElement.getName()).isEqualTo(TEST_NAME);
+        assertThat(imElement.getReadBool()).isFalse();
+        BluetoothMapConvoListingElement smsElement = listing.getList().get(1);
+        assertThat(smsElement.getType()).isEqualTo(TYPE.SMS_GSM);
+        assertThat(smsElement.getLastActivity()).isEqualTo(TEST_DATE_SMS);
+        assertThat(smsElement.getName()).isEqualTo("");
+        assertThat(smsElement.getReadBool()).isTrue();
+    }
 }
\ No newline at end of file
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMasInstanceTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMasInstanceTest.java
new file mode 100644
index 0000000..621c991
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapMasInstanceTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2023 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.map;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothMapMasInstanceTest {
+    private static final int TEST_MAS_ID = 1;
+    private static final boolean TEST_ENABLE_SMS_MMS = true;
+    private static final String TEST_NAME = "test_name";
+    private static final String TEST_PACKAGE_NAME = "test.package.name";
+    private static final String TEST_ID = "1111";
+    private static final String TEST_PROVIDER_AUTHORITY = "test.project.provider";
+    private static final Drawable TEST_DRAWABLE = new ColorDrawable();
+    private static final BluetoothMapUtils.TYPE TEST_TYPE = BluetoothMapUtils.TYPE.EMAIL;
+    private static final String TEST_UCI = "uci";
+    private static final String TEST_UCI_PREFIX = "uci_prefix";
+
+    private BluetoothMapAccountItem mAccountItem;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private BluetoothMapService mMapService;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mAccountItem = BluetoothMapAccountItem.create(TEST_ID, TEST_NAME, TEST_PACKAGE_NAME,
+                TEST_PROVIDER_AUTHORITY, TEST_DRAWABLE, TEST_TYPE, TEST_UCI, TEST_UCI_PREFIX);
+    }
+
+    @Test
+    public void constructor_withNoParameters() {
+        BluetoothMapMasInstance instance = new BluetoothMapMasInstance();
+
+        assertThat(instance.mTag).isEqualTo(
+                "BluetoothMapMasInstance" + (BluetoothMapMasInstance.sInstanceCounter - 1));
+    }
+
+    @Test
+    public void toString_returnsInfo() {
+        BluetoothMapMasInstance instance = new BluetoothMapMasInstance(mMapService, mContext,
+                mAccountItem, TEST_MAS_ID, TEST_ENABLE_SMS_MMS);
+
+        String expected = "MasId: " + TEST_MAS_ID + " Uri:" + mAccountItem.mBase_uri + " SMS/MMS:"
+                + TEST_ENABLE_SMS_MMS;
+        assertThat(instance.toString()).isEqualTo(expected);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java
new file mode 100644
index 0000000..0a6f6bd
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2023 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.map;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.database.MatrixCursor;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.BluetoothMethodProxy;
+import com.android.bluetooth.mapapi.BluetoothMapContract;
+import com.android.obex.ResponseCodes;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothMapObexServerTest {
+    private static final int TEST_MAS_ID = 1;
+    private static final boolean TEST_ENABLE_SMS_MMS = true;
+    private static final String TEST_NAME = "test_name";
+    private static final String TEST_PACKAGE_NAME = "test.package.name";
+    private static final String TEST_ID = "1111";
+    private static final String TEST_PROVIDER_AUTHORITY = "test.project.provider";
+    private static final Drawable TEST_DRAWABLE = new ColorDrawable();
+    private static final BluetoothMapUtils.TYPE TEST_TYPE = BluetoothMapUtils.TYPE.IM;
+    private static final String TEST_UCI = "uci";
+    private static final String TEST_UCI_PREFIX = "uci_prefix";
+
+    private BluetoothMapAccountItem mAccountItem;
+    private BluetoothMapMasInstance mMasInstance;
+    private BluetoothMapObexServer mObexServer;
+    private BluetoothMapAppParams mParams;
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private BluetoothMapService mMapService;
+    @Mock
+    private ContentProviderClient mProviderClient;
+    @Mock
+    private BluetoothMapContentObserver mObserver;
+    @Spy
+    private BluetoothMethodProxy mMapMethodProxy = BluetoothMethodProxy.getInstance();
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        BluetoothMethodProxy.setInstanceForTesting(mMapMethodProxy);
+        doReturn(mProviderClient).when(
+                mMapMethodProxy).contentResolverAcquireUnstableContentProviderClient(any(), any());
+        mAccountItem = BluetoothMapAccountItem.create(TEST_ID, TEST_NAME, TEST_PACKAGE_NAME,
+                TEST_PROVIDER_AUTHORITY, TEST_DRAWABLE, TEST_TYPE, TEST_UCI, TEST_UCI_PREFIX);
+        mMasInstance = new BluetoothMapMasInstance(mMapService, mContext,
+                mAccountItem, TEST_MAS_ID, TEST_ENABLE_SMS_MMS);
+        mParams = new BluetoothMapAppParams();
+        mObexServer = new BluetoothMapObexServer(null, mContext, mObserver, mMasInstance,
+                mAccountItem, TEST_ENABLE_SMS_MMS);
+    }
+
+    @Test
+    public void setOwnerStatus_withAccountTypeEmail() throws Exception {
+        doReturn(null).when(mProviderClient).query(any(), any(), any(), any(), any());
+        BluetoothMapAccountItem accountItemWithTypeEmail = BluetoothMapAccountItem.create(TEST_ID,
+                TEST_NAME, TEST_PACKAGE_NAME, TEST_PROVIDER_AUTHORITY, TEST_DRAWABLE,
+                BluetoothMapUtils.TYPE.EMAIL, TEST_UCI, TEST_UCI_PREFIX);
+        BluetoothMapObexServer obexServer = new BluetoothMapObexServer(null, mContext, mObserver,
+                mMasInstance, accountItemWithTypeEmail, TEST_ENABLE_SMS_MMS);
+
+        assertThat(obexServer.setOwnerStatus(mParams)).isEqualTo(
+                ResponseCodes.OBEX_HTTP_UNAVAILABLE);
+    }
+
+    @Test
+    public void setOwnerStatus_withAppParamsInvalid() throws Exception {
+        BluetoothMapAppParams params = mock(BluetoothMapAppParams.class);
+        when(params.getPresenceAvailability()).thenReturn(
+                BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        when(params.getPresenceStatus()).thenReturn(null);
+        when(params.getLastActivity()).thenReturn(
+                (long) BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        when(params.getChatState()).thenReturn(BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        when(params.getChatStateConvoIdString()).thenReturn(null);
+
+        assertThat(mObexServer.setOwnerStatus(params)).isEqualTo(
+                ResponseCodes.OBEX_HTTP_PRECON_FAILED);
+    }
+
+    @Test
+    public void setOwnerStatus_withNonNullBundle() throws Exception {
+        setUpBluetoothMapAppParams(mParams);
+        Bundle bundle = new Bundle();
+        when(mProviderClient.call(any(), any(), any())).thenReturn(bundle);
+
+        assertThat(mObexServer.setOwnerStatus(mParams)).isEqualTo(
+                ResponseCodes.OBEX_HTTP_OK);
+    }
+
+    @Test
+    public void setOwnerStatus_withNullBundle() throws Exception {
+        setUpBluetoothMapAppParams(mParams);
+        when(mProviderClient.call(any(), any(), any())).thenReturn(null);
+
+        assertThat(mObexServer.setOwnerStatus(mParams)).isEqualTo(
+                ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED);
+    }
+
+    @Test
+    public void setOwnerStatus_withRemoteExceptionThrown() throws Exception {
+        setUpBluetoothMapAppParams(mParams);
+        doThrow(RemoteException.class).when(mProviderClient).call(any(), any(), any());
+
+        assertThat(mObexServer.setOwnerStatus(mParams)).isEqualTo(
+                ResponseCodes.OBEX_HTTP_UNAVAILABLE);
+    }
+
+    @Test
+    public void setOwnerStatus_withNullPointerExceptionThrown() throws Exception {
+        setUpBluetoothMapAppParams(mParams);
+        doThrow(NullPointerException.class).when(mProviderClient).call(any(), any(), any());
+
+        assertThat(mObexServer.setOwnerStatus(mParams)).isEqualTo(
+                ResponseCodes.OBEX_HTTP_UNAVAILABLE);
+    }
+
+    @Test
+    public void setOwnerStatus_withIllegalArgumentExceptionThrown() throws Exception {
+        setUpBluetoothMapAppParams(mParams);
+        doThrow(IllegalArgumentException.class).when(mProviderClient).call(any(), any(), any());
+
+        assertThat(mObexServer.setOwnerStatus(mParams)).isEqualTo(
+                ResponseCodes.OBEX_HTTP_UNAVAILABLE);
+    }
+
+    @Test
+    public void addEmailFolders() throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[]{BluetoothMapContract.FolderColumns.NAME,
+                BluetoothMapContract.FolderColumns._ID});
+        long parentId = 1;
+        long childId = 2;
+        cursor.addRow(new Object[]{"test_name", childId});
+        cursor.moveToFirst();
+        BluetoothMapFolderElement parentFolder = new BluetoothMapFolderElement("parent", null);
+        parentFolder.setFolderId(parentId);
+        doReturn(cursor).when(mProviderClient).query(any(), any(),
+                eq(BluetoothMapContract.FolderColumns.PARENT_FOLDER_ID + " = " + parentId), any(),
+                any());
+
+        mObexServer.addEmailFolders(parentFolder);
+
+        assertThat(parentFolder.getFolderById(childId)).isNotNull();
+    }
+
+    private void setUpBluetoothMapAppParams(BluetoothMapAppParams params) {
+        params.setPresenceAvailability(1);
+        params.setPresenceStatus("test_presence_status");
+        params.setLastActivity(0);
+        params.setChatState(1);
+        params.setChatStateConvoId(1, 1);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceTest.java
index 511aba1..1a3ed24 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceTest.java
@@ -15,24 +15,38 @@
  */
 package com.android.bluetooth.map;
 
+import static com.android.bluetooth.map.BluetoothMapService.MSG_MAS_CONNECT_CANCEL;
+import static com.android.bluetooth.map.BluetoothMapService.UPDATE_MAS_INSTANCES;
+import static com.android.bluetooth.map.BluetoothMapService.USER_TIMEOUT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
@@ -44,9 +58,11 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class BluetoothMapServiceTest {
+    private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
+
     private BluetoothMapService mService = null;
     private BluetoothAdapter mAdapter = null;
-    private Context mTargetContext;
+    private BluetoothDevice mRemoteDevice;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
@@ -55,7 +71,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mTargetContext = InstrumentationRegistry.getTargetContext();
         Assume.assumeTrue("Ignore test when BluetoothMapService is not enabled",
                 BluetoothMapService.isEnabled());
         MockitoAnnotations.initMocks(this);
@@ -64,10 +79,11 @@
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
         TestUtils.startService(mServiceRule, BluetoothMapService.class);
         mService = BluetoothMapService.getBluetoothMapService();
-        Assert.assertNotNull(mService);
+        assertThat(mService).isNotNull();
         // Try getting the Bluetooth adapter
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        Assert.assertNotNull(mAdapter);
+        assertThat(mAdapter).isNotNull();
+        mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
     }
 
     @After
@@ -77,12 +93,79 @@
         }
         TestUtils.stopService(mServiceRule, BluetoothMapService.class);
         mService = BluetoothMapService.getBluetoothMapService();
-        Assert.assertNull(mService);
+        assertThat(mService).isNull();
         TestUtils.clearAdapterService(mAdapterService);
     }
 
     @Test
-    public void testInitialize() {
-        Assert.assertNotNull(BluetoothMapService.getBluetoothMapService());
+    public void initialize() {
+        assertThat(BluetoothMapService.getBluetoothMapService()).isNotNull();
+    }
+
+    @Test
+    public void getDevicesMatchingConnectionStates_whenNoDeviceIsConnected_returnsEmptyList() {
+        when(mAdapterService.getBondedDevices()).thenReturn(new BluetoothDevice[] {mRemoteDevice});
+
+        assertThat(mService.getDevicesMatchingConnectionStates(
+                new int[] {BluetoothProfile.STATE_CONNECTED})).isEmpty();
+    }
+
+    @Test
+    public void getNextMasId_isInRange() {
+        int masId = mService.getNextMasId();
+        assertThat(masId).isAtMost(0xff);
+        assertThat(masId).isAtLeast(1);
+    }
+
+    @Test
+    public void sendConnectCancelMessage() {
+        TestableHandler handler = spy(new TestableHandler(Looper.getMainLooper()));
+        mService.mSessionStatusHandler = handler;
+
+        mService.sendConnectCancelMessage();
+
+        verify(handler, timeout(1_000)).messageArrived(
+                eq(MSG_MAS_CONNECT_CANCEL), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void sendConnectTimeoutMessage() {
+        TestableHandler handler = spy(new TestableHandler(Looper.getMainLooper()));
+        mService.mSessionStatusHandler = handler;
+
+        mService.sendConnectTimeoutMessage();
+
+        verify(handler, timeout(1_000)).messageArrived(
+                eq(USER_TIMEOUT), anyInt(), anyInt(), any());
+    }
+
+    @Test
+    public void updateMasInstances() {
+        int action = 5;
+        TestableHandler handler = spy(new TestableHandler(Looper.getMainLooper()));
+        mService.mSessionStatusHandler = handler;
+
+        mService.updateMasInstances(action);
+
+        verify(handler, timeout(1_000)).messageArrived(
+                eq(UPDATE_MAS_INSTANCES), eq(action), anyInt(), any());
+    }
+
+    public static class TestableHandler extends Handler {
+        public TestableHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            messageArrived(msg.what, msg.arg1, msg.arg2, msg.obj);
+        }
+
+        public void messageArrived(int what, int arg1, int arg2, Object obj) {}
+    }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        mService.dump(new StringBuilder());
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSettingsTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSettingsTest.java
new file mode 100644
index 0000000..c1d7bc8
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapSettingsTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2023 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.map;
+
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.DONT_KILL_APP;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.R;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothMapSettingsTest {
+
+    Context mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+    Intent mIntent;
+
+    ActivityScenario<BluetoothMapSettings> mActivityScenario;
+
+    @Before
+    public void setUp() {
+        enableActivity(true);
+
+        mIntent = new Intent();
+        mIntent.setClass(mTargetContext, BluetoothMapSettings.class);
+
+        mActivityScenario = ActivityScenario.launch(mIntent);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        if (mActivityScenario != null) {
+            // Workaround for b/159805732. Without this, test hangs for 45 seconds.
+            Thread.sleep(1_000);
+            mActivityScenario.close();
+        }
+
+        enableActivity(false);
+    }
+
+    @Test
+    public void initialize() throws Exception {
+        onView(withId(R.id.bluetooth_map_settings_list_view)).check(matches(isDisplayed()));
+    }
+
+    private void enableActivity(boolean enable) {
+        int enabledState = enable ? COMPONENT_ENABLED_STATE_ENABLED
+                : COMPONENT_ENABLED_STATE_DEFAULT;
+
+        mTargetContext.getPackageManager().setApplicationEnabledSetting(
+                mTargetContext.getPackageName(), enabledState, DONT_KILL_APP);
+
+        ComponentName activityName = new ComponentName(mTargetContext, BluetoothMapSettings.class);
+        mTargetContext.getPackageManager().setComponentEnabledSetting(
+                activityName, enabledState, DONT_KILL_APP);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapUtilsTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapUtilsTest.java
new file mode 100644
index 0000000..6a8eb18
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapUtilsTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.map;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.database.MatrixCursor;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.bluetooth.mapapi.BluetoothMapContract;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothMapUtilsTest {
+
+    private static final String TEXT = "코드";
+    private static final String QUOTED_PRINTABLE_ENCODED_TEXT = "=EC=BD=94=EB=93=9C";
+    private static final String BASE64_ENCODED_TEXT = "7L2U65Oc";
+
+    @Test
+    public void encodeQuotedPrintable_withNullInput_returnsNull() {
+        assertThat(BluetoothMapUtils.encodeQuotedPrintable(null)).isNull();
+    }
+
+    @Test
+    public void encodeQuotedPrintable() {
+        assertThat(BluetoothMapUtils.encodeQuotedPrintable(TEXT.getBytes(StandardCharsets.UTF_8)))
+                .isEqualTo(QUOTED_PRINTABLE_ENCODED_TEXT);
+    }
+
+    @Test
+    public void quotedPrintableToUtf8() {
+        assertThat(BluetoothMapUtils.quotedPrintableToUtf8(QUOTED_PRINTABLE_ENCODED_TEXT, null))
+                .isEqualTo(TEXT.getBytes(StandardCharsets.UTF_8));
+    }
+
+    @Test
+    public void printCursor_doesNotCrash() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.PresenceColumns.LAST_ONLINE, "Name"});
+        cursor.addRow(new Object[] {345345226L, "test_name"});
+
+        BluetoothMapUtils.printCursor(cursor);
+    }
+
+    @Test
+    public void stripEncoding_quotedPrintable() {
+        assertThat(BluetoothMapUtils.stripEncoding("=?UTF-8?Q?" + QUOTED_PRINTABLE_ENCODED_TEXT
+                + "?=")).isEqualTo(TEXT);
+    }
+
+    @Test
+    public void stripEncoding_base64() {
+        assertThat(BluetoothMapUtils.stripEncoding("=?UTF-8?B?" + BASE64_ENCODED_TEXT + "?="))
+                .isEqualTo(TEXT);
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
index c78616e..3249a42 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientTest.java
@@ -24,6 +24,7 @@
 import android.bluetooth.IBluetoothMapClient;
 import android.content.Context;
 import android.os.UserHandle;
+import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
@@ -122,6 +123,8 @@
 
         // is the statemachine created
         Map<BluetoothDevice, MceStateMachine> map = mService.getInstanceMap();
+        Log.d("MapClientTest", "map=" + map);
+
         Assert.assertEquals(1, map.size());
         Assert.assertNotNull(map.get(device));
         TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MnsObexServerTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MnsObexServerTest.java
new file mode 100644
index 0000000..e57ab50
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MnsObexServerTest.java
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.mapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.obex.HeaderSet;
+import com.android.obex.Operation;
+import com.android.obex.ResponseCodes;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MnsObexServerTest {
+
+    @Mock
+    MceStateMachine mStateMachine;
+
+    MnsObexServer mServer;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mServer = new MnsObexServer(mStateMachine, null);
+    }
+
+    @Test
+    public void onConnect_whenUuidIsWrong() {
+        byte[] wrongUuid = new byte[]{};
+        HeaderSet request = new HeaderSet();
+        request.setHeader(HeaderSet.TARGET, wrongUuid);
+        HeaderSet reply = new HeaderSet();
+
+        assertThat(mServer.onConnect(request, reply))
+                .isEqualTo(ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE);
+    }
+
+    @Test
+    public void onConnect_withCorrectUuid() throws Exception {
+        HeaderSet request = new HeaderSet();
+        request.setHeader(HeaderSet.TARGET, MnsObexServer.MNS_TARGET);
+        HeaderSet reply = new HeaderSet();
+
+        assertThat(mServer.onConnect(request, reply)).isEqualTo(ResponseCodes.OBEX_HTTP_OK);
+        assertThat(reply.getHeader(HeaderSet.WHO)).isEqualTo(MnsObexServer.MNS_TARGET);
+    }
+
+    @Test
+    public void onDisconnect_callsStateMachineDisconnect() {
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+
+        mServer.onDisconnect(request, reply);
+
+        verify(mStateMachine).disconnect();
+    }
+
+    @Test
+    public void onGet_returnsBadRequest() {
+        Operation op = mock(Operation.class);
+
+        assertThat(mServer.onGet(op)).isEqualTo(ResponseCodes.OBEX_HTTP_BAD_REQUEST);
+    }
+
+    @Test
+    public void onPut_whenTypeIsInvalid_returnsBadRequest() throws Exception {
+        HeaderSet headerSet = new HeaderSet();
+        headerSet.setHeader(HeaderSet.TYPE, "some_invalid_type");
+        Operation op = mock(Operation.class);
+        when(op.getReceivedHeader()).thenReturn(headerSet);
+
+        assertThat(mServer.onPut(op)).isEqualTo(ResponseCodes.OBEX_HTTP_BAD_REQUEST);
+    }
+
+    @Test
+    public void onPut_whenHeaderSetIsValid_returnsOk() throws Exception {
+        final StringBuilder xml = new StringBuilder();
+        xml.append("<event\n");
+        xml.append("    type=\"test_type\"\n");
+        xml.append("    handle=\"FFAB\"\n");
+        xml.append("    folder=\"test_folder\"\n");
+        xml.append("    old_folder=\"test_old_folder\"\n");
+        xml.append("    msg_type=\"MMS\"\n");
+        xml.append("/>\n");
+        DataInputStream stream = new DataInputStream(
+                new ByteArrayInputStream(xml.toString().getBytes()));
+
+        byte[] applicationParameter = new byte[] {
+                Request.OAP_TAGID_MAS_INSTANCE_ID,
+                1, // length in byte
+                (byte) 55
+        };
+
+        HeaderSet headerSet = new HeaderSet();
+        headerSet.setHeader(HeaderSet.TYPE, MnsObexServer.TYPE);
+        headerSet.setHeader(HeaderSet.APPLICATION_PARAMETER, applicationParameter);
+
+        Operation op = mock(Operation.class);
+        when(op.getReceivedHeader()).thenReturn(headerSet);
+        when(op.openDataInputStream()).thenReturn(stream);
+
+        assertThat(mServer.onPut(op)).isEqualTo(ResponseCodes.OBEX_HTTP_OK);
+
+        verify(mStateMachine).receiveEvent(any());
+    }
+
+    @Test
+    public void onAbort_returnsNotImplemented() {
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+
+        assertThat(mServer.onAbort(request, reply))
+                .isEqualTo(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED);
+    }
+
+    @Test
+    public void onSetPath_returnsBadRequest() {
+        HeaderSet request = new HeaderSet();
+        HeaderSet reply = new HeaderSet();
+
+        assertThat(mServer.onSetPath(request, reply, false, false))
+                .isEqualTo(ResponseCodes.OBEX_HTTP_BAD_REQUEST);
+    }
+
+    @Test
+    public void onClose_doesNotCrash() {
+        mServer.onClose();
+    }
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
index 3374607..cfd051a 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceTest.java
@@ -136,4 +136,9 @@
             }
         });
     }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        mMcpService.dump(new StringBuilder());
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
index 3ec632a..bed8b79 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/MediaControlProfileTest.java
@@ -465,4 +465,9 @@
         testGetSupportedPlayingOrder(false, true);
         testGetSupportedPlayingOrder(false, false);
     }
+
+    @Test
+    public void testDumpDoesNotCrash() {
+        mMediaControlProfile.dump(new StringBuilder());
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java
index 07c88b8..e16e310 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java
@@ -57,6 +57,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -65,7 +66,6 @@
 
 import java.io.File;
 
-
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class BluetoothOppLauncherActivityTest {
@@ -139,6 +139,7 @@
         assertThat(argument.getValue().getData()).isEqualTo(Uri.EMPTY);
     }
 
+    @Ignore("b/263724420")
     @Test
     public void launchDevicePicker_bluetoothNotEnabled_launchEnableActivity() throws Exception {
         doReturn(false).when(mMethodProxy).bluetoothAdapterIsEnabled(any());
@@ -151,6 +152,7 @@
         intended(hasComponent(BluetoothOppBtEnableActivity.class.getName()));
     }
 
+    @Ignore("b/263724420")
     @Test
     public void launchDevicePicker_bluetoothEnabled_launchActivity() throws Exception {
         doReturn(true).when(mMethodProxy).bluetoothAdapterIsEnabled(any());
@@ -185,6 +187,7 @@
         assertThat(file.length()).isGreaterThan(shareContent.length());
     }
 
+    @Ignore("b/263754734")
     @Test
     public void sendFileInfo_finishImmediately() throws Exception {
         doReturn(true).when(mMethodProxy).bluetoothAdapterIsEnabled(any());
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java
index 242db8d..2f087f4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppReceiverTest.java
@@ -54,6 +54,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -93,6 +94,7 @@
         Intents.release();
     }
 
+    @Ignore("b/262201478")
     @Test
     public void onReceive_withActionDeviceSelected_callsStartTransfer() {
         BluetoothOppManager bluetoothOppManager = spy(BluetoothOppManager.getInstance(mContext));
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
index a6736cf..fb33486 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceTest.java
@@ -15,27 +15,32 @@
  */
 package com.android.bluetooth.pan;
 
+import static android.bluetooth.BluetoothPan.PAN_ROLE_NONE;
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
+
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
-import android.content.Context;
+import android.bluetooth.BluetoothProfile;
+import android.net.TetheringInterface;
 import android.os.UserManager;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
+import com.android.bluetooth.pan.PanService.BluetoothPanDevice;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
@@ -47,9 +52,12 @@
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class PanServiceTest {
+    private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
+    private static final byte[] REMOTE_DEVICE_ADDRESS_AS_ARRAY = new byte[] {0, 0, 0, 0, 0, 0};
+
     private PanService mService = null;
     private BluetoothAdapter mAdapter = null;
-    private Context mTargetContext;
+    private BluetoothDevice mRemoteDevice;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
@@ -59,7 +67,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mTargetContext = InstrumentationRegistry.getTargetContext();
         Assume.assumeTrue("Ignore test when PanService is not enabled",
                 PanService.isEnabled());
         MockitoAnnotations.initMocks(this);
@@ -68,11 +75,12 @@
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
         TestUtils.startService(mServiceRule, PanService.class);
         mService = PanService.getPanService();
-        Assert.assertNotNull(mService);
+        assertThat(mService).isNotNull();
         // Try getting the Bluetooth adapter
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        Assert.assertNotNull(mAdapter);
+        assertThat(mAdapter).isNotNull();
         mService.mUserManager = mMockUserManager;
+        mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
     }
 
     @After
@@ -82,19 +90,127 @@
         }
         TestUtils.stopService(mServiceRule, PanService.class);
         mService = PanService.getPanService();
-        Assert.assertNull(mService);
+        assertThat(mService).isNull();
         TestUtils.clearAdapterService(mAdapterService);
     }
 
     @Test
-    public void testInitialize() {
-        Assert.assertNotNull(PanService.getPanService());
+    public void initialize() {
+        assertThat(PanService.getPanService()).isNotNull();
     }
 
     @Test
-    public void testGuestUserConnect() {
-        BluetoothDevice device = TestUtils.getTestDevice(mAdapter, 0);
+    public void connect_whenGuestUser_returnsFalse() {
         when(mMockUserManager.isGuestUser()).thenReturn(true);
-        Assert.assertFalse(mService.connect(device));
+        assertThat(mService.connect(mRemoteDevice)).isFalse();
+    }
+
+    @Test
+    public void connect_inConnectedState_returnsFalse() {
+        when(mMockUserManager.isGuestUser()).thenReturn(false);
+        mService.mPanDevices.put(mRemoteDevice, new BluetoothPanDevice(
+                BluetoothProfile.STATE_CONNECTED, "iface", PAN_ROLE_NONE, PAN_ROLE_NONE));
+
+        assertThat(mService.connect(mRemoteDevice)).isFalse();
+    }
+
+    @Test
+    public void connect() {
+        when(mMockUserManager.isGuestUser()).thenReturn(false);
+        mService.mPanDevices.put(mRemoteDevice, new BluetoothPanDevice(
+                BluetoothProfile.STATE_DISCONNECTED, "iface", PAN_ROLE_NONE, PAN_ROLE_NONE));
+
+        assertThat(mService.connect(mRemoteDevice)).isTrue();
+    }
+
+    @Test
+    public void disconnect_returnsTrue() {
+        assertThat(mService.disconnect(mRemoteDevice)).isTrue();
+    }
+
+    @Test
+    public void convertHalState() {
+        assertThat(PanService.convertHalState(PanService.CONN_STATE_CONNECTED))
+                .isEqualTo(BluetoothProfile.STATE_CONNECTED);
+        assertThat(PanService.convertHalState(PanService.CONN_STATE_CONNECTING))
+                .isEqualTo(BluetoothProfile.STATE_CONNECTING);
+        assertThat(PanService.convertHalState(PanService.CONN_STATE_DISCONNECTED))
+                .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+        assertThat(PanService.convertHalState(PanService.CONN_STATE_DISCONNECTING))
+                .isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
+        assertThat(PanService.convertHalState(-24664)) // illegal value
+                .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void dump() {
+        mService.mPanDevices.put(mRemoteDevice, new BluetoothPanDevice(
+                BluetoothProfile.STATE_DISCONNECTED, "iface", PAN_ROLE_NONE, PAN_ROLE_NONE));
+
+        mService.dump(new StringBuilder());
+    }
+
+    @Test
+    public void onConnectStateChanged_doesNotCrash() {
+        mService.onConnectStateChanged(REMOTE_DEVICE_ADDRESS_AS_ARRAY, 1, 2, 3, 4);
+    }
+
+    @Test
+    public void onControlStateChanged_doesNotCrash() {
+        mService.onControlStateChanged(1, 2, 3, "ifname");
+    }
+
+    @Test
+    public void setConnectionPolicy_whenDatabaseManagerRefuses_returnsFalse() {
+        int connectionPolicy = BluetoothProfile.CONNECTION_POLICY_ALLOWED;
+        when(mDatabaseManager.setProfileConnectionPolicy(
+                mRemoteDevice, BluetoothProfile.PAN, connectionPolicy)).thenReturn(false);
+
+        assertThat(mService.setConnectionPolicy(mRemoteDevice, connectionPolicy)).isFalse();
+    }
+
+    @Test
+    public void setConnectionPolicy_returnsTrue() {
+        when(mDatabaseManager.setProfileConnectionPolicy(
+                mRemoteDevice, BluetoothProfile.PAN, BluetoothProfile.CONNECTION_POLICY_ALLOWED))
+                .thenReturn(true);
+        assertThat(mService.setConnectionPolicy(
+                mRemoteDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue();
+
+        when(mDatabaseManager.setProfileConnectionPolicy(
+                mRemoteDevice, BluetoothProfile.PAN, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN))
+                .thenReturn(true);
+        assertThat(mService.setConnectionPolicy(
+                mRemoteDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)).isTrue();
+    }
+
+    @Test
+    public void connectState_constructor() {
+        int state = 1;
+        int error = 2;
+        int localRole = 3;
+        int remoteRole = 4;
+
+        PanService.ConnectState connectState = new PanService.ConnectState(
+                REMOTE_DEVICE_ADDRESS_AS_ARRAY, state, error, localRole, remoteRole);
+
+        assertThat(connectState.addr).isEqualTo(REMOTE_DEVICE_ADDRESS_AS_ARRAY);
+        assertThat(connectState.state).isEqualTo(state);
+        assertThat(connectState.error).isEqualTo(error);
+        assertThat(connectState.local_role).isEqualTo(localRole);
+        assertThat(connectState.remote_role).isEqualTo(remoteRole);
+    }
+
+    @Test
+    public void tetheringCallback_onError_clearsPanDevices() {
+        mService.mIsTethering = true;
+        mService.mPanDevices.put(mRemoteDevice, new BluetoothPanDevice(
+                BluetoothProfile.STATE_DISCONNECTED, "iface", PAN_ROLE_NONE, PAN_ROLE_NONE));
+        TetheringInterface iface = new TetheringInterface(TETHERING_BLUETOOTH, "iface");
+
+        mService.mTetheringCallback.onError(iface, TETHER_ERROR_SERVICE_UNAVAIL);
+
+        assertThat(mService.mPanDevices).isEmpty();
+        assertThat(mService.mIsTethering).isFalse();
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapObexServerTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapObexServerTest.java
index 37a449b..62834b4 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapObexServerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapObexServerTest.java
@@ -847,4 +847,10 @@
         assertThat(mServer.parseApplicationParameter(rawBytes, appParamValue)).isTrue();
         assertThat(appParamValue.vCardSelectorOperator).isEqualTo("1");
     }
+
+    @Test
+    public void appParamValueDump_doesNotCrash() {
+        AppParamValue appParamValue = new AppParamValue();
+        appParamValue.dump();
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java
index 75fb5ed..44aedc1 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceTest.java
@@ -15,38 +15,47 @@
  */
 package com.android.bluetooth.pbap;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Intent;
+import android.os.Message;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class BluetoothPbapServiceTest {
+    private static final String REMOTE_DEVICE_ADDRESS = "00:00:00:00:00:00";
+
     private BluetoothPbapService mService;
     private BluetoothAdapter mAdapter = null;
-    private Context mTargetContext;
+    private BluetoothDevice mRemoteDevice;
 
     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
 
@@ -55,7 +64,6 @@
 
     @Before
     public void setUp() throws Exception {
-        mTargetContext = InstrumentationRegistry.getTargetContext();
         Assume.assumeTrue("Ignore test when BluetoothPbapService is not enabled",
                 BluetoothPbapService.isEnabled());
         MockitoAnnotations.initMocks(this);
@@ -64,10 +72,11 @@
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
         TestUtils.startService(mServiceRule, BluetoothPbapService.class);
         mService = BluetoothPbapService.getBluetoothPbapService();
-        Assert.assertNotNull(mService);
+        assertThat(mService).isNotNull();
         // Try getting the Bluetooth adapter
         mAdapter = BluetoothAdapter.getDefaultAdapter();
-        Assert.assertNotNull(mAdapter);
+        assertThat(mAdapter).isNotNull();
+        mRemoteDevice = mAdapter.getRemoteDevice(REMOTE_DEVICE_ADDRESS);
     }
 
     @After
@@ -77,12 +86,122 @@
         }
         TestUtils.stopService(mServiceRule, BluetoothPbapService.class);
         mService = BluetoothPbapService.getBluetoothPbapService();
-        Assert.assertNull(mService);
+        assertThat(mService).isNull();
         TestUtils.clearAdapterService(mAdapterService);
     }
 
     @Test
-    public void testInitialize() {
-        Assert.assertNotNull(BluetoothPbapService.getBluetoothPbapService());
+    public void initialize() {
+        assertThat(BluetoothPbapService.getBluetoothPbapService()).isNotNull();
+    }
+
+    @Test
+    public void disconnect() {
+        PbapStateMachine sm = mock(PbapStateMachine.class);
+        mService.mPbapStateMachineMap.put(mRemoteDevice, sm);
+
+        mService.disconnect(mRemoteDevice);
+
+        verify(sm).sendMessage(PbapStateMachine.DISCONNECT);
+    }
+
+    @Test
+    public void getConnectedDevices() {
+        PbapStateMachine sm = mock(PbapStateMachine.class);
+        mService.mPbapStateMachineMap.put(mRemoteDevice, sm);
+
+        assertThat(mService.getConnectedDevices()).contains(mRemoteDevice);
+    }
+
+    @Test
+    public void getConnectionPolicy_withDeviceIsNull_throwsNPE() {
+        assertThrows(IllegalArgumentException.class, () -> mService.getConnectionPolicy(null));
+    }
+
+    @Test
+    public void getConnectionPolicy() {
+        mService.getConnectionPolicy(mRemoteDevice);
+
+        verify(mDatabaseManager).getProfileConnectionPolicy(mRemoteDevice, BluetoothProfile.PBAP);
+    }
+
+    @Test
+    public void getDevicesMatchingConnectionStates_whenStatesIsNull_returnsEmptyList() {
+        assertThat(mService.getDevicesMatchingConnectionStates(null)).isEmpty();
+    }
+
+    @Test
+    public void getDevicesMatchingConnectionStates() {
+        PbapStateMachine sm = mock(PbapStateMachine.class);
+        mService.mPbapStateMachineMap.put(mRemoteDevice, sm);
+        when(sm.getConnectionState()).thenReturn(BluetoothProfile.STATE_CONNECTED);
+
+        int[] states = new int[] {BluetoothProfile.STATE_CONNECTED};
+        assertThat(mService.getDevicesMatchingConnectionStates(states)).contains(mRemoteDevice);
+    }
+
+    @Test
+    public void onAcceptFailed() {
+        PbapStateMachine sm = mock(PbapStateMachine.class);
+        mService.mPbapStateMachineMap.put(mRemoteDevice, sm);
+
+        mService.onAcceptFailed();
+
+        assertThat(mService.mPbapStateMachineMap).isEmpty();
+    }
+
+    @Test
+    public void broadcastReceiver_onReceive_withActionConnectionAccessReply() {
+        Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
+        intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
+                BluetoothDevice.REQUEST_TYPE_PHONEBOOK_ACCESS);
+        intent.putExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
+                BluetoothDevice.CONNECTION_ACCESS_YES);
+        intent.putExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, true);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+        PbapStateMachine sm = mock(PbapStateMachine.class);
+        mService.mPbapStateMachineMap.put(mRemoteDevice, sm);
+
+        mService.mPbapReceiver.onReceive(null, intent);
+
+        verify(sm).sendMessage(PbapStateMachine.AUTHORIZED);
+    }
+
+    @Test
+    public void broadcastReceiver_onReceive_withActionAuthResponse() {
+        Intent intent = new Intent(BluetoothPbapService.AUTH_RESPONSE_ACTION);
+        String sessionKey = "test_session_key";
+        intent.putExtra(BluetoothPbapService.EXTRA_SESSION_KEY, sessionKey);
+        intent.putExtra(BluetoothPbapService.EXTRA_DEVICE, mRemoteDevice);
+        PbapStateMachine sm = mock(PbapStateMachine.class);
+        mService.mPbapStateMachineMap.put(mRemoteDevice, sm);
+
+        mService.mPbapReceiver.onReceive(null, intent);
+
+        ArgumentCaptor<Message> captor = ArgumentCaptor.forClass(Message.class);
+        verify(sm).sendMessage(captor.capture());
+        Message msg = captor.getValue();
+        assertThat(msg.what).isEqualTo(PbapStateMachine.AUTH_KEY_INPUT);
+        assertThat(msg.obj).isEqualTo(sessionKey);
+        msg.recycle();
+    }
+
+    @Test
+    public void broadcastReceiver_onReceive_withActionAuthCancelled() {
+        Intent intent = new Intent(BluetoothPbapService.AUTH_CANCELLED_ACTION);
+        intent.putExtra(BluetoothPbapService.EXTRA_DEVICE, mRemoteDevice);
+        PbapStateMachine sm = mock(PbapStateMachine.class);
+        mService.mPbapStateMachineMap.put(mRemoteDevice, sm);
+
+        mService.mPbapReceiver.onReceive(null, intent);
+
+        verify(sm).sendMessage(PbapStateMachine.AUTH_CANCELLED);
+    }
+
+    @Test
+    public void broadcastReceiver_onReceive_withIllegalAction_doesNothing() {
+        Intent intent = new Intent("test_random_action");
+
+        mService.mPbapReceiver.onReceive(null, intent);
     }
 }
diff --git a/android/pandora/mmi2grpc/mmi2grpc/avrcp.py b/android/pandora/mmi2grpc/mmi2grpc/avrcp.py
index 7e1d9c2..92972dc 100644
--- a/android/pandora/mmi2grpc/mmi2grpc/avrcp.py
+++ b/android/pandora/mmi2grpc/mmi2grpc/avrcp.py
@@ -793,3 +793,169 @@
         self.mediaplayer.Play()
 
         return "OK"
+
+    @assert_description
+    def _mmi_1016(self, test: str, pts_addr: bytes, **kwargs):
+        """
+        Create an AVDTP signaling channel.
+
+        Action: Create an audio or video
+        connection with PTS.
+        """
+        self.connection = self.host.Connect(address=pts_addr).connection
+        if "TG" in test:
+            try:
+                self.source = self.a2dp.OpenSource(connection=self.connection).source
+            except RpcError:
+                pass
+        else:
+            try:
+                self.sink = self.a2dp.WaitSink(connection=self.connection).sink
+            except RpcError:
+                pass
+
+        return "OK"
+
+    @assert_description
+    def TSC_AVCTP_mmi_send_AVCT_ConnectReq(self, pts_addr: bytes, **kwargs):
+        """
+        Using the Upper Tester, send an AVCT_ConnectReq command to the IUT with
+        the following input parameter values:
+           * BD_ADDR = BD_ADDRLower_Tester
+        * PID = PIDTest_System
+
+        The IUT should then initiate an
+        L2CAP_ConnectReq.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_AVCTP_mmi_verify_ConnectCfm_CB(self, pts_addr: bytes, **kwargs):
+        """
+        Press 'OK' if the following conditions were met :
+
+        1. The IUT returns
+        the following AVCT_ConnectReq output parameters to the Upper Tester:
+        * Result = 0x0000 (Event successfully registered)
+
+        2. The IUT calls the
+        ConnectCfm_CBTest_System function in the Upper Tester with the following
+        parameters:
+           * BD_ADDR = BD_ADDRLower_Tester
+           * Connect Result =
+        0x0000 (L2CAP Connect Request successful)
+           * Config Result = 0x0000
+        (L2CAP Configure successful)
+           * Status = L2CAP Connect Request Status
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_AVCTP_mmi_register_DisconnectCfm_CB(self, pts_addr: bytes, **kwargs):
+        """
+        Using the Upper Tester register the function DisconnectCfm_CBTest_System
+        for callback on the AVCT_Disconnect_Cfm event by sending an
+        AVCT_EventRegistration command to the IUT with the following parameter
+        values:
+           * Event = AVCT_Disconnect_Cfm
+           * Callback =
+        DisconnectCfm_CBTest_System
+           * PID = PIDTest_System
+
+        Press 'OK' to
+        continue once the IUT has responded.
+        """
+
+        return "OK"
+
+    def TSC_AVCTP_mmi_send_AVCT_Disconnect_Req(self, test: str, pts_addr: bytes, **kwargs):
+        """
+        Using the Upper Tester send an AVCT_DisconnectReq command to the IUT
+        with the following parameter values:
+           * BD_ADDR = BD_ADDRLower_Tester
+        * PID = PIDTest_System
+
+        The IUT should then initiate an
+        L2CAP_DisconnectReq.   
+        """
+        # Currently disconnect is required in TG role
+        if "TG" in test:
+            if self.connection is None:
+                self.connection = self.host.GetConnection(address=pts_addr).connection
+            time.sleep(3)
+            self.host.Disconnect(connection=self.connection)
+            self.connection = None
+
+        return "OK"
+
+    @assert_description
+    def TSC_AVCTP_mmi_verify_DisconnectCfm_CB(self, **kwargs):
+        """
+        Press 'OK' if the following conditions were met :
+
+        1. The IUT returns
+        the following AVCT_EventRegistration output parameters to the Upper
+        Tester:
+           * Result = 0x0000 (Event successfully registered)
+
+        2. The IUT
+        calls the DisconnectCfm_CBTest_System function in the Upper Tester with
+        the following parameter values:
+           * BD_ADDR = BD_ADDRLower_Tester
+           *
+        Disconnect Result = 0x0000 (L2CAP disconnect success)
+
+        3. The IUT
+        returns the following AVCT_DisconnectReq output parameter values to the
+        Upper Tester:
+           * RSP = 0x0000 (Request accepted)
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_AVCTP_mmi_send_AVCT_SendMessage_TG(self, **kwargs):
+        """
+        Upon a call to the call back function MessageInd_CBTest_System, use the
+        Upper Tester to send an AVCT_SendMessage command to the IUT with the
+        following parameter values:
+           * BD_ADDR = BD_ADDRTest_System
+           *
+        Transaction = TRANSTest_System
+           * Type = CRTest_System = 1 (Response
+        Message)
+           * PID = PIDTest_System
+           * Data = ADDRESSdata_buffer
+        (Buffer containing DATA[]Upper_Tester)
+           * Length =
+        LengthOf(DATA[]Upper_Tester) <= MTU – 3bytes
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_AVCTP_mmi_verify_MessageInd_CB_TG(self, **kwargs):
+        """
+        Press 'OK' if the following conditions were met :
+
+        1. The
+        MessageInd_CBTest_System function in the Upper Tester is called with the
+        following parameters:
+           * BD_ADDR = BD_ADDRLower_Tester
+           *
+        Transaction = TRANSTest_System
+           * Type = 0x00 (Command message)
+           *
+        Data = ADDRESSdata_buffer (Buffer containing DATA[]Lower_Tester)
+           *
+        Length = LengthOf(DATA[]Lower_Tester)
+
+        2. the IUT returns the following
+        AVCT_SendMessage output parameters to the Upper Tester:
+           * Result =
+        0x0000 (Request accepted)
+        """
+
+        return "OK"
diff --git a/android/pandora/mmi2grpc/mmi2grpc/hfp.py b/android/pandora/mmi2grpc/mmi2grpc/hfp.py
index f6ce7c9..1cf2227 100644
--- a/android/pandora/mmi2grpc/mmi2grpc/hfp.py
+++ b/android/pandora/mmi2grpc/mmi2grpc/hfp.py
@@ -18,7 +18,7 @@
 
 from pandora_experimental.hfp_grpc import HFP
 from pandora_experimental.host_grpc import Host
-from pandora_experimental.host_pb2 import ConnectabilityMode
+from pandora_experimental.host_pb2 import ConnectabilityMode, DiscoverabilityMode
 from pandora_experimental.security_grpc import Security, SecurityStorage
 from pandora_experimental.hfp_pb2 import AudioPath
 
@@ -96,7 +96,10 @@
                 if not self.connection:
                     self.connection = self.host.Connect(address=pts_addr).connection
 
-            self.hfp.EnableSlc(connection=self.connection)
+            if "HFP/HF" in test:
+                self.hfp.EnableSlcAsHandsfree(connection=self.connection)
+            else:
+                self.hfp.EnableSlc(connection=self.connection)
 
         threading.Thread(target=enable_slc).start()
 
@@ -137,7 +140,7 @@
         return "OK"
 
     @assert_description
-    def TSC_iut_disable_slc(self, pts_addr: bytes, **kwargs):
+    def TSC_iut_disable_slc(self, test: str, pts_addr: bytes, **kwargs):
         """
         Click Ok, then disable the service level connection using the
         Implementation Under Test (IUT).
@@ -147,7 +150,10 @@
 
         def disable_slc():
             time.sleep(2)
-            self.hfp.DisableSlc(connection=self.connection)
+            if "HFP/HF" in test:
+                self.hfp.DisableSlcAsHandsfree(connection=self.connection)
+            else:
+                self.hfp.DisableSlc(connection=self.connection)
 
         threading.Thread(target=disable_slc).start()
 
@@ -225,16 +231,21 @@
         return "OK"
 
     @assert_description
-    def TSC_iut_disable_audio(self, **kwargs):
+    def TSC_iut_disable_audio(self, test: str, pts_addr: bytes, **kwargs):
         """
         Click Ok, then close the audio connection (SCO) between the
         Implementation Under Test (IUT) and the PTS.  Do not close the serivice
         level connection (SLC) or power-off the IUT.
         """
 
+        self.connection = self.host.GetConnection(address=pts_addr).connection
+
         def disable_audio():
             time.sleep(2)
-            self.hfp.SetAudioPath(audio_path=AudioPath.AUDIO_PATH_SPEAKERS)
+            if "HFP/HF" in test:
+                self.hfp.DisconnectToAudioAsHandsfree(connection=self.connection)
+            else:
+                self.hfp.SetAudioPath(audio_path=AudioPath.AUDIO_PATH_SPEAKERS)
 
         threading.Thread(target=disable_audio).start()
 
@@ -249,15 +260,20 @@
         return "OK"
 
     @assert_description
-    def TSC_iut_enable_audio(self, **kwargs):
+    def TSC_iut_enable_audio(self, test: str, pts_addr: bytes, **kwargs):
         """
         Click Ok, then initiate an audio connection (SCO) from the
         Implementation Under Test (IUT) to the PTS.
         """
 
+        self.connection = self.host.GetConnection(address=pts_addr).connection
+
         def enable_audio():
             time.sleep(2)
-            self.hfp.SetAudioPath(audio_path=AudioPath.AUDIO_PATH_HANDSFREE)
+            if "HFP/HF" in test:
+                self.hfp.ConnectToAudioAsHandsfree(connection=self.connection)
+            else:
+                self.hfp.SetAudioPath(audio_path=AudioPath.AUDIO_PATH_HANDSFREE)
 
         threading.Thread(target=enable_audio).start()
 
@@ -579,16 +595,26 @@
         return "OK"
 
     @assert_description
-    def TSC_prepare_iut_for_vra(self, pts_addr: bytes, **kwargs):
+    def TSC_prepare_iut_for_vra(self, pts_addr: bytes, test: str, **kwargs):
         """
         Place the Implementation Under Test (IUT) in a state which will allow a
         request from the PTS to activate voice recognition, then click Ok.
         """
 
-        self.hfp.SetVoiceRecognition(
-            enabled=True,
-            connection=self.host.GetConnection(address=pts_addr).connection,
-        )
+        if "HFP/HF" not in test:
+            self.hfp.SetVoiceRecognition(
+                enabled=True,
+                connection=self.host.GetConnection(address=pts_addr).connection,
+            )
+
+        return "OK"
+
+    @assert_description
+    def TSC_prepare_iut_for_vrd(self, **kwargs):
+        """
+        Place the Implementation Under Test (IUT) in a state which will allow a
+        voice recognition deactivation from PTS, then click Ok.
+        """
 
         return "OK"
 
@@ -604,20 +630,285 @@
         return "OK"
 
     @assert_description
-    def TSC_reject_call(self, **kwargs):
+    def TSC_reject_call(self, test: str, pts_addr: bytes, **kwargs):
         """
         Click Ok, then reject the incoming call using the Implemention Under
         Test (IUT).
         """
 
+        self.connection = self.host.GetConnection(address=pts_addr).connection
+
         def reject_call():
             time.sleep(2)
-            self.hfp.DeclineCall()
+            if "HFP/HF" in test:
+                self.hfp.DeclineCallAsHandsfree(connection=self.connection)
+            else:
+                self.hfp.DeclineCall()
 
         threading.Thread(target=reject_call).start()
 
         return "OK"
 
+    @assert_description
+    def TSC_hf_iut_answer_call(self, pts_addr: bytes, **kwargs):
+        """
+        Click Ok, then answer the incoming call using the Implementation Under
+        Test (IUT).
+        """
+
+        self.connection = self.host.GetConnection(address=pts_addr).connection
+
+        def answer_call():
+            time.sleep(2)
+            self.hfp.AnswerCallAsHandsfree(connection=self.connection)
+
+        threading.Thread(target=answer_call).start()
+
+        return "OK"
+
+    @assert_description
+    def TSC_iut_disable_audio_poweroff_ok(self, **kwargs):
+        """
+        Click Ok, then close the audio connection (SCO) by one of the following
+        ways:
+
+        1. Close the service level connection (SLC)
+        2. Powering off the
+        Implementation Under Test (IUT)
+        """
+
+        self.host.Reset()
+
+        return "OK"
+
+    @assert_description
+    def TSC_verify_inband_ring(self, **kwargs):
+        """
+        Verify that the in-band ringtone is audible, then click Ok.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_verify_inband_ring_muting(self, **kwargs):
+        """
+        Verify that the in-band ringtone is not audible , then click Ok.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_hf_iut_disable_call(self, pts_addr: bytes, **kwargs):
+        """
+        Click Ok, then end the call process from the Implementation Under Test
+        (IUT).
+        """
+
+        self.connection = self.host.GetConnection(address=pts_addr).connection
+
+        def disable_call():
+            time.sleep(2)
+            self.hfp.EndCallAsHandsfree(connection=self.connection)
+
+        threading.Thread(target=disable_call).start()
+
+        return "OK"
+
+    @assert_description
+    def TSC_mute_inband_ring_iut(self, **kwargs):
+        """
+        Mute the in-band ringtone on the Implementation Under Test (IUT) and
+        then click OK.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_verify_iut_alerting(self, **kwargs):
+        """
+        Verify that the Implementation Under Test (IUT) is generating a local
+        alert, then click Ok.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_verify_iut_not_alerting(self, **kwargs):
+        """
+        Verify that the Implementation Under Test (IUT) is not generating a
+        local alert.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_hf_iut_enable_call_number(self, pts_addr: bytes, **kwargs):
+        """
+        Click Ok, then place an outgoing call from the Implementation Under Test
+        (IUT) using an enterted phone number.
+        """
+
+        self.connection = self.host.GetConnection(address=pts_addr).connection
+
+        def disable_call():
+            time.sleep(2)
+            self.hfp.MakeCallAsHandsfree(connection=self.connection, number="42")
+
+        threading.Thread(target=disable_call).start()
+
+        return "OK"
+
+    @assert_description
+    def TSC_hf_iut_enable_call_memory(self, **kwargs):
+        """
+        Click Ok, then place an outgoing call from the Implementation Under Test
+        (IUT) by entering the memory index.  For further clarification please
+        see the HFP 1.5 Specification.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_hf_iut_call_swap_then_disable_held_alternative(self, pts_addr: bytes, **kwargs):
+        """
+        Using the Implementation Under Test (IUT), perform one of the following
+        two actions:
+
+        1. Click OK, make the held/waiting call active, disabling
+        the active call.
+        2. Click OK, make the held/waiting call active, placing
+        the active call on hold.
+        """
+
+        self.connection = self.host.GetConnection(address=pts_addr).connection
+
+        def call_swap_then_disable_held_alternative():
+            time.sleep(2)
+            self.hfp.CallTransferAsHandsfree(connection=self.connection)
+
+        threading.Thread(target=call_swap_then_disable_held_alternative).start()
+
+        return "OK"
+
+    @assert_description
+    def TSC_iut_make_discoverable(self, **kwargs):
+        """
+        Place the Implementation Under Test (IUT) in discoverable mode, then
+        click Ok.
+        """
+
+        self.host.SetDiscoverabilityMode(mode=DiscoverabilityMode.DISCOVERABLE_GENERAL)
+
+        return "OK"
+
+    @assert_description
+    def TSC_iut_accept_connection(self, **kwargs):
+        """
+        Click Ok, then accept the pairing and connection requests on the
+        Implementation Under Test (IUT), if prompted.
+        """
+
+        return "OK"
+
+    @assert_description
+    def TSC_voice_recognition_enable_iut(self, pts_addr: bytes, **kwargs):
+        """
+        Using the Implementation Under Test (IUT), activate voice recognition.
+        """
+
+        self.hfp.SetVoiceRecognitionAsHandsfree(
+            enabled=True,
+            connection=self.host.GetConnection(address=pts_addr).connection,
+        )
+
+        return "OK"
+
+    @assert_description
+    def TSC_voice_recognition_disable_iut(self, pts_addr: bytes, **kwargs):
+        """
+        Using the Implementation Under Test (IUT), deactivate voice recognition.
+        """
+
+        self.hfp.SetVoiceRecognitionAsHandsfree(
+            enabled=False,
+            connection=self.host.GetConnection(address=pts_addr).connection,
+        )
+
+        return "OK"
+
+    @match_description
+    def TSC_dtmf_send(self, pts_addr: bytes, dtmf: str, **kwargs):
+        r"""
+        Send the DTMF code, then click Ok. (?P<dtmf>.*)
+        """
+
+        self.hfp.SendDtmfFromHandsfree(
+            connection=self.host.GetConnection(address=pts_addr).connection,
+            code=dtmf[0].encode("ascii")[0],
+        )
+
+        return "OK"
+
+    @assert_description
+    def TSC_verify_hf_iut_reports_held_and_active_call(self, **kwargs):
+        """
+        Verify that the Implementation Under Test (IUT) interprets both held and
+        active call signals, then click Ok.  If applicable, verify that the
+        information is correctly displayed on the IUT, then click Ok.
+        """
+
+        return "OK"
+
+    def TSC_rf_shield_iut_or_pts(self, **kwargs):
+        """
+        Click Ok, then move the PTS and the Implementation Under Test (IUT) out
+        of range of each other by performing one of the following IUT specific
+        actions:
+
+        1. Hands Free (HF) IUT - Place the IUT in the RF shield box or
+        physically take out of range from the PTS.
+
+        2. Audio Gateway (AG) IUT-
+        Physically take the IUT out range.  Do not place in the RF shield box as
+        it will interfere with the cellular network.
+
+        Note: The PTS can also be
+        placed in the RF shield box if necessary.
+        """
+
+        def shield_iut_or_pts():
+            time.sleep(2)
+            self.rootcanal.disconnect_phy()
+
+        threading.Thread(target=shield_iut_or_pts).start()
+
+        return "OK"
+
+    @assert_description
+    def TSC_rf_shield_open(self, **kwargs):
+        """
+        Click Ok, then remove the Implementation Under Test (IUT) and/or the PTS
+        from the RF shield.  If the out of range method was used, bring the IUT
+        and PTS back within range.
+        """
+
+        def shield_open():
+            time.sleep(2)
+            self.rootcanal.reconnect_phy_if_needed()
+
+        threading.Thread(target=shield_open).start()
+
+        return "OK"
+
+    @match_description
+    def TSC_verify_speaker_volume(self, volume: str, **kwargs):
+        r"""
+        Verify that the Hands Free \(HF\) speaker volume is displayed correctly on
+        the Implementation Under Test \(IUT\).(?P<volume>[0-9]*)
+        """
+
+        return "OK"
+
     def _auto_confirm_requests(self, times=None):
 
         def task():
diff --git a/android/pandora/mmi2grpc/mmi2grpc/l2cap.py b/android/pandora/mmi2grpc/mmi2grpc/l2cap.py
index 561c600..b6db482 100644
--- a/android/pandora/mmi2grpc/mmi2grpc/l2cap.py
+++ b/android/pandora/mmi2grpc/mmi2grpc/l2cap.py
@@ -16,6 +16,7 @@
 class L2CAPProxy(ProfileProxy):
     test_status_map = {}  # record test status and pass them between MMI
     LE_DATA_PACKET_LARGE = "data: LE_DATA_PACKET_LARGE"
+    LE_DATA_PACKET1 = "data: LE_PACKET1"
     connection: Optional[Connection] = None
 
     def __init__(self, channel):
@@ -101,6 +102,10 @@
         )
         # not strictly necessary, but can save time on waiting connection
         tests_to_open_bluetooth_server_socket = [
+            "L2CAP/COS/CFC/BV-01-C",
+            "L2CAP/COS/CFC/BV-02-C",
+            "L2CAP/COS/CFC/BV-03-C",
+            "L2CAP/COS/CFC/BV-04-C",
             "L2CAP/LE/CFC/BV-03-C",
             "L2CAP/LE/CFC/BV-05-C",
             "L2CAP/LE/CFC/BV-06-C",
@@ -138,7 +143,7 @@
         return "OK"
 
     @match_description
-    def MMI_UPPER_TESTER_CONFIRM_LE_DATA(self, sent_data: str, **kwargs):
+    def MMI_UPPER_TESTER_CONFIRM_LE_DATA(self, sent_data: str, test: str, **kwargs):
         """
         Did the Upper Tester send the data (?P<sent_data>[0-9A-F]*) to to the
         PTS\? Click Yes if it matched, otherwise click No.
@@ -146,10 +151,13 @@
         Description: The Implementation Under Test
         \(IUT\) send data is receive correctly in the PTS.
         """
-        hex_LE_DATA_PACKET_LARGE = self.LE_DATA_PACKET_LARGE.encode("utf-8").hex().upper()
-        if sent_data != hex_LE_DATA_PACKET_LARGE:
-            print(f"data not match, sent_data:{sent_data} and {hex_LE_DATA_PACKET_LARGE}", file=sys.stderr)
-            raise Exception(f"data not match, sent_data:{sent_data} and {hex_LE_DATA_PACKET_LARGE}")
+        if test == 'L2CAP/COS/CFC/BV-02-C':
+            hex_LE_DATA_PACKET = self.LE_DATA_PACKET1.encode("utf-8").hex().upper()
+        else:
+            hex_LE_DATA_PACKET = self.LE_DATA_PACKET_LARGE.encode("utf-8").hex().upper()
+        if sent_data != hex_LE_DATA_PACKET:
+            print(f"data not match, sent_data:{sent_data} and {hex_LE_DATA_PACKET}", file=sys.stderr)
+            raise Exception(f"data not match, sent_data:{sent_data} and {hex_LE_DATA_PACKET}")
         return "OK"
 
     @assert_description
@@ -424,3 +432,23 @@
         """
 
         return "OK"
+
+    @assert_description
+    def MMI_UPPER_TESTER_SEND_LE_DATA_PACKET1(self, **kwargs):
+        """
+        Upper Tester command IUT to send a non-segmented LE data packet to the
+        PTS with any values.
+         Description : The Implementation Under Test(IUT)
+        should send none segmantation LE frame of LE data to the PTS.
+        """
+        self.l2cap.SendData(connection=self.connection, data=bytes(self.LE_DATA_PACKET1, "utf-8"))
+        return "OK"
+
+    @assert_description
+    def MMI_IUT_SEND_L2CAP_DATA(self, **kwargs):
+        """
+        Using the Implementation Under Test(IUT), send L2CAP_Data over the
+        assigned channel with correct DCID to the PTS.
+        """
+
+        return "OK"
diff --git a/android/pandora/server/configs/PtsBotTest.xml b/android/pandora/server/configs/PtsBotTest.xml
index feb8524..e748a2e 100644
--- a/android/pandora/server/configs/PtsBotTest.xml
+++ b/android/pandora/server/configs/PtsBotTest.xml
@@ -38,9 +38,11 @@
         <option name="profile" value="GAP" />
         <option name="profile" value="GATT" />
         <option name="profile" value="HFP/AG" />
+        <option name="profile" value="HFP/HF" />
         <option name="profile" value="HID/HOS" />
         <option name="profile" value="HOGP" />
         <option name="profile" value="L2CAP/COS" />
+        <option name="profile" value="L2CAP/EXF" />
         <option name="profile" value="L2CAP/LE" />
         <option name="profile" value="MAP" />
         <option name="profile" value="OPP" />
diff --git a/android/pandora/server/configs/PtsBotTestMts.xml b/android/pandora/server/configs/PtsBotTestMts.xml
index 5df935c..127cb4f 100644
--- a/android/pandora/server/configs/PtsBotTestMts.xml
+++ b/android/pandora/server/configs/PtsBotTestMts.xml
@@ -38,9 +38,11 @@
         <option name="profile" value="GAP" />
         <option name="profile" value="GATT" />
         <option name="profile" value="HFP/AG" />
+        <option name="profile" value="HFP/HF" />
         <option name="profile" value="HID/HOS" />
         <option name="profile" value="HOGP" />
         <option name="profile" value="L2CAP/COS" />
+        <option name="profile" value="L2CAP/EXF" />
         <option name="profile" value="L2CAP/LE" />
         <option name="profile" value="MAP" />
         <option name="profile" value="OPP" />
diff --git a/android/pandora/server/configs/pts_bot_tests_config.json b/android/pandora/server/configs/pts_bot_tests_config.json
index 2421238..19b572f 100644
--- a/android/pandora/server/configs/pts_bot_tests_config.json
+++ b/android/pandora/server/configs/pts_bot_tests_config.json
@@ -25,10 +25,13 @@
     "A2DP/SRC/SUS/BV-01-I",
     "AVCTP/CT/CCM/BV-03-C",
     "AVCTP/CT/CCM/BV-04-C",
+    "AVCTP/TG/CCM/BV-01-C",
+    "AVCTP/TG/CCM/BV-02-C",
     "AVCTP/TG/CCM/BV-03-C",
     "AVCTP/TG/CCM/BV-04-C",
     "AVCTP/TG/FRA/BV-03-C",
     "AVCTP/TG/NFR/BI-01-C",
+    "AVCTP/TG/NFR/BV-02-C",
     "AVCTP/TG/NFR/BV-03-C",
     "AVDTP/SNK/ACP/SIG/SMG/BI-05-C",
     "AVDTP/SNK/ACP/SIG/SMG/BI-08-C",
@@ -316,15 +319,26 @@
     "HOGP/RH/HGRF/BV-10-I",
     "HOGP/RH/HGRF/BV-12-I",
     "L2CAP/COS/CED/BI-01-C",
+    "L2CAP/COS/CED/BV-03-C",
     "L2CAP/COS/CED/BV-05-C",
     "L2CAP/COS/CED/BV-07-C",
     "L2CAP/COS/CED/BV-08-C",
     "L2CAP/COS/CED/BV-11-C",
+    "L2CAP/COS/CFC/BV-01-C",
+    "L2CAP/COS/CFC/BV-02-C",
+    "L2CAP/COS/CFC/BV-03-C",
+    "L2CAP/COS/CFC/BV-04-C",
     "L2CAP/COS/CFD/BV-02-C",
     "L2CAP/COS/CFD/BV-03-C",
     "L2CAP/COS/CFD/BV-11-C",
     "L2CAP/COS/CFD/BV-12-C",
     "L2CAP/COS/CFD/BV-14-C",
+    "L2CAP/COS/ECH/BV-01-C",
+    "L2CAP/COS/IEX/BV-02-C",
+    "L2CAP/EXF/BV-01-C",
+    "L2CAP/EXF/BV-02-C",
+    "L2CAP/EXF/BV-03-C",
+    "L2CAP/EXF/BV-05-C",
     "L2CAP/LE/CFC/BI-01-C",
     "L2CAP/LE/CFC/BV-01-C",
     "L2CAP/LE/CFC/BV-02-C",
@@ -540,10 +554,7 @@
     "AVCTP/CT/FRA/BV-04-C",
     "AVCTP/CT/NFR/BV-01-C",
     "AVCTP/CT/NFR/BV-04-C",
-    "AVCTP/TG/CCM/BV-01-C",
-    "AVCTP/TG/CCM/BV-02-C",
     "AVCTP/TG/FRA/BV-02-C",
-    "AVCTP/TG/NFR/BV-02-C",
     "AVDTP/SNK/ACP/SIG/SMG/BI-11-C",
     "AVDTP/SNK/ACP/SIG/SMG/BI-23-C",
     "AVDTP/SNK/ACP/SIG/SMG/BV-14-C",
@@ -737,10 +748,24 @@
     "HFP/AG/HFI/BI-03-I",
     "HFP/AG/OCN/BV-01-I",
     "HFP/AG/SLC/BV-04-C",
+    "HFP/HF/OCM/BV-01-I",
+    "HFP/HF/OCM/BV-02-I",
+    "HFP/HF/OCL/BV-01-I",
+    "HFP/HF/OCL/BV-02-I",
+    "HFP/HF/TWC/BV-02-I",
+    "HFP/HF/TWC/BV-03-I",
+    "HFP/HF/ENO/BV-01-I",
+    "HFP/HF/VRD/BV-01-I",
+    "HFP/HF/NUM/BV-01-I",
+    "HFP/HF/NUM/BI-01-I",
+    "HFP/HF/ACC/BV-01-I",
+    "HFP/HF/ACC/BV-02-I",
+    "HFP/HF/ECC/BV-01-I",
+    "HFP/HF/ECC/BV-02-I",
+    "HFP/HF/ECS/BV-01-I",
     "HID/HOS/HCR/BV-01-I",
     "L2CAP/COS/CED/BI-02-C",
     "L2CAP/COS/CED/BV-01-C",
-    "L2CAP/COS/CED/BV-03-C",
     "L2CAP/COS/CED/BV-04-C",
     "L2CAP/COS/CED/BV-09-C",
     "L2CAP/COS/CED/BV-10-C",
@@ -751,14 +776,9 @@
     "L2CAP/COS/CFD/BV-08-C",
     "L2CAP/COS/CFD/BV-10-C",
     "L2CAP/COS/CFD/BV-13-C",
-    "L2CAP/COS/CFC/BV-01-C",
-    "L2CAP/COS/CFC/BV-02-C",
-    "L2CAP/COS/CFC/BV-03-C",
-    "L2CAP/COS/CFC/BV-04-C",
     "L2CAP/COS/CFC/BV-05-C",
     "L2CAP/COS/ECH/BV-02-C",
     "L2CAP/COS/IEX/BV-01-C",
-    "L2CAP/COS/IEX/BV-02-C",
     "L2CAP/LE/CFC/BV-07-C",
     "L2CAP/LE/CFC/BV-11-C",
     "L2CAP/LE/CFC/BV-13-C",
@@ -1411,8 +1431,11 @@
     "TSPC_HFP_3_14": true,
     "TSPC_HFP_3_15": true,
     "TSPC_HFP_3_17": true,
+    "TSPC_HFP_3_18a": true,
+    "TSPC_HFP_3_18c": true,
     "TSPC_HFP_3_20": true,
     "TSPC_HFP_3_21a": true,
+    "TSPC_HFP_3_21b": true,
     "TSPC_HFP_3_23": true,
     "TSPC_HFP_3_24": true,
     "TSPC_HFP_3_26": true,
diff --git a/android/pandora/server/src/com/android/pandora/HfpHandsfree.kt b/android/pandora/server/src/com/android/pandora/HfpHandsfree.kt
new file mode 100644
index 0000000..f2af500
--- /dev/null
+++ b/android/pandora/server/src/com/android/pandora/HfpHandsfree.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.pandora
+
+import android.annotation.SuppressLint
+import android.bluetooth.BluetoothDevice
+import android.bluetooth.BluetoothHeadset
+import android.bluetooth.BluetoothHeadsetClient
+import android.bluetooth.BluetoothManager
+import android.bluetooth.BluetoothProfile
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.net.Uri
+import android.os.Bundle
+import android.os.IBinder
+import android.provider.CallLog
+import android.telecom.Call
+import android.telecom.CallAudioState
+import android.telecom.InCallService
+import android.telecom.TelecomManager
+import android.telecom.VideoProfile
+import com.google.protobuf.Empty
+import io.grpc.stub.StreamObserver
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.shareIn
+import pandora.HFPGrpc.HFPImplBase
+import pandora.HfpProto.*
+
+private const val TAG = "PandoraHfpHandsfree"
+
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class HfpHandsfree(val context: Context) : HFPImplBase() {
+  private val scope: CoroutineScope = CoroutineScope(Dispatchers.Default)
+  private val flow: Flow<Intent>
+
+  private val bluetoothManager = context.getSystemService(BluetoothManager::class.java)!!
+  private val telecomManager = context.getSystemService(TelecomManager::class.java)!!
+  private val bluetoothAdapter = bluetoothManager.adapter
+
+  private val bluetoothHfpClient = getProfileProxy<BluetoothHeadsetClient>(context, BluetoothProfile.HEADSET_CLIENT)
+
+  companion object {
+    @SuppressLint("StaticFieldLeak") private lateinit var inCallService: InCallService
+  }
+
+  init {
+    val intentFilter = IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
+    flow = intentFlow(context, intentFilter).shareIn(scope, SharingStarted.Eagerly)
+  }
+
+  fun deinit() {
+    bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET_CLIENT, bluetoothHfpClient)
+    scope.cancel()
+  }
+
+  override fun answerCallAsHandsfree(
+    request: AnswerCallAsHandsfreeRequest,
+    responseObserver: StreamObserver<AnswerCallAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.acceptCall(request.connection.toBluetoothDevice(bluetoothAdapter), BluetoothHeadsetClient.CALL_ACCEPT_NONE)
+      AnswerCallAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun endCallAsHandsfree(
+    request: EndCallAsHandsfreeRequest,
+    responseObserver: StreamObserver<EndCallAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      for (call in bluetoothHfpClient.getCurrentCalls(request.connection.toBluetoothDevice(bluetoothAdapter))) {
+        bluetoothHfpClient.terminateCall(request.connection.toBluetoothDevice(bluetoothAdapter), call)
+      }
+      EndCallAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun declineCallAsHandsfree(
+    request: DeclineCallAsHandsfreeRequest,
+    responseObserver: StreamObserver<DeclineCallAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.rejectCall(request.connection.toBluetoothDevice(bluetoothAdapter))
+      DeclineCallAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun connectToAudioAsHandsfree(
+    request: ConnectToAudioAsHandsfreeRequest,
+    responseObserver: StreamObserver<ConnectToAudioAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.connectAudio(request.connection.toBluetoothDevice(bluetoothAdapter))
+      ConnectToAudioAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun disconnectFromAudioAsHandsfree(
+    request: DisconnectFromAudioAsHandsfreeRequest,
+    responseObserver: StreamObserver<DisconnectFromAudioAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.disconnectAudio(request.connection.toBluetoothDevice(bluetoothAdapter))
+      DisconnectFromAudioAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun makeCallAsHandsfree(
+    request: MakeCallAsHandsfreeRequest,
+    responseObserver: StreamObserver<MakeCallAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.dial(request.connection.toBluetoothDevice(bluetoothAdapter), request.number)
+      MakeCallAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun callTransferAsHandsfree(
+    request: CallTransferAsHandsfreeRequest,
+    responseObserver: StreamObserver<CallTransferAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.explicitCallTransfer(request.connection.toBluetoothDevice(bluetoothAdapter))
+      CallTransferAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun enableSlcAsHandsfree(
+    request: EnableSlcAsHandsfreeRequest,
+    responseObserver: StreamObserver<Empty>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.setConnectionPolicy(request.connection.toBluetoothDevice(bluetoothAdapter), BluetoothProfile.CONNECTION_POLICY_ALLOWED)
+      Empty.getDefaultInstance()
+    }
+  }
+
+  override fun disableSlcAsHandsfree(
+    request: DisableSlcAsHandsfreeRequest,
+    responseObserver: StreamObserver<Empty>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.setConnectionPolicy(request.connection.toBluetoothDevice(bluetoothAdapter), BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)
+      Empty.getDefaultInstance()
+    }
+  }
+
+  override fun setVoiceRecognitionAsHandsfree(
+    request: SetVoiceRecognitionAsHandsfreeRequest,
+    responseObserver: StreamObserver<SetVoiceRecognitionAsHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      if (request.enabled) {
+        bluetoothHfpClient.startVoiceRecognition(request.connection.toBluetoothDevice(bluetoothAdapter))
+      } else {
+        bluetoothHfpClient.stopVoiceRecognition(request.connection.toBluetoothDevice(bluetoothAdapter))
+      }
+      SetVoiceRecognitionAsHandsfreeResponse.getDefaultInstance()
+    }
+  }
+
+  override fun sendDtmfFromHandsfree(
+    request: SendDtmfFromHandsfreeRequest,
+    responseObserver: StreamObserver<SendDtmfFromHandsfreeResponse>
+  ) {
+    grpcUnary(scope, responseObserver) {
+      bluetoothHfpClient.sendDTMF(request.connection.toBluetoothDevice(bluetoothAdapter), request.code.toByte())
+      SendDtmfFromHandsfreeResponse.getDefaultInstance()
+    }
+  }
+}
diff --git a/android/pandora/server/src/com/android/pandora/Server.kt b/android/pandora/server/src/com/android/pandora/Server.kt
index c4f95ca..7b17dd4 100644
--- a/android/pandora/server/src/com/android/pandora/Server.kt
+++ b/android/pandora/server/src/com/android/pandora/Server.kt
@@ -34,7 +34,8 @@
   private var a2dpSink: A2dpSink? = null
   private var avrcp: Avrcp
   private var gatt: Gatt
-  private var hfp: Hfp
+  private var hfp: Hfp? = null
+  private var hfpHandsfree: HfpHandsfree? = null
   private var hid: Hid
   private var l2cap: L2cap
   private var mediaplayer: MediaPlayer
@@ -50,7 +51,6 @@
     host = Host(context, security, this)
     avrcp = Avrcp(context)
     gatt = Gatt(context)
-    hfp = Hfp(context)
     hid = Hid(context)
     l2cap = L2cap(context)
     mediaplayer = MediaPlayer(context)
@@ -64,7 +64,6 @@
         .addService(host)
         .addService(avrcp)
         .addService(gatt)
-        .addService(hfp)
         .addService(hid)
         .addService(l2cap)
         .addService(mediaplayer)
@@ -84,6 +83,15 @@
       grpcServerBuilder.addService(a2dpSink!!)
     }
 
+    val is_hfp_hf = bluetoothAdapter.getSupportedProfiles().contains(BluetoothProfile.HEADSET_CLIENT)
+    if (is_hfp_hf) {
+      hfpHandsfree = HfpHandsfree(context)
+      grpcServerBuilder.addService(hfpHandsfree!!)
+    } else {
+      hfp = Hfp(context)
+      grpcServerBuilder.addService(hfp!!)
+    }
+
     grpcServer = grpcServerBuilder.build()
 
     Log.d(TAG, "Starting Pandora Server")
@@ -101,7 +109,8 @@
     a2dpSink?.deinit()
     avrcp.deinit()
     gatt.deinit()
-    hfp.deinit()
+    hfp?.deinit()
+    hfpHandsfree?.deinit()
     hid.deinit()
     l2cap.deinit()
     mediaplayer.deinit()
diff --git a/framework/tests/unit/Android.bp b/framework/tests/unit/Android.bp
index 23d9db2..a976d96 100644
--- a/framework/tests/unit/Android.bp
+++ b/framework/tests/unit/Android.bp
@@ -30,5 +30,4 @@
         "general-tests",
         "mts-bluetooth",
     ],
-    certificate: ":com.android.bluetooth.certificate",
 }
diff --git a/framework/tests/unit/AndroidTest.xml b/framework/tests/unit/AndroidTest.xml
index f6dfc2d..4c04e11 100644
--- a/framework/tests/unit/AndroidTest.xml
+++ b/framework/tests/unit/AndroidTest.xml
@@ -19,6 +19,10 @@
         <option name="test-file-name" value="FrameworkBluetoothTests.apk" />
     </target_preparer>
 
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+        <option name="force-root" value="true" />
+    </target_preparer>
+
     <option name="test-suite-tag" value="apct"/>
     <option name="test-tag" value="FrameworkBluetoothTests"/>
 
diff --git a/pandora/interfaces/pandora_experimental/hfp.proto b/pandora/interfaces/pandora_experimental/hfp.proto
index 496e665..2406165 100644
--- a/pandora/interfaces/pandora_experimental/hfp.proto
+++ b/pandora/interfaces/pandora_experimental/hfp.proto
@@ -31,6 +31,28 @@
   rpc SetVoiceRecognition(SetVoiceRecognitionRequest) returns (SetVoiceRecognitionResponse);
   // Clear the call history
   rpc ClearCallHistory(ClearCallHistoryRequest) returns (ClearCallHistoryResponse);
+  // Answer an incoming call from a peer device (as a handsfree)
+  rpc AnswerCallAsHandsfree(AnswerCallAsHandsfreeRequest) returns (AnswerCallAsHandsfreeResponse);
+  // End a call from a peer device (as a handsfree)
+  rpc EndCallAsHandsfree(EndCallAsHandsfreeRequest) returns (EndCallAsHandsfreeResponse);
+  // Decline an incoming call from a peer device (as a handsfree)
+  rpc DeclineCallAsHandsfree(DeclineCallAsHandsfreeRequest) returns (DeclineCallAsHandsfreeResponse);
+  // Connect to an incoming audio stream from a peer device (as a handsfree)
+  rpc ConnectToAudioAsHandsfree(ConnectToAudioAsHandsfreeRequest) returns (ConnectToAudioAsHandsfreeResponse);
+  // Disonnect from an incoming audio stream from a peer device (as a handsfree)
+  rpc DisconnectFromAudioAsHandsfree(DisconnectFromAudioAsHandsfreeRequest) returns (DisconnectFromAudioAsHandsfreeResponse);
+  // Make a call to a given phone number (as a handsfree)
+  rpc MakeCallAsHandsfree(MakeCallAsHandsfreeRequest) returns (MakeCallAsHandsfreeResponse);
+  // Connect a call on hold, and disconnect the current call (as a handsfree)
+  rpc CallTransferAsHandsfree(CallTransferAsHandsfreeRequest) returns (CallTransferAsHandsfreeResponse);
+  // Enable Service level connection (as a handsfree)
+  rpc EnableSlcAsHandsfree(EnableSlcAsHandsfreeRequest) returns (google.protobuf.Empty);
+  // Disable Service level connection (as a handsfree)
+  rpc DisableSlcAsHandsfree(DisableSlcAsHandsfreeRequest) returns (google.protobuf.Empty);
+  // Set voice recognition (as a handsfree)
+  rpc SetVoiceRecognitionAsHandsfree(SetVoiceRecognitionAsHandsfreeRequest) returns (SetVoiceRecognitionAsHandsfreeResponse);
+  // Send DTMF code from the handsfree
+  rpc SendDtmfFromHandsfree(SendDtmfFromHandsfreeRequest) returns (SendDtmfFromHandsfreeResponse);
 }
 
 // Request of the `EnableSlc` method.
@@ -99,3 +121,68 @@
 message ClearCallHistoryRequest {}
 
 message ClearCallHistoryResponse {}
+
+message AnswerCallAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message AnswerCallAsHandsfreeResponse {}
+
+message EndCallAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message EndCallAsHandsfreeResponse {}
+
+message DeclineCallAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message DeclineCallAsHandsfreeResponse {}
+
+message ConnectToAudioAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message ConnectToAudioAsHandsfreeResponse {}
+
+message DisconnectFromAudioAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message DisconnectFromAudioAsHandsfreeResponse {}
+
+message MakeCallAsHandsfreeRequest {
+  Connection connection = 1;
+  string number = 2;
+}
+
+message MakeCallAsHandsfreeResponse {}
+
+message CallTransferAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message CallTransferAsHandsfreeResponse {}
+
+message EnableSlcAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message DisableSlcAsHandsfreeRequest {
+  Connection connection = 1;
+}
+
+message SetVoiceRecognitionAsHandsfreeRequest {
+  Connection connection = 1;
+  bool enabled = 2;
+}
+
+message SetVoiceRecognitionAsHandsfreeResponse {}
+
+message SendDtmfFromHandsfreeRequest {
+  Connection connection = 1;
+  uint32 code = 2;
+}
+
+message SendDtmfFromHandsfreeResponse {}
diff --git a/system/bta/csis/csis_client.cc b/system/bta/csis/csis_client.cc
index 8bc1190..ac1ce0d 100644
--- a/system/bta/csis/csis_client.cc
+++ b/system/bta/csis/csis_client.cc
@@ -984,7 +984,11 @@
       return;
     }
 
-    csis_group->SetDesiredSize(value[0]);
+    auto new_size = value[0];
+    csis_group->SetDesiredSize(new_size);
+    if (new_size > csis_group->GetCurrentSize()) {
+      CsisActiveDiscovery(csis_group);
+    }
   }
 
   void OnCsisLockReadRsp(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
@@ -1127,14 +1131,14 @@
   std::vector<RawAddress> GetAllRsiFromAdvertising(
       const tBTA_DM_INQ_RES* result) {
     const uint8_t* p_service_data = result->p_eir;
-    uint16_t remaining_data_len = result->eir_len;
     std::vector<RawAddress> devices;
     uint8_t service_data_len = 0;
 
     while ((p_service_data = AdvertiseDataParser::GetFieldByType(
                 p_service_data + service_data_len,
-                (remaining_data_len -= service_data_len), BTM_BLE_AD_TYPE_RSI,
-                &service_data_len))) {
+                result->eir_len - (p_service_data - result->p_eir) -
+                    service_data_len,
+                BTM_BLE_AD_TYPE_RSI, &service_data_len))) {
       RawAddress bda;
       STREAM_TO_BDADDR(bda, p_service_data);
       devices.push_back(std::move(bda));
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index 1896cc9..3cbb536 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -1861,6 +1861,8 @@
   result.inq_res.p_eir = const_cast<uint8_t*>(p_eir);
   result.inq_res.eir_len = eir_len;
 
+  result.inq_res.ble_evt_type = p_inq->ble_evt_type;
+
   p_inq_info = BTM_InqDbRead(p_inq->remote_bd_addr);
   if (p_inq_info != NULL) {
     /* initialize remt_name_not_required to false so that we get the name by
@@ -2545,9 +2547,6 @@
       memset(&bta_dm_cb.device_list.peer_device[clear_index], 0,
              sizeof(bta_dm_cb.device_list.peer_device[clear_index]));
     }
-
-    device->conn_state = BTA_DM_NOT_CONNECTED;
-
     break;
   }
   if (bta_dm_cb.device_list.count) bta_dm_cb.device_list.count--;
diff --git a/system/bta/dm/bta_dm_api.cc b/system/bta/dm/bta_dm_api.cc
index eded208..0703594 100644
--- a/system/bta/dm/bta_dm_api.cc
+++ b/system/bta/dm/bta_dm_api.cc
@@ -689,3 +689,14 @@
   APPL_TRACE_API("BTA_DmBleResetId");
   do_in_main_thread(FROM_HERE, base::Bind(bta_dm_ble_reset_id));
 }
+
+bool BTA_DmCheckLeAudioCapable(const RawAddress& address) {
+  for (tBTM_INQ_INFO* inq_ent = BTM_InqDbFirst(); inq_ent != nullptr;
+       inq_ent = BTM_InqDbNext(inq_ent)) {
+    if (inq_ent->results.remote_bd_addr != address) continue;
+
+    LOG_INFO("Device is LE Audio capable based on AD content");
+    return inq_ent->results.ble_ad_is_le_audio_capable;
+  }
+  return false;
+}
\ No newline at end of file
diff --git a/system/bta/dm/bta_dm_cfg.cc b/system/bta/dm/bta_dm_cfg.cc
index 4a05f8e..6da7c43 100644
--- a/system/bta/dm/bta_dm_cfg.cc
+++ b/system/bta/dm/bta_dm_cfg.cc
@@ -26,12 +26,12 @@
 #include <cstdint>
 
 #include "bt_target.h"  // Must be first to define build configuration
-
 #include "bta/dm/bta_dm_int.h"
 #include "bta/include/bta_api.h"
 #include "bta/include/bta_hh_api.h"
 #include "bta/include/bta_jv_api.h"
 #include "bta/sys/bta_sys.h"
+#include "osi/include/properties.h"
 #include "types/raw_address.h"
 
 /* page timeout in 625uS */
@@ -44,14 +44,6 @@
 #define BTA_DM_AVOID_SCATTER_A2DP TRUE
 #endif
 
-/* For Insight, PM cfg lookup tables are runtime configurable (to allow tweaking
- * of params for power consumption measurements) */
-#ifndef BTE_SIM_APP
-#define tBTA_DM_PM_TYPE_QUALIFIER const
-#else
-#define tBTA_DM_PM_TYPE_QUALIFIER
-#endif
-
 const tBTA_DM_CFG bta_dm_cfg = {
     /* page timeout in 625uS */
     BTA_DM_PAGE_TIMEOUT,
@@ -134,317 +126,257 @@
         {BTA_ID_GATTS, BTA_ALL_APP_ID, 15}  /* gatts spec table */
 };
 
-tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = {
-    /* AG : 0 */
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff  */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_SNIFF_SCO_OPEN_IDX, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco open, active */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close sniff  */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}},                        /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_RETRY, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC* get_bta_dm_pm_spec() {
+  static uint16_t hs_sniff_delay = uint16_t(
+      osi_property_get_int32("bluetooth.bta_hs_sniff_delay_ms.config", 7000));
 
-    /* CT, CG : 1 */
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_PARK, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  park */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco open sniff */
-         {{BTA_DM_PM_PARK, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close  park */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_RETRY, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+  static tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC
+      bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = {
+          /* AG : 0 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR2),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 7000},     /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},               /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},             /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},             /* app close */
+               {BTA_DM_PM_SNIFF_SCO_OPEN_IDX, 7000}, /* sco open, active */
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 7000},     /* sco close sniff */
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 7000},     /* idle */
+               {BTA_DM_PM_ACTIVE, 0},                /* busy */
+               {BTA_DM_PM_RETRY, 7000}               /* mode change retry */
+           }},
 
-    /* DG, PBC : 2 */
-    {(BTA_DM_PM_ACTIVE), /* no power saving mode allowed */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open active */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF, 1000}, {BTA_DM_PM_NO_ACTION, 0}},  /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* CT, CG : 1 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR2),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_PARK, 5000},           /* conn open park */
+               {BTA_DM_PM_NO_PREF, 0},           /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app close */
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 5000}, /* sco open sniff */
+               {BTA_DM_PM_PARK, 5000},           /* sco close park */
+               {BTA_DM_PM_NO_ACTION, 0},         /* idle */
+               {BTA_DM_PM_NO_ACTION, 0},         /* busy */
+               {BTA_DM_PM_RETRY, 5000}           /* mode change retry */
+           }},
 
-    /* HD : 3 */
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR3), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */
-         {{BTA_DM_PM_SNIFF_HD_IDLE_IDX, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* DG, PBC : 2 */
+          {(BTA_DM_PM_ACTIVE), /* no power saving mode allowed */
+           (BTA_DM_PM_SSR2),   /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF, 5000},  /* conn open active */
+               {BTA_DM_PM_NO_PREF, 0},   /* conn close */
+               {BTA_DM_PM_ACTIVE, 0},    /* app open */
+               {BTA_DM_PM_NO_ACTION, 0}, /* app close */
+               {BTA_DM_PM_NO_ACTION, 0}, /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0}, /* sco close */
+               {BTA_DM_PM_SNIFF, 1000},  /* idle */
+               {BTA_DM_PM_ACTIVE, 0},    /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}  /* mode change retry */
+           }},
 
-    /* AV : 4 */
-    {(BTA_DM_PM_SNIFF), /* allow sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  sniff */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}},                        /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* HD : 3 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR3),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 5000}, /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},                /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},              /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},              /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},              /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0},              /* sco close */
+               {BTA_DM_PM_SNIFF_HD_IDLE_IDX, 5000},   /* idle */
+               {BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 0},    /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}               /* mode change retry */
+           }},
 
-    /* HH for joysticks and gamepad : 5 */
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR1), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF6, BTA_DM_PM_HH_OPEN_DELAY},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  sniff */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close, used for HH suspend   */
-         {{BTA_DM_PM_SNIFF6, BTA_DM_PM_HH_IDLE_DELAY},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_SNIFF6, BTA_DM_PM_HH_ACTIVE_DELAY},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* AV : 4 */
+          {(BTA_DM_PM_SNIFF), /* allow sniff */
+           (BTA_DM_PM_SSR2),  /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 7000}, /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},           /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco close */
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 7000}, /* idle */
+               {BTA_DM_PM_ACTIVE, 0},            /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}          /* mode change retry */
+           }},
 
-    /* HH : 6 */
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR1), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF_HH_OPEN_IDX, BTA_DM_PM_HH_OPEN_DELAY},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  sniff */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close, used for HH suspend   */
-         {{BTA_DM_PM_SNIFF_HH_IDLE_IDX, BTA_DM_PM_HH_IDLE_DELAY},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_SNIFF_HH_ACTIVE_IDX, BTA_DM_PM_HH_ACTIVE_DELAY},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* HH for joysticks and gamepad : 5 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR1),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF6,
+                BTA_DM_PM_HH_OPEN_DELAY}, /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},    /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},  /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},  /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},  /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0},  /* sco close, used for HH suspend */
+               {BTA_DM_PM_SNIFF6, BTA_DM_PM_HH_IDLE_DELAY},   /* idle */
+               {BTA_DM_PM_SNIFF6, BTA_DM_PM_HH_ACTIVE_DELAY}, /* busy */
+               {BTA_DM_PM_NO_ACTION, 0} /* mode change retry */
+           }},
 
-    /* FTC, OPC, JV : 7 */
-    {(BTA_DM_PM_SNIFF), /* allow sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_ACTIVE, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  active */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, BTA_FTC_IDLE_TO_SNIFF_DELAY_MS},
-          {BTA_DM_PM_NO_ACTION, 0}},                        /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* HH : 6 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR1),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF_HH_OPEN_IDX,
+                BTA_DM_PM_HH_OPEN_DELAY}, /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},    /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},  /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},  /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},  /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0},  /* sco close, used for HH suspend */
+               {BTA_DM_PM_SNIFF_HH_IDLE_IDX,
+                BTA_DM_PM_HH_IDLE_DELAY}, /* idle */
+               {BTA_DM_PM_SNIFF_HH_ACTIVE_IDX,
+                BTA_DM_PM_HH_ACTIVE_DELAY}, /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}     /* mode change retry */
+           }},
 
-    /* FTS, PBS, OPS, MSE, BTA_JV_PM_ID_1 : 8 */
-    {(BTA_DM_PM_SNIFF), /* allow sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_ACTIVE, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  active */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, BTA_FTS_OPS_IDLE_TO_SNIFF_DELAY_MS},
-          {BTA_DM_PM_NO_ACTION, 0}},                        /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* FTC, OPC, JV : 7 */
+          {(BTA_DM_PM_SNIFF), /* allow sniff */
+           (BTA_DM_PM_SSR2),  /* the SSR entry */
+           {
+               {BTA_DM_PM_ACTIVE, 0},    /* conn open active */
+               {BTA_DM_PM_NO_PREF, 0},   /* conn close */
+               {BTA_DM_PM_ACTIVE, 0},    /* app open */
+               {BTA_DM_PM_NO_ACTION, 0}, /* app close */
+               {BTA_DM_PM_NO_ACTION, 0}, /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0}, /* sco close */
+               {BTA_DM_PM_SNIFF_A2DP_IDX,
+                BTA_FTC_IDLE_TO_SNIFF_DELAY_MS}, /* idle */
+               {BTA_DM_PM_ACTIVE, 0},            /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}          /* mode change retry */
+           }},
 
-    /* HL : 9 */
-    {(BTA_DM_PM_SNIFF), /* allow sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff  */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco open, active */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close sniff  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* HL : 9 */
+          {(BTA_DM_PM_SNIFF), /* allow sniff */
+           (BTA_DM_PM_SSR2),  /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 5000}, /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},           /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco open, active */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco close sniff */
+               {BTA_DM_PM_NO_ACTION, 0},         /* idle */
+               {BTA_DM_PM_ACTIVE, 0},            /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}          /* mode change retry */
+           }},
 
-    /* PANU : 10 */
-    {(BTA_DM_PM_SNIFF), /* allow sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_ACTIVE, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  active */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}},                        /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* PANU : 10 */
+          {(BTA_DM_PM_SNIFF), /* allow sniff */
+           (BTA_DM_PM_SSR2),  /* the SSR entry */
+           {
+               {BTA_DM_PM_ACTIVE, 0},            /* conn open active */
+               {BTA_DM_PM_NO_PREF, 0},           /* conn close */
+               {BTA_DM_PM_ACTIVE, 0},            /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco close */
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 5000}, /* idle */
+               {BTA_DM_PM_ACTIVE, 0},            /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}          /* mode change retry */
+           }},
 
-    /* NAP : 11 */
-    {(BTA_DM_PM_SNIFF), /* allow sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_ACTIVE, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  active */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}},                        /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
+          /* NAP : 11 */
+          {(BTA_DM_PM_SNIFF), /* allow sniff */
+           (BTA_DM_PM_SSR2),  /* the SSR entry */
+           {
+               {BTA_DM_PM_ACTIVE, 0},            /* conn open active */
+               {BTA_DM_PM_NO_PREF, 0},           /* conn close */
+               {BTA_DM_PM_ACTIVE, 0},            /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0},         /* sco close */
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 5000}, /* idle */
+               {BTA_DM_PM_ACTIVE, 0},            /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}          /* mode change retry */
+           }},
 
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* HS : 12 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR2),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF, hs_sniff_delay}, /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},            /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0},          /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},          /* app close */
+               {BTA_DM_PM_SNIFF3, 7000},          /* sco open, active */
+               {BTA_DM_PM_SNIFF, 7000},           /* sco close sniff */
+               {BTA_DM_PM_SNIFF, hs_sniff_delay}, /* idle */
+               {BTA_DM_PM_ACTIVE, 0},             /* busy */
+               {BTA_DM_PM_RETRY, 7000}            /* mode change retry */
+           }},
 
-    /* HS : 12 */
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff  */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_SNIFF3, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco open, active */
-         {{BTA_DM_PM_SNIFF, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* sco close sniff  */
-         {{BTA_DM_PM_SNIFF, 7000}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* busy */
-         {{BTA_DM_PM_RETRY, 7000},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }},
+          /* AVK : 13 */
+          {(BTA_DM_PM_SNIFF), /* allow sniff */
+           (BTA_DM_PM_SSR2),  /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF, 3000},  /* conn open sniff */
+               {BTA_DM_PM_NO_PREF, 0},   /* conn close */
+               {BTA_DM_PM_NO_ACTION, 0}, /* app open */
+               {BTA_DM_PM_NO_ACTION, 0}, /* app close */
+               {BTA_DM_PM_NO_ACTION, 0}, /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0}, /* sco close */
+               {BTA_DM_PM_SNIFF4, 3000}, /* idle */
+               {BTA_DM_PM_ACTIVE, 0},    /* busy */
+               {BTA_DM_PM_NO_ACTION, 0}  /* mode change retry */
+           }},
 
-    /* AVK : 13 */
-    {(BTA_DM_PM_SNIFF), /* allow sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF, 3000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  sniff */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF4, 3000}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* busy */
-         {{BTA_DM_PM_NO_ACTION, 0},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }}
+          /* GATTC : 14 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR2),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 10000}, /* conn open active */
+               {BTA_DM_PM_NO_PREF, 0},            /* conn close */
+               {BTA_DM_PM_ACTIVE, 0},             /* app open */
+               {BTA_DM_PM_NO_ACTION, 0},          /* app close */
+               {BTA_DM_PM_NO_ACTION, 0},          /* sco open */
+               {BTA_DM_PM_NO_ACTION, 0},          /* sco close */
+               {BTA_DM_PM_SNIFF_A2DP_IDX, 10000}, /* idle */
+               {BTA_DM_PM_ACTIVE, 0},             /* busy */
+               {BTA_DM_PM_RETRY, 5000}            /* mode change retry */
+           }},
 
-    /* GATTC : 14 */
-    ,
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 10000},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  active */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}},   /* conn close  */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}},    /* app open */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close   */
-         {{BTA_DM_PM_SNIFF_A2DP_IDX, 10000},
-          {BTA_DM_PM_NO_ACTION, 0}},                        /* idle */
-         {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_RETRY, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }}
-    /* GATTS : 15 */
-    ,
-    {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
-     (BTA_DM_PM_SSR2), /* the SSR entry */
-     {
-         {{BTA_DM_PM_NO_PREF, 0},
-          {BTA_DM_PM_NO_ACTION, 0}}, /* conn open  active */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close  */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open  */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */
-         {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */
-         {{BTA_DM_PM_RETRY, 5000},
-          {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */
-     }}
+          /* GATTS : 15 */
+          {(BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */
+           (BTA_DM_PM_SSR2),                   /* the SSR entry */
+           {
+               {BTA_DM_PM_NO_PREF, 0}, /* conn open active */
+               {BTA_DM_PM_NO_PREF, 0}, /* conn close */
+               {BTA_DM_PM_NO_PREF, 0}, /* app open */
+               {BTA_DM_PM_NO_PREF, 0}, /* app close */
+               {BTA_DM_PM_NO_PREF, 0}, /* sco open */
+               {BTA_DM_PM_NO_PREF, 0}, /* sco close */
+               {BTA_DM_PM_NO_PREF, 0}, /* idle */
+               {BTA_DM_PM_NO_PREF, 0}, /* busy */
+               {BTA_DM_PM_RETRY, 5000} /* mode change retry */
+           }}
 
 #ifdef BTE_SIM_APP /* For Insight builds only */
-    /* Entries at the end of the pm_spec table are user-defined (runtime
-       configurable),
-       for power consumption experiments.
-       Insight finds the first user-defined entry by looking for the first
-       BTA_DM_PM_NO_PREF.
-       The number of user_defined specs is defined by
-       BTA_SWRAP_UD_PM_SPEC_COUNT */
-    ,
-    {BTA_DM_PM_NO_PREF}, /* pm_spec USER_DEFINED_0 */
-    {BTA_DM_PM_NO_PREF}  /* pm_spec USER_DEFINED_1 */
+          /* Entries at the end of the pm_spec table are user-defined (runtime
+             configurable),
+             for power consumption experiments.
+             Insight finds the first user-defined entry by looking for the first
+             BTA_DM_PM_NO_PREF.
+             The number of user_defined specs is defined by
+             BTA_SWRAP_UD_PM_SPEC_COUNT */
+          ,
+          {BTA_DM_PM_NO_PREF}, /* pm_spec USER_DEFINED_0 */
+          {BTA_DM_PM_NO_PREF}  /* pm_spec USER_DEFINED_1 */
 #endif                   /* BTE_SIM_APP */
-};
+      };
+  return bta_dm_pm_spec;
+}
 
 /* Please refer to the SNIFF table definitions in bta_api.h.
  *
@@ -541,7 +473,6 @@
 tBTA_DM_SSR_SPEC* p_bta_dm_ssr_spec = &bta_dm_ssr_spec[0];
 
 const tBTA_DM_PM_CFG* p_bta_dm_pm_cfg = &bta_dm_pm_cfg[0];
-const tBTA_DM_PM_SPEC* p_bta_dm_pm_spec = &bta_dm_pm_spec[0];
 const tBTM_PM_PWR_MD* p_bta_dm_pm_md = &bta_dm_pm_md[0];
 
 /* The performance impact of EIR packet size
diff --git a/system/bta/dm/bta_dm_int.h b/system/bta/dm/bta_dm_int.h
index 3eb2baf..a0d6a1f 100644
--- a/system/bta/dm/bta_dm_int.h
+++ b/system/bta/dm/bta_dm_int.h
@@ -451,7 +451,7 @@
 typedef struct {
   uint8_t allow_mask; /* mask of sniff/hold/park modes to allow */
   uint8_t ssr; /* set SSR on conn open/unpark */
-  tBTA_DM_PM_ACTN actn_tbl[BTA_DM_PM_NUM_EVTS][2];
+  tBTA_DM_PM_ACTN actn_tbl[BTA_DM_PM_NUM_EVTS];
 
 } tBTA_DM_PM_SPEC;
 
@@ -470,8 +470,16 @@
 
 extern const uint16_t bta_service_id_to_uuid_lkup_tbl[];
 
+/* For Insight, PM cfg lookup tables are runtime configurable (to allow tweaking
+ * of params for power consumption measurements) */
+#ifndef BTE_SIM_APP
+#define tBTA_DM_PM_TYPE_QUALIFIER const
+#else
+#define tBTA_DM_PM_TYPE_QUALIFIER
+#endif
+
 extern const tBTA_DM_PM_CFG* p_bta_dm_pm_cfg;
-extern const tBTA_DM_PM_SPEC* p_bta_dm_pm_spec;
+tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC* get_bta_dm_pm_spec();
 extern const tBTM_PM_PWR_MD* p_bta_dm_pm_md;
 extern tBTA_DM_SSR_SPEC* p_bta_dm_ssr_spec;
 
diff --git a/system/bta/dm/bta_dm_pm.cc b/system/bta/dm/bta_dm_pm.cc
index 504799f..6cae974 100644
--- a/system/bta/dm/bta_dm_pm.cc
+++ b/system/bta/dm/bta_dm_pm.cc
@@ -344,7 +344,8 @@
       break;
   }
 
-  /* if no entries are there for the app_id and subsystem in p_bta_dm_pm_spec*/
+  /* if no entries are there for the app_id and subsystem in
+   * get_bta_dm_pm_spec()*/
   if (i > p_bta_dm_pm_cfg[0].app_id) {
     LOG_DEBUG("Ignoring power management callback as no service entries exist");
     return;
@@ -365,19 +366,19 @@
   int index = BTA_DM_PM_SSR0;
   if ((BTA_SYS_CONN_OPEN == status) && p_dev &&
       (p_dev->Info() & BTA_DM_DI_USE_SSR)) {
-    index = p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].ssr;
+    index = get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx].ssr;
   } else if (BTA_ID_AV == id) {
     if (BTA_SYS_CONN_BUSY == status) {
       /* set SSR4 for A2DP on SYS CONN BUSY */
       index = BTA_DM_PM_SSR4;
     } else if (BTA_SYS_CONN_IDLE == status) {
-      index = p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx].ssr;
+      index = get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx].ssr;
     }
   }
 
   /* if no action for the event */
-  if (p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx]
-          .actn_tbl[status][0]
+  if (get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx]
+          .actn_tbl[status]
           .power_mode == BTA_DM_PM_NO_ACTION) {
     if (BTA_DM_PM_SSR0 == index) /* and do not need to set SSR, return. */
       return;
@@ -395,8 +396,8 @@
 
   /* if subsystem has no more preference on the power mode remove
  the cb */
-  if (p_bta_dm_pm_spec[p_bta_dm_pm_cfg[i].spec_idx]
-          .actn_tbl[status][0]
+  if (get_bta_dm_pm_spec()[p_bta_dm_pm_cfg[i].spec_idx]
+          .actn_tbl[status]
           .power_mode == BTA_DM_PM_NO_PREF) {
     if (j != bta_dm_conn_srvcs.count) {
       bta_dm_conn_srvcs.count--;
@@ -499,7 +500,6 @@
   const tBTA_DM_PM_CFG* p_pm_cfg;
   const tBTA_DM_PM_SPEC* p_pm_spec;
   const tBTA_DM_PM_ACTN* p_act0;
-  const tBTA_DM_PM_ACTN* p_act1;
   tBTA_DM_SRVCS* p_srvcs = NULL;
   bool timer_started = false;
   uint8_t timer_idx, available_timer = BTA_DM_PM_MODE_TIMER_MAX;
@@ -532,9 +532,8 @@
       }
 
       p_pm_cfg = &p_bta_dm_pm_cfg[j];
-      p_pm_spec = &p_bta_dm_pm_spec[p_pm_cfg->spec_idx];
-      p_act0 = &p_pm_spec->actn_tbl[p_srvcs->state][0];
-      p_act1 = &p_pm_spec->actn_tbl[p_srvcs->state][1];
+      p_pm_spec = &get_bta_dm_pm_spec()[p_pm_cfg->spec_idx];
+      p_act0 = &p_pm_spec->actn_tbl[p_srvcs->state];
 
       allowed_modes |= p_pm_spec->allow_mask;
       LOG_DEBUG(
@@ -559,15 +558,6 @@
           }
         }
       }
-      /* if first preference has already failed, try second preference */
-      else if (!(failed_pm & p_act1->power_mode)) {
-        pref_modes |= p_act1->power_mode;
-
-        if (p_act1->power_mode > pm_action) {
-          pm_action = p_act1->power_mode;
-          timeout_ms = p_act1->timeout;
-        }
-      }
     }
   }
 
@@ -781,7 +771,7 @@
     for (int j = 1; j <= p_bta_dm_pm_cfg[0].app_id; j++) {
       /* find the associated p_bta_dm_pm_cfg */
       const tBTA_DM_PM_CFG& config = p_bta_dm_pm_cfg[j];
-      current_ssr_index = p_bta_dm_pm_spec[config.spec_idx].ssr;
+      current_ssr_index = get_bta_dm_pm_spec()[config.spec_idx].ssr;
       if ((config.id == service.id) && ((config.app_id == BTA_ALL_APP_ID) ||
                                         (config.app_id == service.app_id))) {
         LOG_INFO("Found connected service:%s app_id:%d peer:%s spec_name:%s",
diff --git a/system/bta/gatt/bta_gattc_act.cc b/system/bta/gatt/bta_gattc_act.cc
index 2068c07..48aa7b9 100644
--- a/system/bta/gatt/bta_gattc_act.cc
+++ b/system/bta/gatt/bta_gattc_act.cc
@@ -630,6 +630,18 @@
     }
   }
 
+  if (p_data->hdr.event == BTA_GATTC_INT_DISCONN_EVT) {
+    /* Since link has been disconnected by and it is possible that here are
+     * already some new p_clcb created for the background connect, the number of
+     * p_srcb->num_clcb is NOT 0. This will prevent p_srcb to be cleared inside
+     * the bta_gattc_clcb_dealloc.
+     *
+     * In this point of time, we know that link does not exist, so let's make
+     * sure the connection state, mtu and database is cleared.
+     */
+    bta_gattc_server_disconnected(p_clcb->p_srcb);
+  }
+
   bta_gattc_clcb_dealloc(p_clcb);
 
   if (p_data->hdr.event == BTA_GATTC_API_CLOSE_EVT) {
diff --git a/system/bta/gatt/bta_gattc_int.h b/system/bta/gatt/bta_gattc_int.h
index 9e1f761..ef70b83 100644
--- a/system/bta/gatt/bta_gattc_int.h
+++ b/system/bta/gatt/bta_gattc_int.h
@@ -423,6 +423,7 @@
                                              const RawAddress& remote_bda,
                                              tBT_TRANSPORT transport);
 extern void bta_gattc_clcb_dealloc(tBTA_GATTC_CLCB* p_clcb);
+extern void bta_gattc_server_disconnected(tBTA_GATTC_SERV* p_srcb);
 extern tBTA_GATTC_CLCB* bta_gattc_find_alloc_clcb(tGATT_IF client_if,
                                                   const RawAddress& remote_bda,
                                                   tBT_TRANSPORT transport);
diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc
index a3ff62d..2861873 100644
--- a/system/bta/gatt/bta_gattc_utils.cc
+++ b/system/bta/gatt/bta_gattc_utils.cc
@@ -191,6 +191,26 @@
 
 /*******************************************************************************
  *
+ * Function         bta_gattc_server_disconnected
+ *
+ * Description      Set server cache disconnected
+ *
+ * Returns          pointer to the srcb
+ *
+ ******************************************************************************/
+void bta_gattc_server_disconnected(tBTA_GATTC_SERV* p_srcb) {
+  if (p_srcb && p_srcb->connected) {
+    p_srcb->connected = false;
+    p_srcb->state = BTA_GATTC_SERV_IDLE;
+    p_srcb->mtu = 0;
+
+    // clear reallocating
+    p_srcb->gatt_database.Clear();
+  }
+}
+
+/*******************************************************************************
+ *
  * Function         bta_gattc_clcb_dealloc
  *
  * Description      Deallocte a clcb
diff --git a/system/bta/gatt/bta_gatts_api.cc b/system/bta/gatt/bta_gatts_api.cc
index 0de36db..d0e4f44 100644
--- a/system/bta/gatt/bta_gatts_api.cc
+++ b/system/bta/gatt/bta_gatts_api.cc
@@ -242,6 +242,12 @@
 void BTA_GATTS_HandleValueIndication(uint16_t conn_id, uint16_t attr_id,
                                      std::vector<uint8_t> value,
                                      bool need_confirm) {
+
+  if (value.size() > sizeof(tBTA_GATTS_API_INDICATION::value)) {
+    LOG(ERROR) << __func__ << "data to indicate is too long";
+    return;
+  }
+
   tBTA_GATTS_API_INDICATION* p_buf =
       (tBTA_GATTS_API_INDICATION*)osi_calloc(sizeof(tBTA_GATTS_API_INDICATION));
 
diff --git a/system/bta/include/bta_api.h b/system/bta/include/bta_api.h
index f5caac3..2efb326 100644
--- a/system/bta/include/bta_api.h
+++ b/system/bta/include/bta_api.h
@@ -1224,4 +1224,14 @@
  ******************************************************************************/
 extern void BTA_DmBleResetId(void);
 
+/*******************************************************************************
+ *
+ * Function         BTA_DmCheckLeAudioCapable
+ *
+ * Description      Checks if device should be considered as LE Audio capable
+ *
+ * Returns          True if Le Audio capable device, false otherwise
+ *
+ ******************************************************************************/
+extern bool BTA_DmCheckLeAudioCapable(const RawAddress& address);
 #endif /* BTA_API_H */
diff --git a/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc b/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
index f55eee0..dfbb026 100644
--- a/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
+++ b/system/bta/le_audio/audio_hal_client/audio_hal_client_test.cc
@@ -34,6 +34,7 @@
 using ::testing::Assign;
 using ::testing::AtLeast;
 using ::testing::DoAll;
+using ::testing::DoDefault;
 using ::testing::Invoke;
 using ::testing::Mock;
 using ::testing::Return;
@@ -476,6 +477,13 @@
         // Return exactly as much data as requested
         promise.set_value();
         return len;
+      }))
+      .WillRepeatedly(Invoke([](uint8_t* p_buf, uint32_t len) -> uint32_t {
+        // fake some data from audio framework
+        for (uint32_t i = 0u; i < len; ++i) {
+          p_buf[i] = i;
+        }
+        return len;
       }));
 
   std::promise<void> data_promise;
@@ -484,10 +492,12 @@
   /* Expect this callback to be called to Client by the HAL glue layer */
   std::vector<uint8_t> media_data_to_send;
   EXPECT_CALL(mock_hal_sink_event_receiver_, OnAudioDataReady(_))
+      .Times(AtLeast(1))
       .WillOnce(Invoke([&](const std::vector<uint8_t>& data) -> void {
         media_data_to_send = std::move(data);
         data_promise.set_value();
-      }));
+      }))
+      .WillRepeatedly(DoDefault());
 
   /* Expect LeAudio registered event listener to get called when HAL calls the
    * audio_hal_client's internal resume callback.
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 5ad4163..b5061ef 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -1108,13 +1108,12 @@
     if (!leAudioDevice) {
       leAudioDevices_.Add(address, DeviceConnectState::CONNECTING_BY_USER);
     } else {
-      if (leAudioDevice->GetConnectionState() !=
-          DeviceConnectState::DISCONNECTED) {
-        LOG_ERROR(
-            "Device %s is in invalid state: %s",
-            leAudioDevice->address_.ToString().c_str(),
-            bluetooth::common::ToString(leAudioDevice->GetConnectionState())
-                .c_str());
+      auto current_connect_state = leAudioDevice->GetConnectionState();
+      if ((current_connect_state == DeviceConnectState::CONNECTED) ||
+          (current_connect_state == DeviceConnectState::CONNECTING_BY_USER)) {
+        LOG_ERROR("Device %s is in invalid state: %s",
+                  leAudioDevice->address_.ToString().c_str(),
+                  bluetooth::common::ToString(current_connect_state).c_str());
 
         return;
       }
@@ -4414,7 +4413,7 @@
 void le_audio_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) {
   if (!p_data || !instance) return;
 
-  DLOG(INFO) << __func__ << " event = " << +event;
+  LOG_DEBUG("event = %d", static_cast<int>(event));
 
   switch (event) {
     case BTA_GATTC_DEREG_EVT:
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index f40c282..16c79b5 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -2469,7 +2469,8 @@
              << ",\tactive: " << ase.active << ", dir: "
              << (ase.direction == types::kLeAudioDirectionSink ? "sink"
                                                                : "source")
-             << ",\tcis_id: " << static_cast<int>(ase.cis_id) << ",\tstate: "
+             << ",\tcis_id: " << static_cast<int>(ase.cis_id)
+             << ",\tcis_handle: " << ase.cis_conn_hdl << ",\tstate: "
              << bluetooth::common::ToString(ase.data_path_state);
     }
   }
@@ -2546,13 +2547,19 @@
 
 void LeAudioDevice::DeactivateAllAses(void) {
   for (auto& ase : ases_) {
-    if (ase.active) {
-      ase.state = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
-      ase.data_path_state = AudioStreamDataPathState::IDLE;
-      ase.active = false;
-      ase.cis_id = le_audio::kInvalidCisId;
-      ase.cis_conn_hdl = 0;
+    if (ase.active == false &&
+        ase.data_path_state != AudioStreamDataPathState::IDLE) {
+      LOG_WARN(
+          " %s, ase_id: %d, ase.cis_id: %d, cis_handle: 0x%02x, "
+          "ase.data_path=%s",
+          address_.ToString().c_str(), ase.id, ase.cis_id, ase.cis_conn_hdl,
+          bluetooth::common::ToString(ase.data_path_state).c_str());
     }
+    ase.state = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
+    ase.data_path_state = AudioStreamDataPathState::IDLE;
+    ase.active = false;
+    ase.cis_id = le_audio::kInvalidCisId;
+    ase.cis_conn_hdl = 0;
   }
 }
 
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 8476fec..09bba6f 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -614,6 +614,8 @@
                                       LeAudioDevice* leAudioDevice) {
     FreeLinkQualityReports(leAudioDevice);
     leAudioDevice->conn_id_ = GATT_INVALID_CONN_ID;
+    /* mark ASEs as not used. */
+    leAudioDevice->DeactivateAllAses();
 
     if (!group) {
       LOG(ERROR) << __func__
@@ -622,9 +624,6 @@
       return;
     }
 
-    /* mark ASEs as not used. */
-    leAudioDevice->DeactivateAllAses();
-
     /* If group is in Idle and not transitioning, just update the current group
      * audio context availability which could change due to disconnected group
      * member.
diff --git a/system/btif/co/bta_av_co.cc b/system/btif/co/bta_av_co.cc
index 34406f1..e828f0a 100644
--- a/system/btif/co/bta_av_co.cc
+++ b/system/btif/co/bta_av_co.cc
@@ -33,6 +33,7 @@
 #include "btif/include/btif_av.h"
 #include "include/hardware/bt_av.h"
 #include "osi/include/osi.h"  // UNUSED_ATTR
+#include "osi/include/allocator.h"
 #include "stack/include/a2dp_codec_api.h"
 #include "stack/include/a2dp_error_codes.h"
 #include "stack/include/avdt_api.h"
@@ -1372,6 +1373,12 @@
   p_buf = btif_a2dp_source_audio_readbuf();
   if (p_buf == nullptr) return nullptr;
 
+  if (p_buf->offset < 4) {
+    osi_free(p_buf);
+    APPL_TRACE_ERROR("No space for timestamp in packet, dropped");
+    return nullptr;
+  }
+
   /*
    * Retrieve the timestamp information from the media packet,
    * and set up the packet header.
@@ -1385,6 +1392,8 @@
       !A2DP_BuildCodecHeader(p_codec_info, p_buf, p_buf->layer_specific)) {
     APPL_TRACE_ERROR("%s: unsupported codec type (%d)", __func__,
                      A2DP_GetCodecType(p_codec_info));
+    osi_free(p_buf);
+    return nullptr;
   }
 
   if (ContentProtectEnabled() && (active_peer_ != nullptr) &&
diff --git a/system/btif/co/bta_hh_co.cc b/system/btif/co/bta_hh_co.cc
index 90fe1df..6115627 100644
--- a/system/btif/co/bta_hh_co.cc
+++ b/system/btif/co/bta_hh_co.cc
@@ -673,15 +673,16 @@
     ev.type = UHID_FEATURE_ANSWER;
     ev.u.feature_answer.id = *get_rpt_id;
     ev.u.feature_answer.err = status;
-    ev.u.feature_answer.size = len;
+    ev.u.feature_answer.size = len - GET_RPT_RSP_OFFSET;
     osi_free(get_rpt_id);
-    if (len > 0) {
-      if (len > UHID_DATA_MAX) {
+    if (len > GET_RPT_RSP_OFFSET) {
+      if (len - GET_RPT_RSP_OFFSET > UHID_DATA_MAX) {
         APPL_TRACE_WARNING("%s: Report size greater than allowed size",
                            __func__);
         return;
       }
-      memcpy(ev.u.feature_answer.data, p_rpt + GET_RPT_RSP_OFFSET, len);
+      memcpy(ev.u.feature_answer.data, p_rpt + GET_RPT_RSP_OFFSET,
+             len - GET_RPT_RSP_OFFSET);
       uhid_write(p_dev->fd, &ev);
     }
   }
diff --git a/system/btif/include/btif_a2dp_source.h b/system/btif/include/btif_a2dp_source.h
index f0b4b24..df20319 100644
--- a/system/btif/include/btif_a2dp_source.h
+++ b/system/btif/include/btif_a2dp_source.h
@@ -63,7 +63,7 @@
 
 // Shutdown the A2DP Source module.
 // This function should be called by the BTIF state machine to stop streaming.
-void btif_a2dp_source_shutdown(void);
+void btif_a2dp_source_shutdown(std::promise<void>);
 
 // Cleanup the A2DP Source module.
 // This function should be called by the BTIF state machine during graceful
diff --git a/system/btif/include/btif_bqr.h b/system/btif/include/btif_bqr.h
index 3905a8a..b0778ab 100644
--- a/system/btif/include/btif_bqr.h
+++ b/system/btif/include/btif_bqr.h
@@ -99,6 +99,8 @@
 // Total length of all parameters of the link Quality related event except
 // Vendor Specific Parameters.
 static constexpr uint8_t kLinkQualityParamTotalLen = 48;
+// 7.8.116 LE Read ISO Link Quality command
+static constexpr uint8_t kISOLinkQualityParamTotalLen = 24;
 // Total length of all parameters of the ROOT_INFLAMMATION event except Vendor
 // Specific Parameters.
 static constexpr uint8_t kRootInflammationParamTotalLen = 3;
diff --git a/system/btif/src/btif_a2dp_source.cc b/system/btif/src/btif_a2dp_source.cc
index f13abac..4ca2c8c 100644
--- a/system/btif/src/btif_a2dp_source.cc
+++ b/system/btif/src/btif_a2dp_source.cc
@@ -30,6 +30,7 @@
 #include <string.h>
 
 #include <algorithm>
+#include <future>
 
 #include "audio_a2dp_hw/include/audio_a2dp_hw.h"
 #include "audio_hal_interface/a2dp_encoding.h"
@@ -242,7 +243,7 @@
     const RawAddress& peer_address, std::promise<void> start_session_promise);
 static void btif_a2dp_source_end_session_delayed(
     const RawAddress& peer_address);
-static void btif_a2dp_source_shutdown_delayed(void);
+static void btif_a2dp_source_shutdown_delayed(std::promise<void>);
 static void btif_a2dp_source_cleanup_delayed(void);
 static void btif_a2dp_source_audio_tx_start_event(void);
 static void btif_a2dp_source_audio_tx_stop_event(void);
@@ -483,7 +484,7 @@
   }
 }
 
-void btif_a2dp_source_shutdown(void) {
+void btif_a2dp_source_shutdown(std::promise<void> shutdown_complete_promise) {
   LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   if ((btif_a2dp_source_cb.State() == BtifA2dpSource::kStateOff) ||
@@ -495,10 +496,12 @@
   btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateShuttingDown);
 
   btif_a2dp_source_thread.DoInThread(
-      FROM_HERE, base::Bind(&btif_a2dp_source_shutdown_delayed));
+      FROM_HERE, base::BindOnce(&btif_a2dp_source_shutdown_delayed,
+                                std::move(shutdown_complete_promise)));
 }
 
-static void btif_a2dp_source_shutdown_delayed(void) {
+static void btif_a2dp_source_shutdown_delayed(
+    std::promise<void> shutdown_complete_promise) {
   LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   // Stop the timer
@@ -514,13 +517,16 @@
   btif_a2dp_source_cb.tx_audio_queue = nullptr;
 
   btif_a2dp_source_cb.SetState(BtifA2dpSource::kStateOff);
+
+  shutdown_complete_promise.set_value();
 }
 
 void btif_a2dp_source_cleanup(void) {
   LOG_INFO("%s: state=%s", __func__, btif_a2dp_source_cb.StateStr().c_str());
 
   // Make sure the source is shutdown
-  btif_a2dp_source_shutdown();
+  std::promise<void> shutdown_complete_promise;
+  btif_a2dp_source_shutdown(std::move(shutdown_complete_promise));
 
   btif_a2dp_source_thread.DoInThread(
       FROM_HERE, base::Bind(&btif_a2dp_source_cleanup_delayed));
diff --git a/system/btif/src/btif_av.cc b/system/btif/src/btif_av.cc
index 4e132c6..ed0e952 100644
--- a/system/btif/src/btif_av.cc
+++ b/system/btif/src/btif_av.cc
@@ -26,6 +26,7 @@
 #include <frameworks/proto_logging/stats/enums/bluetooth/a2dp/enums.pb.h>
 #include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h>
 
+#include <chrono>
 #include <cstdint>
 #include <future>
 #include <memory>
@@ -500,7 +501,15 @@
                      << ": unable to set active peer to empty in BtaAvCo";
       }
       btif_a2dp_source_end_session(active_peer_);
-      btif_a2dp_source_shutdown();
+      std::promise<void> shutdown_complete_promise;
+      std::future<void> shutdown_complete_future =
+          shutdown_complete_promise.get_future();
+      btif_a2dp_source_shutdown(std::move(shutdown_complete_promise));
+      using namespace std::chrono_literals;
+      if (shutdown_complete_future.wait_for(1s) ==
+          std::future_status::timeout) {
+        LOG_ERROR("Timed out waiting for A2DP source shutdown to complete.");
+      }
       active_peer_ = peer_address;
       peer_ready_promise.set_value();
       return true;
diff --git a/system/btif/src/btif_bqr.cc b/system/btif/src/btif_bqr.cc
index a728d80..03f1a25 100644
--- a/system/btif/src/btif_bqr.cc
+++ b/system/btif/src/btif_bqr.cc
@@ -77,13 +77,27 @@
   STREAM_TO_UINT32(bqr_link_quality_event_.buffer_underflow_bytes, p_param_buf);
 
   if (vendor_cap_supported_version >= kBqrIsoVersion) {
-    STREAM_TO_UINT32(bqr_link_quality_event_.tx_total_packets, p_param_buf);
-    STREAM_TO_UINT32(bqr_link_quality_event_.tx_unacked_packets, p_param_buf);
-    STREAM_TO_UINT32(bqr_link_quality_event_.tx_flushed_packets, p_param_buf);
-    STREAM_TO_UINT32(bqr_link_quality_event_.tx_last_subevent_packets,
-                     p_param_buf);
-    STREAM_TO_UINT32(bqr_link_quality_event_.crc_error_packets, p_param_buf);
-    STREAM_TO_UINT32(bqr_link_quality_event_.rx_duplicate_packets, p_param_buf);
+    if (length < kLinkQualityParamTotalLen + kISOLinkQualityParamTotalLen) {
+      LOG(WARNING) << __func__
+                   << ": Parameter total length: " << std::to_string(length)
+                   << " is abnormal. "
+                   << "vendor_cap_supported_version: "
+                   << vendor_cap_supported_version << " "
+                   << " (>= "
+                   << "kBqrIsoVersion=" << kBqrIsoVersion << "), "
+                   << "It should not be shorter than: "
+                   << std::to_string(kLinkQualityParamTotalLen +
+                                     kISOLinkQualityParamTotalLen);
+    } else {
+      STREAM_TO_UINT32(bqr_link_quality_event_.tx_total_packets, p_param_buf);
+      STREAM_TO_UINT32(bqr_link_quality_event_.tx_unacked_packets, p_param_buf);
+      STREAM_TO_UINT32(bqr_link_quality_event_.tx_flushed_packets, p_param_buf);
+      STREAM_TO_UINT32(bqr_link_quality_event_.tx_last_subevent_packets,
+                       p_param_buf);
+      STREAM_TO_UINT32(bqr_link_quality_event_.crc_error_packets, p_param_buf);
+      STREAM_TO_UINT32(bqr_link_quality_event_.rx_duplicate_packets,
+                       p_param_buf);
+    }
   }
 
   const auto now = system_clock::to_time_t(system_clock::now());
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index b05c342..733e597 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -680,12 +680,16 @@
  * up LE profile connection, and limits all possible service discovery
  * ordering issues (first Classic, GATT over SDP, etc) */
 static bool is_device_le_audio_capable(const RawAddress bd_addr) {
-  if (!LeAudioClient::IsLeAudioClientRunning() ||
-      !check_cod_le_audio(bd_addr)) {
+  if (!LeAudioClient::IsLeAudioClientRunning()) {
     /* If LE Audio profile is not enabled, do nothing. */
     return false;
   }
 
+  if (!check_cod_le_audio(bd_addr) && !BTA_DmCheckLeAudioCapable(bd_addr)) {
+    /* LE Audio not present in CoD or in LE Advertisement, do nothing.*/
+    return false;
+  }
+
   tBT_DEVICE_TYPE tmp_dev_type;
   tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC;
   BTM_ReadDevInfo(bd_addr, &tmp_dev_type, &addr_type);
@@ -1377,6 +1381,17 @@
         status = btif_storage_set_remote_addr_type(&bdaddr, addr_type);
         ASSERTC(status == BT_STATUS_SUCCESS,
                 "failed to save remote addr type (inquiry)", status);
+
+        bool restrict_report = osi_property_get_bool(
+            "bluetooth.restrict_discovered_device.enabled", false);
+        if (restrict_report &&
+            p_search_data->inq_res.device_type == BT_DEVICE_TYPE_BLE &&
+            !(p_search_data->inq_res.ble_evt_type & BTM_BLE_CONNECTABLE_MASK)) {
+          LOG_INFO("%s: Ble device is not connectable",
+                   bdaddr.ToString().c_str());
+          break;
+        }
+
         /* Callback to notify upper layer of device */
         invoke_device_found_cb(num_properties, properties);
       }
@@ -1517,12 +1532,12 @@
        * before before passing services to upper layers. */
       if ((bd_addr == pairing_cb.bd_addr ||
            bd_addr == pairing_cb.static_bdaddr) &&
-          a2dp_sink_capable &&
-          LeAudioClient::IsLeAudioClientRunning() &&
+          a2dp_sink_capable && LeAudioClient::IsLeAudioClientRunning() &&
           pairing_cb.gatt_over_le !=
               btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED &&
           (check_cod_le_audio(bd_addr) ||
-           metadata_cb.le_audio_cache.contains(bd_addr))) {
+           metadata_cb.le_audio_cache.contains(bd_addr) ||
+           BTA_DmCheckLeAudioCapable(bd_addr))) {
         skip_reporting_wait_for_le = true;
       }
 
diff --git a/system/btif/src/btif_rc.cc b/system/btif/src/btif_rc.cc
index d66c427..1b68326 100644
--- a/system/btif/src/btif_rc.cc
+++ b/system/btif/src/btif_rc.cc
@@ -3650,29 +3650,31 @@
      * for standard attributes.
      */
     p_app_settings->num_ext_attrs = 0;
-    for (xx = 0; xx < p_app_settings->ext_attr_index; xx++) {
+    for (xx = 0;
+         xx < p_app_settings->ext_attr_index && xx < AVRC_MAX_APP_ATTR_SIZE;
+         xx++) {
       osi_free_and_reset((void**)&p_app_settings->ext_attrs[xx].p_str);
     }
     p_app_settings->ext_attr_index = 0;
 
-    if (p_dev) {
-      for (xx = 0; xx < p_app_settings->num_attrs; xx++) {
-        attrs[xx] = p_app_settings->attrs[xx].attr_id;
-      }
-
-      do_in_jni_thread(
-          FROM_HERE,
-          base::Bind(bt_rc_ctrl_callbacks->playerapplicationsetting_cb,
-                     p_dev->rc_addr, p_app_settings->num_attrs,
-                     p_app_settings->attrs, 0, nullptr));
-      get_player_app_setting_cmd(xx, attrs, p_dev);
+    for (xx = 0; xx < p_app_settings->num_attrs && xx < AVRC_MAX_APP_ATTR_SIZE;
+         xx++) {
+      attrs[xx] = p_app_settings->attrs[xx].attr_id;
     }
+
+    do_in_jni_thread(
+        FROM_HERE, base::Bind(bt_rc_ctrl_callbacks->playerapplicationsetting_cb,
+                              p_dev->rc_addr, p_app_settings->num_attrs,
+                              p_app_settings->attrs, 0, nullptr));
+    get_player_app_setting_cmd(xx, attrs, p_dev);
+
     return;
   }
 
   for (xx = 0; xx < p_rsp->num_attr; xx++) {
     uint8_t x;
-    for (x = 0; x < p_app_settings->num_ext_attrs; x++) {
+    for (x = 0; x < p_app_settings->num_ext_attrs && x < AVRC_MAX_APP_ATTR_SIZE;
+         x++) {
       if (p_app_settings->ext_attrs[x].attr_id == p_rsp->p_attrs[xx].attr_id) {
         p_app_settings->ext_attrs[x].charset_id = p_rsp->p_attrs[xx].charset_id;
         p_app_settings->ext_attrs[x].str_len = p_rsp->p_attrs[xx].str_len;
@@ -3682,7 +3684,9 @@
     }
   }
 
-  for (xx = 0; xx < p_app_settings->ext_attrs[0].num_val; xx++) {
+  for (xx = 0;
+       xx < p_app_settings->ext_attrs[0].num_val && xx < BTRC_MAX_APP_ATTR_SIZE;
+       xx++) {
     vals[xx] = p_app_settings->ext_attrs[0].ext_attr_val[xx].val;
   }
   get_player_app_setting_value_text_cmd(vals, xx, p_dev);
@@ -3726,11 +3730,13 @@
      * for standard attributes.
      */
     p_app_settings->num_ext_attrs = 0;
-    for (xx = 0; xx < p_app_settings->ext_attr_index; xx++) {
+    for (xx = 0;
+         xx < p_app_settings->ext_attr_index && xx < AVRC_MAX_APP_ATTR_SIZE;
+         xx++) {
       int x;
       btrc_player_app_ext_attr_t* p_ext_attr = &p_app_settings->ext_attrs[xx];
 
-      for (x = 0; x < p_ext_attr->num_val; x++)
+      for (x = 0; x < p_ext_attr->num_val && x < BTRC_MAX_APP_ATTR_SIZE; x++)
         osi_free_and_reset((void**)&p_ext_attr->ext_attr_val[x].p_str);
       p_ext_attr->num_val = 0;
       osi_free_and_reset((void**)&p_app_settings->ext_attrs[xx].p_str);
@@ -3749,11 +3755,17 @@
     return;
   }
 
+  if (p_app_settings->ext_val_index >= AVRC_MAX_APP_ATTR_SIZE) {
+    BTIF_TRACE_ERROR("ext_val_index is 0x%02x, overflow!",
+                     p_app_settings->ext_val_index);
+    return;
+  }
+
   for (xx = 0; xx < p_rsp->num_attr; xx++) {
     uint8_t x;
     btrc_player_app_ext_attr_t* p_ext_attr;
     p_ext_attr = &p_app_settings->ext_attrs[p_app_settings->ext_val_index];
-    for (x = 0; x < p_rsp->num_attr; x++) {
+    for (x = 0; x < p_rsp->num_attr && x < BTRC_MAX_APP_ATTR_SIZE; x++) {
       if (p_ext_attr->ext_attr_val[x].val == p_rsp->p_attrs[xx].attr_id) {
         p_ext_attr->ext_attr_val[x].charset_id = p_rsp->p_attrs[xx].charset_id;
         p_ext_attr->ext_attr_val[x].str_len = p_rsp->p_attrs[xx].str_len;
@@ -3806,10 +3818,12 @@
  **************************************************************************/
 static void cleanup_app_attr_val_txt_response(
     btif_rc_player_app_settings_t* p_app_settings) {
-  for (uint8_t xx = 0; xx < p_app_settings->ext_attr_index; xx++) {
+  for (uint8_t xx = 0;
+       xx < p_app_settings->ext_attr_index && xx < AVRC_MAX_APP_ATTR_SIZE;
+       xx++) {
     int x;
     btrc_player_app_ext_attr_t* p_ext_attr = &p_app_settings->ext_attrs[xx];
-    for (x = 0; x < p_ext_attr->num_val; x++) {
+    for (x = 0; x < p_ext_attr->num_val && x < BTRC_MAX_APP_ATTR_SIZE; x++) {
       osi_free_and_reset((void**)&p_ext_attr->ext_attr_val[x].p_str);
     }
     p_ext_attr->num_val = 0;
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index ed6c375..d2ecc50 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -394,6 +394,7 @@
         ":BluetoothShimTestSources",
         ":BluetoothSecurityUnitTestSources",
         ":BluetoothStorageUnitTestSources",
+        ":BluetoothBtaaSources_linux_generic_tests",
     ],
     generated_headers: [
         "BluetoothGeneratedBundlerSchema_h_bfbs",
diff --git a/system/gd/btaa/Android.bp b/system/gd/btaa/Android.bp
index 2aea7ee..6b4c269 100644
--- a/system/gd/btaa/Android.bp
+++ b/system/gd/btaa/Android.bp
@@ -30,3 +30,10 @@
         "linux_generic/wakelock_processor.cc",
     ],
 }
+
+filegroup {
+    name: "BluetoothBtaaSources_linux_generic_tests",
+    srcs: [
+        "linux_generic/attribution_processor_tests.cc",
+    ],
+}
diff --git a/system/gd/btaa/attribution_processor.h b/system/gd/btaa/attribution_processor.h
index d18a8ce..e28e6cb 100644
--- a/system/gd/btaa/attribution_processor.h
+++ b/system/gd/btaa/attribution_processor.h
@@ -82,7 +82,20 @@
   void Dump(
       std::promise<flatbuffers::Offset<ActivityAttributionData>> promise, flatbuffers::FlatBufferBuilder* fb_builder);
 
+  using ClockType = std::chrono::time_point<std::chrono::system_clock>;
+  using NowFunc = ClockType (*)();
+
+  // by default, we use the std::chrono::system_clock::now implementation to
+  // get the current timestamp
+  AttributionProcessor() : now_func_(std::chrono::system_clock::now) {}
+  // in other cases, we may need to use different implementation
+  // e.g., for testing purposes
+  AttributionProcessor(NowFunc func) : now_func_(func) {}
+
  private:
+  // this function is added for testing support in
+  // OnWakelockReleased
+  NowFunc now_func_ = std::chrono::system_clock::now;
   bool wakeup_ = false;
   std::unordered_map<AddressActivityKey, BtaaAggregationEntry, AddressActivityKeyHasher> btaa_aggregator_;
   std::unordered_map<AddressActivityKey, BtaaAggregationEntry, AddressActivityKeyHasher> wakelock_duration_aggregator_;
diff --git a/system/gd/btaa/linux_generic/attribution_processor.cc b/system/gd/btaa/linux_generic/attribution_processor.cc
index 62e62ae..eadad9d 100644
--- a/system/gd/btaa/linux_generic/attribution_processor.cc
+++ b/system/gd/btaa/linux_generic/attribution_processor.cc
@@ -69,7 +69,7 @@
     return;
   }
 
-  auto cur_time = std::chrono::system_clock::now();
+  auto cur_time = now_func_();
   for (auto& it : wakelock_duration_aggregator_) {
     it.second.wakelock_duration_ms = (uint64_t)duration_ms * it.second.byte_count / total_byte_count;
     if (btaa_aggregator_.find(it.first) == btaa_aggregator_.end()) {
@@ -126,23 +126,29 @@
   }
   // Trim down the transient entries in the aggregator to avoid that it overgrows
   if (btaa_aggregator_.size() > kMapSizeTrimDownAggregationEntry) {
-    for (auto& it : btaa_aggregator_) {
+    auto it = btaa_aggregator_.begin();
+    while (it != btaa_aggregator_.end()) {
       auto elapsed_time_sec =
-          std::chrono::duration_cast<std::chrono::seconds>(cur_time - it.second.creation_time).count();
+          std::chrono::duration_cast<std::chrono::seconds>(cur_time - it->second.creation_time).count();
       if (elapsed_time_sec > kDurationTransientDeviceActivityEntrySecs &&
-          it.second.byte_count < kByteCountTransientDeviceActivityEntry) {
-        btaa_aggregator_.erase(it.first);
+          it->second.byte_count < kByteCountTransientDeviceActivityEntry) {
+        it = btaa_aggregator_.erase(it);
+      } else {
+        it++;
       }
     }
   }
 
   if (app_activity_aggregator_.size() > kMapSizeTrimDownAggregationEntry) {
-    for (auto& it : app_activity_aggregator_) {
+    auto it = app_activity_aggregator_.begin();
+    while (it != app_activity_aggregator_.end()) {
       auto elapsed_time_sec =
-          std::chrono::duration_cast<std::chrono::seconds>(cur_time - it.second.creation_time).count();
+          std::chrono::duration_cast<std::chrono::seconds>(cur_time - it->second.creation_time).count();
       if (elapsed_time_sec > kDurationTransientDeviceActivityEntrySecs &&
-          it.second.byte_count < kByteCountTransientDeviceActivityEntry) {
-        app_activity_aggregator_.erase(it.first);
+          it->second.byte_count < kByteCountTransientDeviceActivityEntry) {
+        it = app_activity_aggregator_.erase(it);
+      } else {
+        it++;
       }
     }
   }
diff --git a/system/gd/btaa/linux_generic/attribution_processor_tests.cc b/system/gd/btaa/linux_generic/attribution_processor_tests.cc
new file mode 100644
index 0000000..1a27206
--- /dev/null
+++ b/system/gd/btaa/linux_generic/attribution_processor_tests.cc
@@ -0,0 +1,85 @@
+/*
+ * 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 <base/strings/stringprintf.h>
+#include <gtest/gtest.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "btaa/activity_attribution.h"
+#include "btaa/attribution_processor.h"
+
+using bluetooth::hci::Address;
+using namespace bluetooth::activity_attribution;
+using namespace std::chrono;
+
+// mock for std::chrono::system_clock::now
+static AttributionProcessor::ClockType now_ret_val;
+static AttributionProcessor::ClockType fake_now() {
+  return now_ret_val;
+}
+
+class AttributionProcessorTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    pAttProc = std::make_unique<AttributionProcessor>(fake_now);
+  }
+  void TearDown() override {
+    pAttProc.reset();
+  }
+
+  std::unique_ptr<AttributionProcessor> pAttProc;
+};
+
+static void fake_now_set_current() {
+  now_ret_val = system_clock::now();
+}
+
+static void fake_now_advance_1000sec() {
+  now_ret_val += seconds(1000s);
+}
+
+TEST_F(AttributionProcessorTest, UAFInOnWakelockReleasedRegressionTest) {
+  std::vector<BtaaHciPacket> btaaPackets;
+  Address addr;
+
+  fake_now_set_current();
+
+  // setup the condition 1 for triggering erase operation
+  // add 220 entries in app_activity_aggregator_
+  // and btaa_aggregator_
+  for (int i = 0; i < 220; i++) {
+    std::string addrStr = base::StringPrintf("21:43:65:87:a9:%02x", i + 10);
+    ASSERT_TRUE(Address::FromString(addrStr, addr));
+    BtaaHciPacket packet(Activity::ACL, addr, 30 * i);
+    btaaPackets.push_back(packet);
+    pAttProc->NotifyActivityAttributionInfo(i + 1000, "com.test.app" + std::to_string(i), addrStr);
+  }
+
+  pAttProc->OnBtaaPackets(btaaPackets);
+  pAttProc->OnWakelockReleased(100);
+
+  // setup the condition 2 for triggering erase operation
+  // make elapsed_time_sec > 900s
+  fake_now_advance_1000sec();
+
+  pAttProc->OnBtaaPackets(btaaPackets);
+  pAttProc->OnWakelockReleased(100);
+}
diff --git a/system/gd/hci/acl_manager/round_robin_scheduler_test.cc b/system/gd/hci/acl_manager/round_robin_scheduler_test.cc
index 9d43c0f..e27ff7c 100644
--- a/system/gd/hci/acl_manager/round_robin_scheduler_test.cc
+++ b/system/gd/hci/acl_manager/round_robin_scheduler_test.cc
@@ -136,8 +136,9 @@
 
     packet_count_--;
     if (packet_count_ == 0) {
-      packet_promise_->set_value();
-      packet_promise_ = nullptr;
+      std::promise<void>* prom = packet_promise_.release();
+      prom->set_value();
+      delete prom;
     }
   }
 
diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc
index 9953ab8..c96b971 100644
--- a/system/gd/hci/le_advertising_manager.cc
+++ b/system/gd/hci/le_advertising_manager.cc
@@ -61,7 +61,6 @@
   int8_t tx_power;
   uint16_t duration;
   uint8_t max_extended_advertising_events;
-  bool pending_start = false;  // whether we have started but are still in the queue
   bool started = false;
   bool connectable = false;
   bool directed = false;
@@ -348,7 +347,6 @@
           enable_advertiser(id, true, 0, 0);
         } else {
           enabled_sets_[id].advertising_handle_ = id;
-          advertising_sets_[id].pending_start = true;
         }
       } break;
       case (AdvertisingApiType::ANDROID_HCI): {
@@ -374,7 +372,6 @@
           enable_advertiser(id, true, 0, 0);
         } else {
           enabled_sets_[id].advertising_handle_ = id;
-          advertising_sets_[id].pending_start = true;
         }
       } break;
       case (AdvertisingApiType::EXTENDED): {
@@ -485,9 +482,6 @@
     if (!paused) {
       enable_advertiser(id, true, duration, max_ext_adv_events);
     } else {
-      // invoke callbacks upon OnResume()
-      advertising_sets_[id].pending_start = true;
-
       EnabledSet curr_set;
       curr_set.advertising_handle_ = id;
       curr_set.duration_ = duration;
@@ -927,11 +921,6 @@
     std::vector<EnabledSet> enabled_sets = {curr_set};
     Enable enable_value = enable ? Enable::ENABLED : Enable::DISABLED;
 
-    if (enable) {
-      // so that callbacks get invoked on command complete
-      advertising_sets_[advertiser_id].pending_start = true;
-    }
-
     switch (advertising_api_type_) {
       case (AdvertisingApiType::LEGACY): {
         le_advertising_interface_->EnqueueCommand(
@@ -940,22 +929,29 @@
                 this,
                 &impl::on_set_advertising_enable_complete<LeSetAdvertisingEnableCompleteView>,
                 enable,
-                enabled_sets));
+                enabled_sets,
+                true /* trigger callbacks */));
       } break;
       case (AdvertisingApiType::ANDROID_HCI): {
         le_advertising_interface_->EnqueueCommand(
             hci::LeMultiAdvtSetEnableBuilder::Create(enable_value, advertiser_id),
             module_handler_->BindOnceOn(
-                this, &impl::on_set_advertising_enable_complete<LeMultiAdvtCompleteView>, enable, enabled_sets));
+                this,
+                &impl::on_set_advertising_enable_complete<LeMultiAdvtCompleteView>,
+                enable,
+                enabled_sets,
+                true /* trigger callbacks */));
       } break;
       case (AdvertisingApiType::EXTENDED): {
         le_advertising_interface_->EnqueueCommand(
             hci::LeSetExtendedAdvertisingEnableBuilder::Create(enable_value, enabled_sets),
             module_handler_->BindOnceOn(
                 this,
-                &impl::on_set_extended_advertising_enable_complete<LeSetExtendedAdvertisingEnableCompleteView>,
+                &impl::on_set_extended_advertising_enable_complete<
+                    LeSetExtendedAdvertisingEnableCompleteView>,
                 enable,
-                enabled_sets));
+                enabled_sets,
+                true /* trigger callbacks */));
       } break;
     }
 
@@ -1121,13 +1117,17 @@
         case (AdvertisingApiType::LEGACY): {
           le_advertising_interface_->EnqueueCommand(
               hci::LeSetAdvertisingEnableBuilder::Create(Enable::ENABLED),
-              common::init_flags::trigger_advertising_callbacks_on_first_resume_after_pause_is_enabled()
+              common::init_flags::
+                      trigger_advertising_callbacks_on_first_resume_after_pause_is_enabled()
                   ? module_handler_->BindOnceOn(
                         this,
-                        &impl::on_set_advertising_enable_complete<LeSetAdvertisingEnableCompleteView>,
+                        &impl::on_set_advertising_enable_complete<
+                            LeSetAdvertisingEnableCompleteView>,
                         true,
-                        enabled_sets)
-                  : module_handler_->BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>));
+                        enabled_sets,
+                        false /* trigger_callbacks */)
+                  : module_handler_->BindOnce(
+                        impl::check_status<LeSetAdvertisingEnableCompleteView>));
         } break;
         case (AdvertisingApiType::ANDROID_HCI): {
           for (size_t i = 0; i < enabled_sets_.size(); i++) {
@@ -1135,12 +1135,14 @@
             if (id != kInvalidHandle) {
               le_advertising_interface_->EnqueueCommand(
                   hci::LeMultiAdvtSetEnableBuilder::Create(Enable::ENABLED, id),
-                  common::init_flags::trigger_advertising_callbacks_on_first_resume_after_pause_is_enabled()
+                  common::init_flags::
+                          trigger_advertising_callbacks_on_first_resume_after_pause_is_enabled()
                       ? module_handler_->BindOnceOn(
                             this,
                             &impl::on_set_advertising_enable_complete<LeMultiAdvtCompleteView>,
                             true,
-                            enabled_sets)
+                            enabled_sets,
+                            false /* trigger_callbacks */)
                       : module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
             }
           }
@@ -1149,14 +1151,17 @@
           if (enabled_sets.size() != 0) {
             le_advertising_interface_->EnqueueCommand(
                 hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets),
-                common::init_flags::trigger_advertising_callbacks_on_first_resume_after_pause_is_enabled()
+                common::init_flags::
+                        trigger_advertising_callbacks_on_first_resume_after_pause_is_enabled()
                     ? module_handler_->BindOnceOn(
                           this,
                           &impl::on_set_extended_advertising_enable_complete<
                               LeSetExtendedAdvertisingEnableCompleteView>,
                           true,
-                          enabled_sets)
-                    : module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
+                          enabled_sets,
+                          false /* trigger_callbacks */)
+                    : module_handler_->BindOnce(
+                          impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
           }
         } break;
       }
@@ -1223,7 +1228,11 @@
   }
 
   template <class View>
-  void on_set_advertising_enable_complete(bool enable, std::vector<EnabledSet> enabled_sets, CommandCompleteView view) {
+  void on_set_advertising_enable_complete(
+      bool enable,
+      std::vector<EnabledSet> enabled_sets,
+      bool trigger_callbacks,
+      CommandCompleteView view) {
     ASSERT(view.IsValid());
     auto complete_view = View::Create(view);
     ASSERT(complete_view.IsValid());
@@ -1252,15 +1261,9 @@
       }
 
       if (started) {
-        // This event can be triggered from OnPause / OnResume. If so, only invoke callbacks if we were initially paused
-        // (pending_start) after an API invocation (i.e. StartAdvertising -> currently paused -> OnResume -> enabled
-        // [callback], but StartAdvertising -> enabled [callback] -> resumed -> OnPause -> OnResume [NO callback]
-        if (!advertising_sets_[enabled_set.advertising_handle_].pending_start) {
-          continue;
+        if (trigger_callbacks) {
+          advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status);
         }
-        advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status);
-        // since we have started, we are no longer pending
-        advertising_sets_[enabled_set.advertising_handle_].pending_start = false;
       } else {
         advertising_sets_[enabled_set.advertising_handle_].started = true;
         advertising_callbacks_->OnAdvertisingSetStarted(reg_id, id, le_physical_channel_tx_power_, advertising_status);
@@ -1270,7 +1273,10 @@
 
   template <class View>
   void on_set_extended_advertising_enable_complete(
-      bool enable, std::vector<EnabledSet> enabled_sets, CommandCompleteView view) {
+      bool enable,
+      std::vector<EnabledSet> enabled_sets,
+      bool trigger_callbacks,
+      CommandCompleteView view) {
     ASSERT(view.IsValid());
     auto complete_view = LeSetExtendedAdvertisingEnableCompleteView::Create(view);
     ASSERT(complete_view.IsValid());
@@ -1302,15 +1308,9 @@
       }
 
       if (started) {
-        // This event can be triggered from OnPause / OnResume. If so, only invoke callbacks if we were initially paused
-        // (pending_start) after an API invocation (i.e. StartAdvertising -> currently paused -> OnResume -> enabled
-        // [callback], but StartAdvertising -> enabled [callback] -> resumed -> OnPause -> OnResume [NO callback]
-        if (!advertising_sets_[enabled_set.advertising_handle_].pending_start) {
-          continue;
+        if (trigger_callbacks) {
+          advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status);
         }
-        advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status);
-        // since we have started, we are no longer pending
-        advertising_sets_[enabled_set.advertising_handle_].pending_start = false;
       } else {
         advertising_sets_[enabled_set.advertising_handle_].started = true;
         advertising_callbacks_->OnAdvertisingSetStarted(reg_id, id, tx_power, advertising_status);
diff --git a/system/gd/hci/le_advertising_manager_test.cc b/system/gd/hci/le_advertising_manager_test.cc
index 6b10971..c7621c9 100644
--- a/system/gd/hci/le_advertising_manager_test.cc
+++ b/system/gd/hci/le_advertising_manager_test.cc
@@ -36,8 +36,13 @@
 namespace hci {
 namespace {
 
+using namespace std::literals;
+
 using packet::RawBuilder;
 
+using testing::_;
+using testing::InSequence;
+
 class TestController : public Controller {
  public:
   bool IsSupported(OpCode op_code) const override {
@@ -228,11 +233,7 @@
   void on_set_terminated(ErrorCode error_code, uint8_t, uint8_t) {}
 
   void sync_client_handler() {
-    std::promise<void> promise;
-    auto future = promise.get_future();
-    client_handler_->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
-    auto future_status = future.wait_for(std::chrono::seconds(1));
-    ASSERT_EQ(future_status, std::future_status::ready);
+    ASSERT(thread_.GetReactor()->WaitForIdle(2s));
   }
 
   class MockAdvertisingCallback : public AdvertisingCallback {
@@ -1003,6 +1004,47 @@
   test_hci_layer_->IncomingEvent(LeSetExtendedAdvertisingEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
 }
 
+TEST_F(LeExtendedAdvertisingAPITest, disable_after_enable) {
+  // we expect Started -> Enable(false) -> Enable(true) -> Enable(false)
+
+  // setup already arranges everything and starts the advertiser
+
+  // expect
+  InSequence s;
+  EXPECT_CALL(mock_advertising_callback_, OnAdvertisingEnabled(_, false, _));
+  EXPECT_CALL(mock_advertising_callback_, OnAdvertisingEnabled(_, true, _));
+  EXPECT_CALL(mock_advertising_callback_, OnAdvertisingEnabled(_, false, _));
+  EXPECT_CALL(mock_advertising_callback_, OnAdvertisingEnabled(_, true, _));
+
+  // act
+
+  // disable
+  le_advertising_manager_->EnableAdvertiser(advertiser_id_, false, 0x00, 0x00);
+  test_hci_layer_->GetCommand();
+  test_hci_layer_->IncomingEvent(
+      LeSetExtendedAdvertisingEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+
+  // enable
+  le_advertising_manager_->EnableAdvertiser(advertiser_id_, true, 0x00, 0x00);
+  test_hci_layer_->GetCommand();
+  test_hci_layer_->IncomingEvent(
+      LeSetExtendedAdvertisingEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+
+  // disable
+  le_advertising_manager_->EnableAdvertiser(advertiser_id_, false, 0x00, 0x00);
+  test_hci_layer_->GetCommand();
+  test_hci_layer_->IncomingEvent(
+      LeSetExtendedAdvertisingEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+
+  // enable
+  le_advertising_manager_->EnableAdvertiser(advertiser_id_, true, 0x00, 0x00);
+  test_hci_layer_->GetCommand();
+  test_hci_layer_->IncomingEvent(
+      LeSetExtendedAdvertisingEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
+
+  sync_client_handler();
+}
+
 TEST_F(LeExtendedAdvertisingAPITest, set_periodic_parameter) {
   PeriodicAdvertisingParameters advertising_config{};
   advertising_config.max_interval = 0x1000;
@@ -1132,7 +1174,6 @@
       [](std::promise<uint8_t> promise, uint8_t id, uint8_t _status) { promise.set_value(id); },
       std::move(id_promise)));
   sync_client_handler();
-  LOG_INFO("start");
   auto set_id = id_future.get();
 
   auto status_promise = std::promise<ErrorCode>{};
@@ -1175,6 +1216,46 @@
 
   // assert
   EXPECT_EQ(status_future.get(), ErrorCode::SUCCESS);
+
+  sync_client_handler();
+}
+
+TEST_F(LeExtendedAdvertisingAPITest, no_callbacks_on_pause) {
+  // arrange
+  auto test_le_address_manager = (TestLeAddressManager*)test_acl_manager_->GetLeAddressManager();
+
+  // expect
+  EXPECT_CALL(mock_advertising_callback_, OnAdvertisingEnabled(_, _, _)).Times(0);
+
+  // act
+  LOG_INFO("pause");
+  test_le_address_manager->client_->OnPause();
+  test_hci_layer_->GetCommand();
+  test_hci_layer_->IncomingEvent(
+      LeSetExtendedAdvertisingEnableCompleteBuilder::Create(1, ErrorCode::SUCCESS));
+
+  sync_client_handler();
+}
+
+TEST_F(LeExtendedAdvertisingAPITest, no_callbacks_on_resume) {
+  // arrange
+  auto test_le_address_manager = (TestLeAddressManager*)test_acl_manager_->GetLeAddressManager();
+  test_le_address_manager->client_->OnPause();
+  test_hci_layer_->GetCommand();
+  test_hci_layer_->IncomingEvent(
+      LeSetExtendedAdvertisingEnableCompleteBuilder::Create(1, ErrorCode::SUCCESS));
+  sync_client_handler();
+
+  // expect
+  EXPECT_CALL(mock_advertising_callback_, OnAdvertisingEnabled(_, _, _)).Times(0);
+
+  // act
+  test_le_address_manager->client_->OnResume();
+  test_hci_layer_->GetCommand();
+  test_hci_layer_->IncomingEvent(
+      LeSetExtendedAdvertisingEnableCompleteBuilder::Create(1, ErrorCode::SUCCESS));
+
+  sync_client_handler();
 }
 
 }  // namespace
diff --git a/system/gd/os/handler_unittest.cc b/system/gd/os/handler_unittest.cc
index b8c580f..04f6347 100644
--- a/system/gd/os/handler_unittest.cc
+++ b/system/gd/os/handler_unittest.cc
@@ -70,19 +70,27 @@
   auto closure_started_future = closure_started.get_future();
   std::promise<void> closure_can_continue;
   auto can_continue_future = closure_can_continue.get_future();
+  std::promise<void> closure_finished;
+  auto closure_finished_future = closure_finished.get_future();
   handler_->Post(common::BindOnce(
-      [](int* val, std::promise<void> closure_started, std::future<void> can_continue_future) {
+      [](int* val,
+         std::promise<void> closure_started,
+         std::future<void> can_continue_future,
+         std::promise<void> closure_finished) {
         closure_started.set_value();
         *val = *val + 1;
         can_continue_future.wait();
+        closure_finished.set_value();
       },
       common::Unretained(&val),
       std::move(closure_started),
-      std::move(can_continue_future)));
+      std::move(can_continue_future),
+      std::move(closure_finished)));
   handler_->Post(common::BindOnce([]() { ASSERT_TRUE(false); }));
   closure_started_future.wait();
   handler_->Clear();
   closure_can_continue.set_value();
+  closure_finished_future.wait();
   ASSERT_EQ(val, 1);
 }
 
diff --git a/system/packet/avrcp/get_element_attributes_packet.cc b/system/packet/avrcp/get_element_attributes_packet.cc
index 8634ce0..f08a698 100644
--- a/system/packet/avrcp/get_element_attributes_packet.cc
+++ b/system/packet/avrcp/get_element_attributes_packet.cc
@@ -90,7 +90,7 @@
   return builder;
 }
 
-bool GetElementAttributesResponseBuilder::AddAttributeEntry(
+size_t GetElementAttributesResponseBuilder::AddAttributeEntry(
     AttributeEntry entry) {
   CHECK_LT(entries_.size(), size_t(0xFF))
       << __func__ << ": attribute entry overflow";
@@ -101,15 +101,15 @@
   }
 
   if (entry.empty()) {
-    return false;
+    return 0;
   }
 
   entries_.insert(entry);
-  return true;
+  return entry.size();
 }
 
-bool GetElementAttributesResponseBuilder::AddAttributeEntry(Attribute attribute,
-                                                            std::string value) {
+size_t GetElementAttributesResponseBuilder::AddAttributeEntry(
+    Attribute attribute, const std::string& value) {
   return AddAttributeEntry(AttributeEntry(attribute, value));
 }
 
@@ -120,7 +120,7 @@
     attr_list_size += attribute_entry.size();
   }
 
-  return VendorPacket::kMinSize() + 1 + attr_list_size;
+  return kHeaderSize() + attr_list_size;
 }
 
 bool GetElementAttributesResponseBuilder::Serialize(
diff --git a/system/packet/avrcp/get_element_attributes_packet.h b/system/packet/avrcp/get_element_attributes_packet.h
index e60844c..b1d4c61 100644
--- a/system/packet/avrcp/get_element_attributes_packet.h
+++ b/system/packet/avrcp/get_element_attributes_packet.h
@@ -58,15 +58,21 @@
   using VendorPacket::VendorPacket;
 };
 
+template <class Builder>
+class AttributesResponseBuilderTestUser;
+
 class GetElementAttributesResponseBuilder : public VendorPacketBuilder {
  public:
   virtual ~GetElementAttributesResponseBuilder() = default;
+  using Builder = std::unique_ptr<GetElementAttributesResponseBuilder>;
+  static Builder MakeBuilder(size_t mtu);
 
-  static std::unique_ptr<GetElementAttributesResponseBuilder> MakeBuilder(
-      size_t mtu);
+  size_t AddAttributeEntry(AttributeEntry entry);
+  size_t AddAttributeEntry(Attribute attribute, const std::string& value);
 
-  bool AddAttributeEntry(AttributeEntry entry);
-  bool AddAttributeEntry(Attribute attribute, std::string value);
+  virtual void clear() { entries_.clear(); }
+
+  static constexpr size_t kHeaderSize() { return VendorPacket::kMinSize() + 1; }
 
   virtual size_t size() const override;
   virtual bool Serialize(
@@ -75,6 +81,8 @@
  private:
   std::set<AttributeEntry> entries_;
   size_t mtu_;
+  friend class AttributesResponseBuilderTestUser<
+      GetElementAttributesResponseBuilder>;
 
   GetElementAttributesResponseBuilder(size_t mtu)
       : VendorPacketBuilder(CType::STABLE, CommandPdu::GET_ELEMENT_ATTRIBUTES,
@@ -83,4 +91,4 @@
 };
 
 }  // namespace avrcp
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/system/packet/avrcp/get_item_attributes.cc b/system/packet/avrcp/get_item_attributes.cc
index fb2ba87..0c7afe4 100644
--- a/system/packet/avrcp/get_item_attributes.cc
+++ b/system/packet/avrcp/get_item_attributes.cc
@@ -27,7 +27,8 @@
   return builder;
 }
 
-bool GetItemAttributesResponseBuilder::AddAttributeEntry(AttributeEntry entry) {
+size_t GetItemAttributesResponseBuilder::AddAttributeEntry(
+    AttributeEntry entry) {
   CHECK(entries_.size() < 0xFF);
 
   size_t remaining_space = mtu_ - size();
@@ -36,24 +37,22 @@
   }
 
   if (entry.empty()) {
-    return false;
+    return 0;
   }
 
   entries_.insert(entry);
-  return true;
+  return entry.size();
 }
 
-bool GetItemAttributesResponseBuilder::AddAttributeEntry(Attribute attribute,
-                                                         std::string value) {
+size_t GetItemAttributesResponseBuilder::AddAttributeEntry(
+    Attribute attribute, const std::string& value) {
   return AddAttributeEntry(AttributeEntry(attribute, value));
 }
 
 size_t GetItemAttributesResponseBuilder::size() const {
-  size_t len = BrowsePacket::kMinSize();
-  len += 1;  // Status
-  if (status_ != Status::NO_ERROR) return len;
+  size_t len = kHeaderSize();
+  if (status_ != Status::NO_ERROR) return kErrorHeaderSize();
 
-  len += 1;  // Number of attributes
   for (const auto& entry : entries_) {
     len += entry.size();
   }
diff --git a/system/packet/avrcp/get_item_attributes.h b/system/packet/avrcp/get_item_attributes.h
index aa1db71..7811909 100644
--- a/system/packet/avrcp/get_item_attributes.h
+++ b/system/packet/avrcp/get_item_attributes.h
@@ -23,15 +23,32 @@
 namespace bluetooth {
 namespace avrcp {
 
+template <class Builder>
+class AttributesResponseBuilderTestUser;
+
 class GetItemAttributesResponseBuilder : public BrowsePacketBuilder {
  public:
   virtual ~GetItemAttributesResponseBuilder() = default;
+  using Builder = std::unique_ptr<GetItemAttributesResponseBuilder>;
+  static Builder MakeBuilder(Status status, size_t mtu);
 
-  static std::unique_ptr<GetItemAttributesResponseBuilder> MakeBuilder(
-      Status status, size_t mtu);
+  size_t AddAttributeEntry(AttributeEntry entry);
+  size_t AddAttributeEntry(Attribute, const std::string&);
 
-  bool AddAttributeEntry(AttributeEntry entry);
-  bool AddAttributeEntry(Attribute, std::string);
+  virtual void clear() { entries_.clear(); }
+
+  static constexpr size_t kHeaderSize() {
+    size_t len = BrowsePacket::kMinSize();
+    len += 1;  // Status
+    len += 1;  // Number of attributes
+    return len;
+  }
+
+  static constexpr size_t kErrorHeaderSize() {
+    size_t len = BrowsePacket::kMinSize();
+    len += 1;  // Status
+    return len;
+  }
 
   virtual size_t size() const override;
   virtual bool Serialize(
@@ -41,6 +58,8 @@
   Status status_;
   size_t mtu_;
   std::set<AttributeEntry> entries_;
+  friend class AttributesResponseBuilderTestUser<
+      GetItemAttributesResponseBuilder>;
 
   GetItemAttributesResponseBuilder(Status status, size_t mtu)
       : BrowsePacketBuilder(BrowsePdu::GET_ITEM_ATTRIBUTES),
@@ -81,4 +100,4 @@
 };
 
 }  // namespace avrcp
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/system/packet/tests/avrcp/get_element_attributes_packet_test.cc b/system/packet/tests/avrcp/get_element_attributes_packet_test.cc
index 8a05487..96ae1fa 100644
--- a/system/packet/tests/avrcp/get_element_attributes_packet_test.cc
+++ b/system/packet/tests/avrcp/get_element_attributes_packet_test.cc
@@ -103,6 +103,47 @@
   ASSERT_EQ(test_packet->GetData(), get_elements_attributes_response_full);
 }
 
+TEST(GetElementAttributesResponseBuilderTest, builderMtuTest) {
+  std::vector<AttributeEntry> test_data = {
+      {Attribute::TITLE, "Test Song 1"},
+      {Attribute::ARTIST_NAME, "Test Artist"},
+      {Attribute::ALBUM_NAME, "Test Album"},
+      {Attribute::TRACK_NUMBER, "1"},
+      {Attribute::TOTAL_NUMBER_OF_TRACKS, "2"},
+      {Attribute::GENRE, "Test Genre"},
+      {Attribute::PLAYING_TIME, "10 200"},
+      {Attribute::TITLE, "Test Song 2"},
+      {Attribute::ARTIST_NAME, "Test Artist"},
+      {Attribute::ALBUM_NAME, "Test Album"},
+      {Attribute::TRACK_NUMBER, "2"},
+      {Attribute::TOTAL_NUMBER_OF_TRACKS, "2"},
+      {Attribute::GENRE, "Test Genre"},
+      {Attribute::PLAYING_TIME, "1500"},
+  };
+
+  using Builder = GetElementAttributesResponseBuilder;
+  using Helper = FragmentationBuilderHelper<Builder>;
+  size_t mtu = size_t(-1);
+  Helper helper(mtu, [](size_t mtu) { return Builder::MakeBuilder(mtu); });
+
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu, false, false));
+
+  mtu = test_data[0].size() + Builder::kHeaderSize();
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu));
+
+  mtu = test_data[0].size() + test_data[1].size() + Builder::kHeaderSize();
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu));
+
+  mtu = test_data[0].size() + (Builder::kHeaderSize() * 2) + 1;
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu, true, false));
+
+  mtu = Builder::kHeaderSize() + AttributeEntry::kHeaderSize() + 1;
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu));
+
+  mtu = Builder::kHeaderSize() + AttributeEntry::kHeaderSize();
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu, false, false));
+}
+
 TEST(GetElementAttributesResponseBuilderTest, truncateBuilderTest) {
   auto attribute = AttributeEntry(Attribute::TITLE, "1234");
   size_t truncated_size = VendorPacket::kMinSize();
@@ -130,4 +171,4 @@
 }
 
 }  // namespace avrcp
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/system/packet/tests/avrcp/get_item_attributes_packet_test.cc b/system/packet/tests/avrcp/get_item_attributes_packet_test.cc
index 3d6f691..4f1da7b 100644
--- a/system/packet/tests/avrcp/get_item_attributes_packet_test.cc
+++ b/system/packet/tests/avrcp/get_item_attributes_packet_test.cc
@@ -126,5 +126,48 @@
   ASSERT_FALSE(test_packet->IsValid());
 }
 
+TEST(GetItemAttributesRequestTest, builderMtuTest) {
+  std::vector<AttributeEntry> test_data = {
+      {Attribute::TITLE, "Test Song 1"},
+      {Attribute::ARTIST_NAME, "Test Artist"},
+      {Attribute::ALBUM_NAME, "Test Album"},
+      {Attribute::TRACK_NUMBER, "1"},
+      {Attribute::TOTAL_NUMBER_OF_TRACKS, "2"},
+      {Attribute::GENRE, "Test Genre"},
+      {Attribute::PLAYING_TIME, "10 200"},
+      {Attribute::TITLE, "Test Song 2"},
+      {Attribute::ARTIST_NAME, "Test Artist"},
+      {Attribute::ALBUM_NAME, "Test Album"},
+      {Attribute::TRACK_NUMBER, "2"},
+      {Attribute::TOTAL_NUMBER_OF_TRACKS, "2"},
+      {Attribute::GENRE, "Test Genre"},
+      {Attribute::PLAYING_TIME, "1500"},
+  };
+
+  using Builder = GetItemAttributesResponseBuilder;
+  using Helper = FragmentationBuilderHelper<Builder>;
+  size_t mtu = size_t(-1);
+  Helper helper(mtu, [](size_t mtu) {
+    return Builder::MakeBuilder(Status::NO_ERROR, mtu);
+  });
+
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu, false, false));
+
+  mtu = test_data[0].size() + Builder::kHeaderSize();
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu));
+
+  mtu = test_data[0].size() + test_data[1].size() + Builder::kHeaderSize();
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu));
+
+  mtu = test_data[0].size() + (Builder::kHeaderSize() * 2) + 1;
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu, true, false));
+
+  mtu = Builder::kHeaderSize() + AttributeEntry::kHeaderSize() + 1;
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu));
+
+  mtu = Builder::kHeaderSize() + AttributeEntry::kHeaderSize();
+  EXPECT_NO_FATAL_FAILURE(helper.runTest(test_data, mtu, false, false));
+}
+
 }  // namespace avrcp
-}  // namespace bluetooth
\ No newline at end of file
+}  // namespace bluetooth
diff --git a/system/packet/tests/packet_test_helper.h b/system/packet/tests/packet_test_helper.h
index ad3db2c..e03280c 100644
--- a/system/packet/tests/packet_test_helper.h
+++ b/system/packet/tests/packet_test_helper.h
@@ -16,10 +16,11 @@
 
 #pragma once
 
+#include <list>
 #include <memory>
 
+#include "avrcp_common.h"
 #include "packet.h"
-
 namespace bluetooth {
 
 // A helper templated class to access the protected members of Packet to make
@@ -63,4 +64,229 @@
   }
 };
 
-}  // namespace bluetooth
\ No newline at end of file
+namespace avrcp {
+
+inline std::string to_string(const Attribute& a) {
+  switch (a) {
+    case Attribute::TITLE:
+      return "TITLE";
+    case Attribute::ARTIST_NAME:
+      return "ARTIST_NAME";
+    case Attribute::ALBUM_NAME:
+      return "ALBUM_NAME";
+    case Attribute::TRACK_NUMBER:
+      return "TRACK_NUMBER";
+    case Attribute::TOTAL_NUMBER_OF_TRACKS:
+      return "TOTAL_NUMBER_OF_TRACKS";
+    case Attribute::GENRE:
+      return "GENRE";
+    case Attribute::PLAYING_TIME:
+      return "PLAYING_TIME";
+    case Attribute::DEFAULT_COVER_ART:
+      return "DEFAULT_COVER_ART";
+    default:
+      return "UNKNOWN ATTRIBUTE";
+  };
+}
+
+inline std::string to_string(const AttributeEntry& entry) {
+  std::stringstream ss;
+  ss << to_string(entry.attribute()) << ": " << entry.value();
+  return ss.str();
+}
+
+template <class Container>
+std::string to_string(const Container& entries) {
+  std::stringstream ss;
+  for (const auto& el : entries) {
+    ss << to_string(el) << std::endl;
+  }
+  return ss.str();
+}
+
+inline bool operator==(const AttributeEntry& a, const AttributeEntry& b) {
+  return (a.attribute() == b.attribute()) && (a.value() == b.value());
+}
+
+inline bool operator!=(const AttributeEntry& a, const AttributeEntry& b) {
+  return !(a == b);
+}
+
+template <class AttributesResponseBuilder>
+class AttributesResponseBuilderTestUser {
+ public:
+  using Builder = AttributesResponseBuilder;
+  using Maker = std::function<typename Builder::Builder(size_t)>;
+
+ private:
+  Maker maker;
+  typename Builder::Builder _builder;
+  size_t _mtu;
+  size_t _current_size = 0;
+  size_t _entry_counter = 0;
+  std::set<AttributeEntry> _control_set;
+  std::list<AttributeEntry> _order_control;
+  std::list<AttributeEntry> _sended_order;
+  std::stringstream _report;
+  bool _test_result = true;
+  bool _order_test_result = true;
+
+  void reset() {
+    for (const auto& en : _builder->entries_) {
+      _sended_order.push_back(en);
+    }
+    _current_size = 0, _entry_counter = 0;
+    _control_set.clear();
+    _builder->clear();
+  }
+
+  size_t expected_size() { return Builder::kHeaderSize() + _current_size; }
+
+ public:
+  std::string getReport() const { return _report.str(); }
+
+  AttributesResponseBuilderTestUser(size_t m_size, Maker maker)
+      : maker(maker), _builder(maker(m_size)), _mtu(m_size) {
+    _report << __func__ << ": mtu \"" << _mtu << "\"\n";
+  }
+
+  void startTest(size_t m_size) {
+    _builder = maker(m_size);
+    _mtu = m_size;
+    reset();
+    _report.str("");
+    _report.clear();
+    _order_control.clear();
+    _sended_order.clear();
+    _report << __func__ << ": mtu \"" << _mtu << "\"\n";
+    _order_test_result = true;
+    _test_result = true;
+  }
+
+  bool testResult() const { return _test_result; }
+
+  bool testOrder() { return _order_test_result; }
+
+  void finishTest() {
+    reset();
+    if (_order_control.size() != _sended_order.size()) {
+      _report << __func__ << ": testOrder FAIL: "
+              << "the count of entries which should send ("
+              << _order_control.size() << ") is not equal to sended entries("
+              << _sended_order.size() << ")) \n input:\n "
+              << to_string(_order_control) << "\n sended:\n"
+              << to_string(_sended_order) << "\n";
+      _order_test_result = false;
+      return;
+    }
+    auto e = _order_control.begin();
+    auto s = _sended_order.begin();
+    for (; e != _order_control.end(); ++e, ++s) {
+      if (*e != *s) {
+        _report << __func__ << "testOrder FAIL: order of entries was changed\n";
+        _order_test_result = false;
+        break;
+      }
+    }
+    _report << __func__ << ": mtu \"" << _mtu << "\"\n";
+  }
+
+  void AddAttributeEntry(AttributeEntry entry) {
+    auto f = _builder->AddAttributeEntry(entry);
+    if (f != 0) {
+      _current_size += f;
+      ++_entry_counter;
+    }
+    if (f == entry.size()) {
+      wholeEntry(f, std::move(entry));
+    } else {
+      fractionEntry(f, std::move(entry));
+    }
+  }
+
+ private:
+  void wholeEntry(size_t f, AttributeEntry&& entry) {
+    _control_set.insert(entry);
+    _order_control.push_back(entry);
+    if (_builder->size() != expected_size()) {
+      _report << __func__ << "FAIL for \"" << to_string(entry)
+              << "\": not allowed to add.\n";
+      _test_result = false;
+    }
+  }
+
+  void fractionEntry(size_t f, AttributeEntry&& entry) {
+    auto l_value = entry.value().size() - (entry.size() - f);
+    if (f != 0) {
+      auto pushed_entry = AttributeEntry(
+          entry.attribute(), std::string(entry.value(), 0, l_value));
+      _control_set.insert(pushed_entry);
+      _order_control.push_back(pushed_entry);
+    }
+
+    if (expected_size() != _builder->size()) {
+      _test_result = false;
+      _report << __func__ << "FAIL for \"" << to_string(entry)
+              << "\": not allowed to add.\n";
+    }
+
+    if (_builder->size() != expected_size() ||
+        _builder->entries_.size() != _entry_counter) {
+      _report << __func__ << "FAIL for \"" << to_string(entry)
+              << "\": unexpected size of packet\n";
+      _test_result = false;
+    }
+    for (auto dat = _builder->entries_.begin(), ex = _control_set.begin();
+         ex != _control_set.end(); ++dat, ++ex) {
+      if (*dat != *ex) {
+        _report << __func__ << "FAIL for \"" << to_string(entry)
+                << "\": unexpected entry order\n";
+        _test_result = false;
+      }
+    }
+    auto tail = (f == 0) ? entry
+                         : AttributeEntry(entry.attribute(),
+                                          std::string(entry.value(), l_value));
+    if (_builder->entries_.size() != 0) {
+      reset();
+      AddAttributeEntry(tail);
+    }
+    if (_builder->entries_.size() == 0) {
+      _report << __func__ << "FAIL: MTU " << _mtu << " too small\n";
+      _test_result = false;
+      _order_control.push_back(entry);
+      reset();
+    }
+  }
+};
+
+template <class AttributesBuilder>
+class FragmentationBuilderHelper {
+ public:
+  using Builder = AttributesBuilder;
+  using Helper = AttributesResponseBuilderTestUser<Builder>;
+  using Maker = typename Helper::Maker;
+
+  FragmentationBuilderHelper(size_t mtu, Maker m) : _helper(mtu, m) {}
+
+  template <class TestCollection>
+  void runTest(const TestCollection& test_data, size_t mtu,
+               bool expect_fragmentation = true, bool expect_ordering = true) {
+    _helper.startTest(mtu);
+
+    for (auto& i : test_data) {
+      _helper.AddAttributeEntry(i);
+    }
+    _helper.finishTest();
+
+    EXPECT_EQ(expect_fragmentation, _helper.testResult())
+        << "Report: " << _helper.getReport();
+    EXPECT_EQ(expect_ordering, _helper.testOrder())
+        << "Report: " << _helper.getReport();
+  }
+
+ private:
+  Helper _helper;
+};
+}  // namespace avrcp
+}  // namespace bluetooth
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index 73b8a53..9bc8dda 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -978,6 +978,7 @@
         "btm/btm_sec.cc",
         "metrics/stack_metrics_logging.cc",
         "test/btm/stack_btm_test.cc",
+        "test/btm/stack_btm_regression_tests.cc",
         "test/btm/peer_packet_types_test.cc",
         "test/common/mock_eatt.cc",
     ],
diff --git a/system/stack/a2dp/a2dp_sbc.cc b/system/stack/a2dp/a2dp_sbc.cc
index c5d5a15..5a95099 100644
--- a/system/stack/a2dp/a2dp_sbc.cc
+++ b/system/stack/a2dp/a2dp_sbc.cc
@@ -696,6 +696,11 @@
     return false;
   }
 
+  // there is an 4-byte timestamp right following p_buf
+  if (p_buf->offset < 4 + A2DP_SBC_MPL_HDR_LEN) {
+    return false;
+  }
+
   p_buf->offset -= A2DP_SBC_MPL_HDR_LEN;
   uint8_t* p = (uint8_t*)(p_buf + 1) + p_buf->offset;
   p_buf->len += A2DP_SBC_MPL_HDR_LEN;
diff --git a/system/stack/a2dp/a2dp_vendor_ldac.cc b/system/stack/a2dp/a2dp_vendor_ldac.cc
index b6aaee5..e8430ff 100644
--- a/system/stack/a2dp/a2dp_vendor_ldac.cc
+++ b/system/stack/a2dp/a2dp_vendor_ldac.cc
@@ -518,6 +518,11 @@
                                      uint16_t frames_per_packet) {
   uint8_t* p;
 
+  // there is a 4 byte timestamp right following p_buf
+  if (p_buf->offset < 4 + A2DP_LDAC_MPL_HDR_LEN) {
+    return false;
+  }
+
   p_buf->offset -= A2DP_LDAC_MPL_HDR_LEN;
   p = (uint8_t*)(p_buf + 1) + p_buf->offset;
   p_buf->len += A2DP_LDAC_MPL_HDR_LEN;
diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc
index 0d49c41..c6264dd 100644
--- a/system/stack/acl/btm_acl.cc
+++ b/system/stack/acl/btm_acl.cc
@@ -630,20 +630,6 @@
     return;
   }
 
-  /* if we are trying to drop encryption on an encrypted connection, drop the
-   * connection */
-  if (p->is_encrypted && !encr_enable) {
-    LOG(ERROR) << __func__
-               << " attempting to decrypt encrypted connection, disconnecting. "
-                  "handle: "
-               << loghex(handle);
-
-    acl_disconnect_from_handle(handle, HCI_ERR_HOST_REJECT_SECURITY,
-                               "stack::btu::btu_hcif::read_drop_encryption "
-                               "Connection Already Encrypted");
-    return;
-  }
-
   p->is_encrypted = encr_enable;
 
   /* Process Role Switch if active */
@@ -1768,7 +1754,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_read_tx_power_complete(uint8_t* p, bool is_ble) {
+void btm_read_tx_power_complete(uint8_t* p, uint16_t evt_len, bool is_ble) {
   tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_tx_power_cmpl_cb;
   tBTM_TX_POWER_RESULT result;
 
@@ -1777,6 +1763,10 @@
 
   /* If there was a registered callback, call it */
   if (p_cb) {
+    if (evt_len < 1) {
+      goto err_out;
+    }
+
     STREAM_TO_UINT8(result.hci_status, p);
 
     if (result.hci_status == HCI_SUCCESS) {
@@ -1784,6 +1774,11 @@
 
       if (!is_ble) {
         uint16_t handle;
+
+        if (evt_len < 4) {
+          goto err_out;
+        }
+
         STREAM_TO_UINT16(handle, p);
         STREAM_TO_UINT8(result.tx_power, p);
 
@@ -1792,6 +1787,10 @@
           result.rem_bda = p_acl_cb->remote_addr;
         }
       } else {
+        if (evt_len < 2) {
+          goto err_out;
+        }
+
         STREAM_TO_UINT8(result.tx_power, p);
         result.rem_bda = btm_cb.devcb.read_tx_pwr_addr;
       }
@@ -1805,6 +1804,11 @@
 
     (*p_cb)(&result);
   }
+
+  return;
+
+ err_out:
+  LOG_ERROR("Bogus event packet, too short");
 }
 
 /*******************************************************************************
@@ -1834,7 +1838,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_read_rssi_complete(uint8_t* p) {
+void btm_read_rssi_complete(uint8_t* p, uint16_t evt_len) {
   tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_rssi_cmpl_cb;
   tBTM_RSSI_RESULT result;
 
@@ -1843,11 +1847,19 @@
 
   /* If there was a registered callback, call it */
   if (p_cb) {
+    if (evt_len < 1) {
+      goto err_out;
+    }
+
     STREAM_TO_UINT8(result.hci_status, p);
     result.status = BTM_ERR_PROCESSING;
 
     if (result.hci_status == HCI_SUCCESS) {
       uint16_t handle;
+
+      if (evt_len < 4) {
+        goto err_out;
+      }
       STREAM_TO_UINT16(handle, p);
 
       STREAM_TO_UINT8(result.rssi, p);
@@ -1863,6 +1875,11 @@
     }
     (*p_cb)(&result);
   }
+
+  return;
+
+err_out:
+  LOG_ERROR("Bogus event packet, too short");
 }
 
 /*******************************************************************************
@@ -1997,7 +2014,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_read_link_quality_complete(uint8_t* p) {
+void btm_read_link_quality_complete(uint8_t* p, uint16_t evt_len) {
   tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_link_qual_cmpl_cb;
   tBTM_LINK_QUALITY_RESULT result;
 
@@ -2006,12 +2023,20 @@
 
   /* If there was a registered callback, call it */
   if (p_cb) {
+    if (evt_len < 1) {
+      goto err_out;
+    }
+
     STREAM_TO_UINT8(result.hci_status, p);
 
     if (result.hci_status == HCI_SUCCESS) {
       uint16_t handle;
       result.status = BTM_SUCCESS;
 
+      if (evt_len < 4) {
+        goto err_out;
+      }
+
       STREAM_TO_UINT16(handle, p);
 
       STREAM_TO_UINT8(result.link_quality, p);
@@ -2030,6 +2055,11 @@
 
     (*p_cb)(&result);
   }
+
+  return;
+
+err_out:
+  LOG_ERROR("Bogus Link Quality event packet, size: %d", evt_len);
 }
 
 /*******************************************************************************
diff --git a/system/stack/btm/ble_scanner_hci_interface.cc b/system/stack/btm/ble_scanner_hci_interface.cc
index 5d3686b..2e521f2 100644
--- a/system/stack/btm/ble_scanner_hci_interface.cc
+++ b/system/stack/btm/ble_scanner_hci_interface.cc
@@ -418,6 +418,11 @@
                                                 uint8_t* data) {
   uint16_t sync_handle;
 
+  if (data_len < 2) {
+    LOG(ERROR) << "Bogus event packet, too short";
+    return;
+  }
+
   STREAM_TO_UINT16(sync_handle, data);
 
   if (BleScannerHciInterface::Get()) {
diff --git a/system/stack/btm/btm_ble.cc b/system/stack/btm/btm_ble.cc
index 0e59a1f..2c77945 100644
--- a/system/stack/btm/btm_ble.cc
+++ b/system/stack/btm/btm_ble.cc
@@ -1019,7 +1019,8 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_ble_rand_enc_complete(uint8_t* p, uint16_t op_code,
+void btm_ble_rand_enc_complete(uint8_t* p, uint16_t evt_len,
+                               uint16_t op_code,
                                tBTM_RAND_ENC_CB* p_enc_cplt_cback) {
   tBTM_RAND_ENC params;
   uint8_t* p_dest = params.param_buf;
@@ -1030,6 +1031,11 @@
 
   /* If there was a callback address for vcs complete, call it */
   if (p_enc_cplt_cback && p) {
+
+    if (evt_len < 1) {
+      goto err_out;
+    }
+
     /* Pass paramters to the callback function */
     STREAM_TO_UINT8(params.status, p); /* command status */
 
@@ -1041,12 +1047,21 @@
       else
         params.param_len = OCTET16_LEN;
 
+      if (evt_len < 1 + params.param_len) {
+        goto err_out;
+      }
+
       /* Fetch return info from HCI event message */
       memcpy(p_dest, p, params.param_len);
     }
     if (p_enc_cplt_cback) /* Call the Encryption complete callback function */
       (*p_enc_cplt_cback)(&params);
   }
+
+  return;
+
+err_out:
+  BTM_TRACE_ERROR("%s malformatted event packet, too short", __func__);
 }
 
 /*******************************************************************************
diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc
index 5a5fd84..5aa30e7 100644
--- a/system/stack/btm/btm_ble_gap.cc
+++ b/system/stack/btm/btm_ble_gap.cc
@@ -2393,9 +2393,7 @@
       has_advertising_flags = true;
       p_cur->flag = *p_flag;
     }
-  }
 
-  if (!data.empty()) {
     /* Check to see the BLE device has the Appearance UUID in the advertising
      * data.  If it does
      * then try to convert the appearance value to a class of device value
@@ -2425,14 +2423,31 @@
         }
       }
     }
-  }
 
-  if (!data.empty()) {
     const uint8_t* p_rsi =
         AdvertiseDataParser::GetFieldByType(data, BTM_BLE_AD_TYPE_RSI, &len);
     if (p_rsi != nullptr && len == 6) {
       STREAM_TO_BDADDR(p_cur->ble_ad_rsi, p_rsi);
     }
+
+    const uint8_t* p_service_data = data.data();
+    uint8_t service_data_len = 0;
+
+    while ((p_service_data = AdvertiseDataParser::GetFieldByType(
+                p_service_data + service_data_len,
+                data.size() - (p_service_data - data.data()) - service_data_len,
+                BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE, &service_data_len))) {
+      uint16_t uuid;
+      STREAM_TO_UINT16(uuid, p_service_data);
+
+      if (uuid == 0x184E /* Audio Stream Control service */ ||
+          uuid == 0x184F /* Broadcast Audio Scan service */ ||
+          uuid == 0x1850 /* Published Audio Capabilities service */ ||
+          uuid == 0x1853 /* Common Audio service */) {
+        p_cur->ble_ad_is_le_audio_capable = true;
+        break;
+      }
+    }
   }
 
   // Non-connectable packets may omit flags entirely, in which case nothing
@@ -3181,9 +3196,14 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_ble_read_remote_features_complete(uint8_t* p) {
+void btm_ble_read_remote_features_complete(uint8_t* p, uint8_t length) {
   uint16_t handle;
   uint8_t status;
+
+  if (length < 3) {
+    goto err_out;
+  }
+
   STREAM_TO_UINT8(status, p);
   STREAM_TO_UINT16(handle, p);
   handle = handle & 0x0FFF;  // only 12 bits meaningful
@@ -3198,6 +3218,12 @@
   }
 
   if (status == HCI_SUCCESS) {
+    // BD_FEATURES_LEN additional bytes are read
+    // in acl_set_peer_le_features_from_handle
+    if (length < 3 + BD_FEATURES_LEN) {
+      goto err_out;
+    }
+
     if (!acl_set_peer_le_features_from_handle(handle, p)) {
       LOG_ERROR(
           "Unable to find existing connection after read remote features");
@@ -3206,6 +3232,10 @@
   }
 
   btsnd_hcic_rmt_ver_req(handle);
+  return;
+
+err_out:
+  LOG_ERROR("bogus event packet, too short");
 }
 
 /*******************************************************************************
@@ -3217,11 +3247,11 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_ble_write_adv_enable_complete(uint8_t* p) {
+void btm_ble_write_adv_enable_complete(uint8_t* p, uint16_t evt_len) {
   tBTM_BLE_INQ_CB* p_cb = &btm_cb.ble_ctr_cb.inq_var;
 
   /* if write adv enable/disbale not succeed */
-  if (*p != HCI_SUCCESS) {
+  if (evt_len < 1 || *p != HCI_SUCCESS) {
     /* toggle back the adv mode */
     p_cb->adv_mode = !p_cb->adv_mode;
   }
diff --git a/system/stack/btm/btm_ble_privacy.cc b/system/stack/btm/btm_ble_privacy.cc
index b2f26fa..6dafc4a 100644
--- a/system/stack/btm/btm_ble_privacy.cc
+++ b/system/stack/btm/btm_ble_privacy.cc
@@ -224,6 +224,12 @@
  ******************************************************************************/
 void btm_ble_clear_resolving_list_complete(uint8_t* p, uint16_t evt_len) {
   uint8_t status = 0;
+
+  if (evt_len < 1) {
+    BTM_TRACE_ERROR("malformatted event packet: containing zero bytes");
+    return;
+  }
+
   STREAM_TO_UINT8(status, p);
 
   BTM_TRACE_DEBUG("%s status=%d", __func__, status);
@@ -268,6 +274,12 @@
  ******************************************************************************/
 void btm_ble_add_resolving_list_entry_complete(uint8_t* p, uint16_t evt_len) {
   uint8_t status;
+
+  if (evt_len < 1) {
+    BTM_TRACE_ERROR("malformatted event packet: containing zero bytes");
+    return;
+  }
+
   STREAM_TO_UINT8(status, p);
 
   BTM_TRACE_DEBUG("%s status = %d", __func__, status);
diff --git a/system/stack/btm/btm_devctl.cc b/system/stack/btm/btm_devctl.cc
index b9fb846..30f2dda 100644
--- a/system/stack/btm/btm_devctl.cc
+++ b/system/stack/btm/btm_devctl.cc
@@ -559,18 +559,20 @@
   const uint8_t* bqr_ptr = p;
   uint8_t event_code;
   uint8_t len;
-  STREAM_TO_UINT8(event_code, bqr_ptr);
-  STREAM_TO_UINT8(len, bqr_ptr);
-  // Check if there's at least a subevent code
-  if (len > 1 && evt_len > 1 && event_code == HCI_VENDOR_SPECIFIC_EVT) {
-    uint8_t sub_event_code;
-    STREAM_TO_UINT8(sub_event_code, bqr_ptr);
-    if (sub_event_code == HCI_VSE_SUBCODE_BQR_SUB_EVT) {
-      // Excluding the HCI Event packet header and 1 octet sub-event code
-      int16_t bqr_parameter_length = evt_len - HCIE_PREAMBLE_SIZE - 1;
-      const uint8_t* p_bqr_event = bqr_ptr;
-      // The stream currently points to the BQR sub-event parameters
-      switch (sub_event_code) {
+
+  if (evt_len >= 2) {
+    STREAM_TO_UINT8(event_code, bqr_ptr);
+    STREAM_TO_UINT8(len, bqr_ptr);
+    // Check if there's at least a subevent code
+    if (len > 1 && evt_len >= 2 + 1 && event_code == HCI_VENDOR_SPECIFIC_EVT) {
+      uint8_t sub_event_code;
+      STREAM_TO_UINT8(sub_event_code, bqr_ptr);
+      if (sub_event_code == HCI_VSE_SUBCODE_BQR_SUB_EVT) {
+        // Excluding the HCI Event packet header and 1 octet sub-event code
+        int16_t bqr_parameter_length = evt_len - HCIE_PREAMBLE_SIZE - 1;
+        const uint8_t* p_bqr_event = bqr_ptr;
+        // The stream currently points to the BQR sub-event parameters
+        switch (sub_event_code) {
         case bluetooth::bqr::QUALITY_REPORT_ID_LMP_LL_MESSAGE_TRACE:
           if (bqr_parameter_length >= bluetooth::bqr::kLogDumpParamTotalLen) {
             bluetooth::bqr::DumpLmpLlMessage(bqr_parameter_length, p_bqr_event);
@@ -591,6 +593,7 @@
 
         default:
           LOG_INFO("Unhandled BQR subevent 0x%02hxx", sub_event_code);
+        }
       }
     }
   }
@@ -721,7 +724,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_delete_stored_link_key_complete(uint8_t* p) {
+void btm_delete_stored_link_key_complete(uint8_t* p, uint16_t evt_len) {
   tBTM_CMPL_CB* p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb;
   tBTM_DELETE_STORED_LINK_KEY_COMPLETE result;
 
@@ -732,6 +735,11 @@
     /* Set the call back event to indicate command complete */
     result.event = BTM_CB_EVT_DELETE_STORED_LINK_KEYS;
 
+    if (evt_len < 3) {
+      LOG(ERROR) << __func__ << "Malformatted event packet, too short";
+      return;
+    }
+
     /* Extract the result fields from the HCI event */
     STREAM_TO_UINT8(result.status, p);
     STREAM_TO_UINT16(result.num_keys, p);
diff --git a/system/stack/btm/btm_iso_impl.h b/system/stack/btm/btm_iso_impl.h
index 358296e..0abe21f 100644
--- a/system/stack/btm/btm_iso_impl.h
+++ b/system/stack/btm/btm_iso_impl.h
@@ -25,6 +25,7 @@
 #include "base/callback.h"
 #include "base/logging.h"
 #include "bind_helpers.h"
+#include "btm_dev.h"
 #include "btm_iso_api.h"
 #include "common/time_util.h"
 #include "device/include/controller.h"
@@ -33,6 +34,7 @@
 #include "osi/include/allocator.h"
 #include "osi/include/log.h"
 #include "stack/include/bt_hdr.h"
+#include "stack/include/btm_log_history.h"
 #include "stack/include/hci_error_code.h"
 #include "stack/include/hcidefs.h"
 
@@ -49,6 +51,8 @@
 static constexpr uint8_t kStateFlagHasDataPathSet = 0x04;
 static constexpr uint8_t kStateFlagIsBroadcast = 0x10;
 
+constexpr char kBtmLogTag[] = "ISO";
+
 struct iso_sync_info {
   uint32_t first_sync_ts;
   uint16_t seq_nb;
@@ -118,6 +122,12 @@
     uint8_t evt_code = IsCigKnown(cig_id) ? kIsoEventCigOnReconfigureCmpl
                                           : kIsoEventCigOnCreateCmpl;
 
+    BTM_LogHistory(
+        kBtmLogTag, RawAddress::kEmpty, "CIG Create complete",
+        base::StringPrintf(
+            "cig_id:0x%02x, status: %s", evt.cig_id,
+            hci_status_code_text((tHCI_STATUS)(evt.status)).c_str()));
+
     if (evt.status == HCI_SUCCESS) {
       LOG_ASSERT(len >= (3) + (cis_cnt * sizeof(uint16_t)))
           << "Invalid CIS count: " << +cis_cnt;
@@ -164,6 +174,11 @@
         cig_params.cis_cfgs.size(), cig_params.cis_cfgs.data(),
         base::BindOnce(&iso_impl::on_set_cig_params, base::Unretained(this),
                        cig_id, cig_params.sdu_itv_mtos));
+
+    BTM_LogHistory(
+        kBtmLogTag, RawAddress::kEmpty, "CIG Create",
+        base::StringPrintf("cig_id:0x%02x, size: %d", cig_id,
+                           static_cast<int>(cig_params.cis_cfgs.size())));
   }
 
   void reconfigure_cig(uint8_t cig_id,
@@ -188,6 +203,12 @@
     STREAM_TO_UINT8(evt.status, stream);
     STREAM_TO_UINT8(evt.cig_id, stream);
 
+    BTM_LogHistory(
+        kBtmLogTag, RawAddress::kEmpty, "CIG Remove complete",
+        base::StringPrintf(
+            "cig_id:0x%02x, status: %s", evt.cig_id,
+            hci_status_code_text((tHCI_STATUS)(evt.status)).c_str()));
+
     if (evt.status == HCI_SUCCESS) {
       auto cis_it = conn_hdl_to_cis_map_.cbegin();
       while (cis_it != conn_hdl_to_cis_map_.cend()) {
@@ -210,6 +231,8 @@
 
     btsnd_hcic_remove_cig(cig_id, base::BindOnce(&iso_impl::on_remove_cig,
                                                  base::Unretained(this)));
+    BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty, "CIG Remove",
+                   base::StringPrintf("cig_id:0x%02x (f:%d)", cig_id, force));
   }
 
   void on_status_establish_cis(
@@ -234,6 +257,14 @@
         evt.cig_id = 0xFF;
         cis->state_flags &= ~kStateFlagIsConnecting;
         cig_callbacks_->OnCisEvent(kIsoEventCisEstablishCmpl, &evt);
+
+        BTM_LogHistory(
+            kBtmLogTag, cis_hdl_to_addr[evt.cis_conn_hdl],
+            "Establish CIS failed ",
+            base::StringPrintf(
+                "handle:0x%04x, status: %s", evt.cis_conn_hdl,
+                hci_status_code_text((tHCI_STATUS)(status)).c_str()));
+        cis_hdl_to_addr.erase(evt.cis_conn_hdl);
       }
     }
   }
@@ -246,6 +277,13 @@
                    (kStateFlagIsConnected | kStateFlagIsConnecting)))
           << "Already connected or connecting";
       cis->state_flags |= kStateFlagIsConnecting;
+
+      tBTM_SEC_DEV_REC* p_rec = btm_find_dev_by_handle(el.acl_conn_handle);
+      if (p_rec) {
+        cis_hdl_to_addr[el.cis_conn_handle] = p_rec->ble.pseudo_addr;
+        BTM_LogHistory(kBtmLogTag, p_rec->ble.pseudo_addr, "Establish CIS",
+                       base::StringPrintf("handle:0x%04x", el.acl_conn_handle));
+      }
     }
     btsnd_hcic_create_cis(conn_params.conn_pairs.size(),
                           conn_params.conn_pairs.data(),
@@ -261,6 +299,11 @@
         << "Not connected";
     bluetooth::legacy::hci::GetInterface().Disconnect(
         cis_handle, static_cast<tHCI_STATUS>(reason));
+
+    BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[cis_handle], "Disconnect CIS ",
+                   base::StringPrintf(
+                       "handle:0x%04x, reason:%s", cis_handle,
+                       hci_reason_code_text((tHCI_REASON)(reason)).c_str()));
   }
 
   void on_setup_iso_data_path(uint8_t* stream, uint16_t len) {
@@ -278,6 +321,12 @@
       return;
     }
 
+    BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[conn_handle],
+                   "Setup data path complete",
+                   base::StringPrintf(
+                       "handle:0x%04x, status:%s", conn_handle,
+                       hci_status_code_text((tHCI_STATUS)(status)).c_str()));
+
     if (status == HCI_SUCCESS) iso->state_flags |= kStateFlagHasDataPathSet;
     if (iso->state_flags & kStateFlagIsBroadcast) {
       LOG_ASSERT(big_callbacks_ != nullptr) << "Invalid BIG callbacks";
@@ -306,6 +355,12 @@
         std::move(path_params.codec_conf),
         base::BindOnce(&iso_impl::on_setup_iso_data_path,
                        base::Unretained(this)));
+    BTM_LogHistory(
+        kBtmLogTag, cis_hdl_to_addr[conn_handle], "Setup data path",
+        base::StringPrintf(
+            "handle:0x%04x, dir:0x%02x, path_id:0x%02x, codec_id:0x%02x",
+            conn_handle, path_params.data_path_dir, path_params.data_path_id,
+            path_params.codec_id_format));
   }
 
   void on_remove_iso_data_path(uint8_t* stream, uint16_t len) {
@@ -323,6 +378,12 @@
       return;
     }
 
+    BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[conn_handle],
+                   "Remove data path complete",
+                   base::StringPrintf(
+                       "handle:0x%04x, status:%s", conn_handle,
+                       hci_status_code_text((tHCI_STATUS)(status)).c_str()));
+
     if (status == HCI_SUCCESS) iso->state_flags &= ~kStateFlagHasDataPathSet;
 
     if (iso->state_flags & kStateFlagIsBroadcast) {
@@ -345,6 +406,9 @@
         iso_handle, data_path_dir,
         base::BindOnce(&iso_impl::on_remove_iso_data_path,
                        base::Unretained(this)));
+    BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[iso_handle], "Remove data path",
+                   base::StringPrintf("handle:0x%04x, dir:0x%02x", iso_handle,
+                                      data_path_dir));
   }
 
   void on_iso_link_quality_read(uint8_t* stream, uint16_t len) {
@@ -358,6 +422,13 @@
     uint32_t rxUnreceivedPackets;
     uint32_t duplicatePackets;
 
+    // 1 + 2 + 4 * 7
+#define ISO_LINK_QUALITY_SIZE 31
+    if (len < ISO_LINK_QUALITY_SIZE) {
+      LOG(ERROR) << "Malformated link quality format, len=" << len;
+      return;
+    }
+
     STREAM_TO_UINT8(status, stream);
     if (status != HCI_SUCCESS) {
       LOG(ERROR) << "Failed to Read ISO Link Quality, status: "
@@ -491,6 +562,12 @@
     auto cis = GetCisIfKnown(evt.cis_conn_hdl);
     LOG_ASSERT(cis != nullptr) << "No such cis: " << +evt.cis_conn_hdl;
 
+    BTM_LogHistory(kBtmLogTag, cis_hdl_to_addr[evt.cis_conn_hdl],
+                   "CIS established event",
+                   base::StringPrintf(
+                       "cis_handle:0x%04x status:%s", evt.cis_conn_hdl,
+                       hci_error_code_text((tHCI_STATUS)(evt.status)).c_str()));
+
     cis->sync_info.first_sync_ts = bluetooth::common::time_get_os_boottime_us();
 
     STREAM_TO_UINT24(evt.cig_sync_delay, data);
@@ -508,7 +585,11 @@
     STREAM_TO_UINT16(evt.max_pdu_stom, data);
     STREAM_TO_UINT16(evt.iso_itv, data);
 
-    if (evt.status == HCI_SUCCESS) cis->state_flags |= kStateFlagIsConnected;
+    if (evt.status == HCI_SUCCESS) {
+      cis->state_flags |= kStateFlagIsConnected;
+    } else {
+      cis_hdl_to_addr.erase(evt.cis_conn_hdl);
+    }
 
     cis->state_flags &= ~kStateFlagIsConnecting;
 
@@ -524,6 +605,13 @@
     LOG_ASSERT(cig_callbacks_ != nullptr) << "Invalid CIG callbacks";
 
     LOG_INFO("%s flags: %d", __func__, +cis->state_flags);
+
+    BTM_LogHistory(
+        kBtmLogTag, cis_hdl_to_addr[handle], "CIS disconnected",
+        base::StringPrintf("cis_handle:0x%04x, reason:%s", handle,
+                           hci_error_code_text((tHCI_REASON)(reason)).c_str()));
+    cis_hdl_to_addr.erase(handle);
+
     if (cis->state_flags & kStateFlagIsConnected) {
       cis_disconnected_evt evt = {
           .reason = reason,
@@ -870,6 +958,7 @@
 
   std::map<uint16_t, std::unique_ptr<iso_cis>> conn_hdl_to_cis_map_;
   std::map<uint16_t, std::unique_ptr<iso_bis>> conn_hdl_to_bis_map_;
+  std::map<uint16_t, RawAddress> cis_hdl_to_addr;
 
   std::atomic_uint16_t iso_credits_;
   uint16_t iso_buffer_size_;
diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc
index efea695..8df08bb 100644
--- a/system/stack/btm/btm_sec.cc
+++ b/system/stack/btm/btm_sec.cc
@@ -2046,8 +2046,14 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_create_conn_cancel_complete(const uint8_t* p) {
+void btm_create_conn_cancel_complete(const uint8_t* p, uint16_t evt_len) {
   uint8_t status;
+
+  if (evt_len < 1 + BD_ADDR_LEN) {
+     BTM_TRACE_ERROR("%s malformatted event packet, too short", __func__);
+     return;
+  }
+
   STREAM_TO_UINT8(status, p);
   RawAddress bd_addr;
   STREAM_TO_BDADDR(bd_addr, p);
@@ -2986,13 +2992,23 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_read_local_oob_complete(uint8_t* p) {
+void btm_read_local_oob_complete(uint8_t* p, uint16_t evt_len) {
   tBTM_SP_LOC_OOB evt_data;
-  uint8_t status = *p++;
+  uint8_t status;
+  if (evt_len < 1) {
+    goto err_out;
+  }
+
+  STREAM_TO_UINT8(status, p);
 
   BTM_TRACE_EVENT("btm_read_local_oob_complete:%d", status);
   if (status == HCI_SUCCESS) {
     evt_data.status = BTM_SUCCESS;
+
+    if (evt_len < 1 + 32) {
+      goto err_out;
+    }
+
     STREAM_TO_ARRAY16(evt_data.c.data(), p);
     STREAM_TO_ARRAY16(evt_data.r.data(), p);
   } else
@@ -3003,6 +3019,11 @@
     btm_sp_evt_data.loc_oob = evt_data;
     (*btm_cb.api.p_sp_callback)(BTM_SP_LOC_OOB_EVT, &btm_sp_evt_data);
   }
+
+  return;
+
+err_out:
+  BTM_TRACE_ERROR("%s malformatted event packet, too short", __func__);
 }
 
 /*******************************************************************************
@@ -3750,7 +3771,7 @@
 void btm_sec_disconnected(uint16_t handle, tHCI_REASON reason,
                           std::string comment) {
   if ((reason != HCI_ERR_CONN_CAUSE_LOCAL_HOST) &&
-      (reason != HCI_ERR_PEER_USER)) {
+      (reason != HCI_ERR_PEER_USER) && (reason != HCI_ERR_REMOTE_POWER_OFF)) {
     LOG_WARN("Got uncommon disconnection reason:%s handle:0x%04x comment:%s",
              hci_error_code_text(reason).c_str(), handle, comment.c_str());
   }
diff --git a/system/stack/btm/btm_sec.h b/system/stack/btm/btm_sec.h
index 8b92d5d..0acb6ca 100644
--- a/system/stack/btm/btm_sec.h
+++ b/system/stack/btm/btm_sec.h
@@ -454,7 +454,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_create_conn_cancel_complete(const uint8_t* p);
+void btm_create_conn_cancel_complete(const uint8_t* p, uint16_t evt_len);
 
 /*******************************************************************************
  *
@@ -582,7 +582,7 @@
  * Returns          void
  *
  ******************************************************************************/
-void btm_read_local_oob_complete(uint8_t* p);
+void btm_read_local_oob_complete(uint8_t* p, uint16_t evt_len);
 
 /*******************************************************************************
  *
diff --git a/system/stack/btm/neighbor_inquiry.h b/system/stack/btm/neighbor_inquiry.h
index 20f76d4..d03ea08 100644
--- a/system/stack/btm/neighbor_inquiry.h
+++ b/system/stack/btm/neighbor_inquiry.h
@@ -117,6 +117,7 @@
   int8_t ble_tx_power;
   uint16_t ble_periodic_adv_int;
   RawAddress ble_ad_rsi; /* Resolvable Set Identifier from advertising */
+  bool ble_ad_is_le_audio_capable;
   uint8_t flag;
   bool include_rsi;
   RawAddress original_bda;
diff --git a/system/stack/btu/btu_hcif.cc b/system/stack/btu/btu_hcif.cc
index 3c0f5d0..552b174 100644
--- a/system/stack/btu/btu_hcif.cc
+++ b/system/stack/btu/btu_hcif.cc
@@ -96,7 +96,7 @@
 static void btu_ble_proc_ltk_req(uint8_t* p);
 static void btu_hcif_encryption_key_refresh_cmpl_evt(uint8_t* p);
 static void btu_ble_data_length_change_evt(uint8_t* p, uint16_t evt_len);
-static void btu_ble_rc_param_req_evt(uint8_t* p);
+static void btu_ble_rc_param_req_evt(uint8_t* p, uint8_t len);
 
 /**
  * Log HCI event metrics that are not handled in special functions
@@ -340,16 +340,16 @@
           btm_ble_process_adv_pkt(ble_evt_len, p);
           break;
         case HCI_BLE_LL_CONN_PARAM_UPD_EVT:
-          btu_ble_ll_conn_param_upd_evt(p, hci_evt_len);
+          btu_ble_ll_conn_param_upd_evt(p, ble_evt_len);
           break;
         case HCI_BLE_READ_REMOTE_FEAT_CMPL_EVT:
-          btm_ble_read_remote_features_complete(p);
+          btm_ble_read_remote_features_complete(p, ble_evt_len);
           break;
         case HCI_BLE_LTK_REQ_EVT: /* received only at peripheral device */
           btu_ble_proc_ltk_req(p);
           break;
         case HCI_BLE_RC_PARAM_REQ_EVT:
-          btu_ble_rc_param_req_evt(p);
+          btu_ble_rc_param_req_evt(p, ble_evt_len);
           break;
         case HCI_BLE_DATA_LENGTH_CHANGE_EVT:
           btu_ble_data_length_change_evt(p, hci_evt_len);
@@ -1185,7 +1185,7 @@
       break;
 
     case HCI_DELETE_STORED_LINK_KEY:
-      btm_delete_stored_link_key_complete(p);
+      btm_delete_stored_link_key_complete(p, evt_len);
       break;
 
     case HCI_READ_LOCAL_NAME:
@@ -1193,11 +1193,11 @@
       break;
 
     case HCI_GET_LINK_QUALITY:
-      btm_read_link_quality_complete(p);
+      btm_read_link_quality_complete(p, evt_len);
       break;
 
     case HCI_READ_RSSI:
-      btm_read_rssi_complete(p);
+      btm_read_rssi_complete(p, evt_len);
       break;
 
     case HCI_READ_FAILED_CONTACT_COUNTER:
@@ -1209,15 +1209,15 @@
       break;
 
     case HCI_READ_TRANSMIT_POWER_LEVEL:
-      btm_read_tx_power_complete(p, false);
+      btm_read_tx_power_complete(p, evt_len, false);
       break;
 
     case HCI_CREATE_CONNECTION_CANCEL:
-      btm_create_conn_cancel_complete(p);
+      btm_create_conn_cancel_complete(p, evt_len);
       break;
 
     case HCI_READ_LOCAL_OOB_DATA:
-      btm_read_local_oob_complete(p);
+      btm_read_local_oob_complete(p, evt_len);
       break;
 
     case HCI_READ_INQ_TX_POWER_LEVEL:
@@ -1226,15 +1226,15 @@
     /* BLE Commands sComplete*/
     case HCI_BLE_RAND:
     case HCI_BLE_ENCRYPT:
-      btm_ble_rand_enc_complete(p, opcode, (tBTM_RAND_ENC_CB*)p_cplt_cback);
+      btm_ble_rand_enc_complete(p, evt_len, opcode, (tBTM_RAND_ENC_CB*)p_cplt_cback);
       break;
 
     case HCI_BLE_READ_ADV_CHNL_TX_POWER:
-      btm_read_tx_power_complete(p, true);
+      btm_read_tx_power_complete(p, evt_len, true);
       break;
 
     case HCI_BLE_WRITE_ADV_ENABLE:
-      btm_ble_write_adv_enable_complete(p);
+      btm_ble_write_adv_enable_complete(p, evt_len);
       break;
 
     case HCI_BLE_CREATE_LL_CONN:
@@ -1645,6 +1645,11 @@
   uint16_t latency;
   uint16_t timeout;
 
+  if (evt_len < 9) {
+     LOG_ERROR("Bogus event packet, too short");
+     return;
+  }
+
   STREAM_TO_UINT8(status, p);
   STREAM_TO_UINT16(handle, p);
   STREAM_TO_UINT16(interval, p);
@@ -1687,10 +1692,15 @@
 /**********************************************
  * End of BLE Events Handler
  **********************************************/
-static void btu_ble_rc_param_req_evt(uint8_t* p) {
+static void btu_ble_rc_param_req_evt(uint8_t* p, uint8_t len) {
   uint16_t handle;
   uint16_t int_min, int_max, latency, timeout;
 
+  if (len < 10) {
+    LOG(ERROR) << __func__ << "bogus event packet, too short";
+    return;
+  }
+
   STREAM_TO_UINT16(handle, p);
   STREAM_TO_UINT16(int_min, p);
   STREAM_TO_UINT16(int_max, p);
diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h
index 8293bb6..f0d8254 100644
--- a/system/stack/eatt/eatt_impl.h
+++ b/system/stack/eatt/eatt_impl.h
@@ -874,7 +874,12 @@
               << " is_eatt_supported = " << int(is_eatt_supported);
     if (!is_eatt_supported) return;
 
-    eatt_device* eatt_dev = add_eatt_device(bd_addr);
+    eatt_device* eatt_dev = this->find_device_by_address(bd_addr);
+    if (!eatt_dev) {
+      LOG(INFO) << __func__ << " Adding device: " << bd_addr
+                << " on supported features callback.";
+      eatt_dev = add_eatt_device(bd_addr);
+    }
 
     if (role != HCI_ROLE_CENTRAL) {
       /* TODO For now do nothing, we could run a timer here and start EATT if
diff --git a/system/stack/gatt/connection_manager.cc b/system/stack/gatt/connection_manager.cc
index f15be22..5c2e880 100644
--- a/system/stack/gatt/connection_manager.cc
+++ b/system/stack/gatt/connection_manager.cc
@@ -42,6 +42,8 @@
 
 #define DIRECT_CONNECT_TIMEOUT (30 * 1000) /* 30 seconds */
 
+constexpr char kBtmLogTag[] = "TA";
+
 struct closure_data {
   base::OnceClosure user_task;
   base::Location posted_from;
@@ -119,12 +121,11 @@
 
 bool IsTargetedAnnouncement(const uint8_t* p_eir, uint16_t eir_len) {
   const uint8_t* p_service_data = p_eir;
-  uint16_t remaining_data_len = eir_len;
   uint8_t service_data_len = 0;
 
   while ((p_service_data = AdvertiseDataParser::GetFieldByType(
               p_service_data + service_data_len,
-              (remaining_data_len -= service_data_len),
+              eir_len - (p_service_data - p_eir) - service_data_len,
               BTM_BLE_AD_TYPE_SERVICE_DATA_TYPE, &service_data_len))) {
     uint16_t uuid;
     uint8_t announcement_type;
@@ -182,6 +183,8 @@
     return;
   }
 
+  BTM_LogHistory(kBtmLogTag, addr, "Found TA from");
+
   /* Take fist app_id and use it for direct_connect */
   auto app_id = *(it->second.doing_targeted_announcements_conn.begin());
 
@@ -192,6 +195,9 @@
 
 void target_announcements_filtering_set(bool enable) {
   LOG_DEBUG("enable %d", enable);
+  BTM_LogHistory(kBtmLogTag, RawAddress::kEmpty,
+                 (enable ? "Start filtering" : "Stop filtering"));
+
   /* Safe to call as if there is no support for filtering, this call will be
    * ignored. */
   bluetooth::shim::set_target_announcements_filter(enable);
@@ -247,6 +253,10 @@
   }
 
   bgconn_dev[address].doing_targeted_announcements_conn.insert(app_id);
+  if (bgconn_dev[address].doing_targeted_announcements_conn.size() == 1) {
+    BTM_LogHistory(kBtmLogTag, address, "Allow connection from");
+  }
+
   if (num_of_targeted_announcements_users() == 1) {
     target_announcements_filtering_set(true);
   }
@@ -336,13 +346,20 @@
   auto num_of_targeted_announcements_before_remove =
       it->second.doing_targeted_announcements_conn.size();
 
-  if (!it->second.doing_bg_conn.erase(app_id) &&
-      !it->second.doing_targeted_announcements_conn.erase(app_id)) {
+  bool removed_from_bg_conn = (it->second.doing_bg_conn.erase(app_id) > 0);
+  bool removed_from_ta =
+      (it->second.doing_targeted_announcements_conn.erase(app_id) > 0);
+  if (!removed_from_bg_conn && !removed_from_ta) {
     LOG_WARN("Failed to remove background connection app %d for address %s",
              static_cast<int>(app_id), address.ToString().c_str());
     return false;
   }
 
+  if (removed_from_ta &&
+      it->second.doing_targeted_announcements_conn.size() == 0) {
+    BTM_LogHistory(kBtmLogTag, address, "Ignore connection from");
+  }
+
   if (is_anyone_connecting(it)) {
     LOG_DEBUG("some device is still connecting, app_id=%d, address=%s",
               static_cast<int>(app_id), address.ToString().c_str());
diff --git a/system/stack/gatt/gatt_auth.cc b/system/stack/gatt/gatt_auth.cc
index 600829a..2329bc5 100644
--- a/system/stack/gatt/gatt_auth.cc
+++ b/system/stack/gatt/gatt_auth.cc
@@ -176,23 +176,26 @@
   tGATT_CLCB* p_clcb = p_tcb->pending_enc_clcb.front();
   p_tcb->pending_enc_clcb.pop_front();
 
-  bool status = false;
-  if (result == BTM_SUCCESS) {
-    if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM) {
-      status = BTM_IsLinkKeyAuthed(*bd_addr, transport);
-    } else {
-      status = true;
+  if (p_clcb != NULL) {
+    bool status = false;
+    if (result == BTM_SUCCESS) {
+      if (gatt_get_sec_act(p_tcb) == GATT_SEC_ENCRYPT_MITM) {
+        status = BTM_IsLinkKeyAuthed(*bd_addr, transport);
+      } else {
+        status = true;
+      }
     }
-  }
 
-  gatt_sec_check_complete(status, p_clcb, p_tcb->sec_act);
+    gatt_sec_check_complete(status, p_clcb, p_tcb->sec_act);
+  }
 
   /* start all other pending operation in queue */
   std::deque<tGATT_CLCB*> new_pending_clcbs;
   while (!p_tcb->pending_enc_clcb.empty()) {
     tGATT_CLCB* p_clcb = p_tcb->pending_enc_clcb.front();
     p_tcb->pending_enc_clcb.pop_front();
-    if (gatt_security_check_start(p_clcb)) new_pending_clcbs.push_back(p_clcb);
+    if (p_clcb != NULL && gatt_security_check_start(p_clcb))
+      new_pending_clcbs.push_back(p_clcb);
   }
   p_tcb->pending_enc_clcb = new_pending_clcbs;
 }
@@ -229,7 +232,7 @@
     while (!p_tcb->pending_enc_clcb.empty()) {
       tGATT_CLCB* p_clcb = p_tcb->pending_enc_clcb.front();
       p_tcb->pending_enc_clcb.pop_front();
-      if (gatt_security_check_start(p_clcb))
+      if (p_clcb != NULL && gatt_security_check_start(p_clcb))
         new_pending_clcbs.push_back(p_clcb);
     }
     p_tcb->pending_enc_clcb = new_pending_clcbs;
diff --git a/system/stack/gatt/gatt_cl.cc b/system/stack/gatt/gatt_cl.cc
index e2f8808..d026633 100644
--- a/system/stack/gatt/gatt_cl.cc
+++ b/system/stack/gatt/gatt_cl.cc
@@ -595,7 +595,8 @@
   VLOG(1) << StringPrintf("value resp op_code = %s len = %d",
                           gatt_dbg_op_name(op_code), len);
 
-  if (len < GATT_PREP_WRITE_RSP_MIN_LEN) {
+  if (len < GATT_PREP_WRITE_RSP_MIN_LEN ||
+      len > GATT_PREP_WRITE_RSP_MIN_LEN + sizeof(value.value)) {
     LOG(ERROR) << "illegal prepare write response length, discard";
     gatt_end_operation(p_clcb, GATT_INVALID_PDU, &value);
     return;
@@ -604,7 +605,7 @@
   STREAM_TO_UINT16(value.handle, p);
   STREAM_TO_UINT16(value.offset, p);
 
-  value.len = len - 4;
+  value.len = len - GATT_PREP_WRITE_RSP_MIN_LEN;
 
   memcpy(value.value, p, value.len);
 
diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc
index ec19948..cd5099c 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -1163,13 +1163,13 @@
   uint16_t cid = p_clcb->cid;
 
   if (!p_tcb->pending_enc_clcb.empty()) {
-    auto iter = std::find_if(p_tcb->pending_enc_clcb.begin(),
-                             p_tcb->pending_enc_clcb.end(),
-                             [p_clcb](auto& el) { return el == p_clcb; });
-    if (iter != p_tcb->pending_enc_clcb.end()) {
-      p_tcb->pending_enc_clcb.erase(iter);
-      LOG_WARN("Removing clcb (%p) for conn id=0x%04x from pending_enc_clcb",
-               p_clcb, p_clcb->conn_id);
+    for (size_t i = 0; i < p_tcb->pending_enc_clcb.size(); i++) {
+      if (p_tcb->pending_enc_clcb.at(i) == p_clcb) {
+        LOG_WARN("Removing clcb (%p) for conn id=0x%04x from pending_enc_clcb",
+                 p_clcb, p_clcb->conn_id);
+        p_tcb->pending_enc_clcb.at(i) = NULL;
+        break;
+      }
     }
   }
 
diff --git a/system/stack/include/acl_hci_link_interface.h b/system/stack/include/acl_hci_link_interface.h
index f109b56..5ce61b7 100644
--- a/system/stack/include/acl_hci_link_interface.h
+++ b/system/stack/include/acl_hci_link_interface.h
@@ -52,7 +52,7 @@
 void btm_pm_proc_ssr_evt(uint8_t* p, uint16_t evt_len);
 void btm_read_automatic_flush_timeout_complete(uint8_t* p);
 void btm_read_failed_contact_counter_complete(uint8_t* p);
-void btm_read_link_quality_complete(uint8_t* p);
+void btm_read_link_quality_complete(uint8_t* p, uint16_t evt_len);
 void btm_read_remote_ext_features_complete_raw(uint8_t* p, uint8_t evt_len);
 void btm_read_remote_ext_features_complete(uint16_t handle, uint8_t page_num,
                                            uint8_t max_page, uint8_t* features);
@@ -62,8 +62,8 @@
                                       uint8_t lmp_version,
                                       uint16_t manufacturer,
                                       uint16_t lmp_subversion);
-void btm_read_rssi_complete(uint8_t* p);
-void btm_read_tx_power_complete(uint8_t* p, bool is_ble);
+void btm_read_rssi_complete(uint8_t* p, uint16_t evt_len);
+void btm_read_tx_power_complete(uint8_t* p, uint16_t evt_len, bool is_ble);
 
 void acl_rcv_acl_data(BT_HDR* p_msg);
 void acl_link_segments_xmitted(BT_HDR* p_msg);
diff --git a/system/stack/include/ble_hci_link_interface.h b/system/stack/include/ble_hci_link_interface.h
index d4f6d35..ab60b48 100644
--- a/system/stack/include/ble_hci_link_interface.h
+++ b/system/stack/include/ble_hci_link_interface.h
@@ -27,14 +27,15 @@
 void btm_ble_process_adv_pkt(uint8_t len, const uint8_t* p);
 void btm_ble_process_ext_adv_pkt(uint8_t len, const uint8_t* p);
 void btm_ble_process_phy_update_pkt(uint8_t len, uint8_t* p);
-void btm_ble_read_remote_features_complete(uint8_t* p);
+void btm_ble_read_remote_features_complete(uint8_t* p, uint8_t length);
 void btm_le_on_advertising_set_terminated(uint8_t* p, uint16_t length);
-extern void btm_ble_write_adv_enable_complete(uint8_t* p);
+extern void btm_ble_write_adv_enable_complete(uint8_t* p, uint16_t evt_len);
 extern void btm_ble_create_ll_conn_complete(tHCI_STATUS status);
 extern void btm_ble_ltk_request(uint16_t handle, uint8_t rand[8],
                                 uint16_t ediv);
 extern void btm_ble_test_command_complete(uint8_t* p);
-extern void btm_ble_rand_enc_complete(uint8_t* p, uint16_t op_code,
+extern void btm_ble_rand_enc_complete(uint8_t* p, uint16_t evt_len,
+                                      uint16_t op_code,
                                       tBTM_RAND_ENC_CB* p_enc_cplt_cback);
 extern bool btm_identity_addr_to_random_pseudo(RawAddress* bd_addr,
                                                tBLE_ADDR_TYPE* p_addr_type,
diff --git a/system/stack/include/dev_hci_link_interface.h b/system/stack/include/dev_hci_link_interface.h
index 9a6a767..b6e3d71 100644
--- a/system/stack/include/dev_hci_link_interface.h
+++ b/system/stack/include/dev_hci_link_interface.h
@@ -23,7 +23,7 @@
 
 #include "types/raw_address.h"
 
-extern void btm_delete_stored_link_key_complete(uint8_t* p);
+extern void btm_delete_stored_link_key_complete(uint8_t* p, uint16_t evt_len);
 extern void btm_vendor_specific_evt(const uint8_t* p, uint8_t evt_len);
 extern void btm_vsc_complete(uint8_t* p, uint16_t cc_opcode, uint16_t evt_len,
                              tBTM_VSC_CMPL_CB* p_vsc_cplt_cback);
diff --git a/system/stack/include/gatt_api.h b/system/stack/include/gatt_api.h
index 5115142..05d4115 100644
--- a/system/stack/include/gatt_api.h
+++ b/system/stack/include/gatt_api.h
@@ -213,6 +213,8 @@
 
   GATT_CONN_FAILED_ESTABLISHMENT = HCI_ERR_CONN_FAILED_ESTABLISHMENT,
 
+  GATT_CONN_TERMINATED_POWER_OFF = HCI_ERR_REMOTE_POWER_OFF,
+
   BTA_GATT_CONN_NONE = 0x0101, /* 0x0101 no connection to cancel  */
 
 } tGATT_DISCONN_REASON;
@@ -232,6 +234,7 @@
     CASE_RETURN_TEXT(GATT_CONN_LMP_TIMEOUT);
     CASE_RETURN_TEXT(GATT_CONN_FAILED_ESTABLISHMENT);
     CASE_RETURN_TEXT(BTA_GATT_CONN_NONE);
+    CASE_RETURN_TEXT(GATT_CONN_TERMINATED_POWER_OFF);
     default:
       return base::StringPrintf("UNKNOWN[%hu]", reason);
   }
diff --git a/system/stack/include/hci_error_code.h b/system/stack/include/hci_error_code.h
index bbff6a6..c2abb8d 100644
--- a/system/stack/include/hci_error_code.h
+++ b/system/stack/include/hci_error_code.h
@@ -44,6 +44,7 @@
   HCI_ERR_HOST_TIMEOUT = 0x10,  // stack/btm/btm_ble_gap,
   HCI_ERR_ILLEGAL_PARAMETER_FMT = 0x12,
   HCI_ERR_PEER_USER = 0x13,
+  HCI_ERR_REMOTE_POWER_OFF = 0x15,
   HCI_ERR_CONN_CAUSE_LOCAL_HOST = 0x16,
   HCI_ERR_REPEATED_ATTEMPTS = 0x17,
   HCI_ERR_PAIRING_NOT_ALLOWED = 0x18,
diff --git a/system/stack/include/sec_hci_link_interface.h b/system/stack/include/sec_hci_link_interface.h
index b5dda7f..dce3c47 100644
--- a/system/stack/include/sec_hci_link_interface.h
+++ b/system/stack/include/sec_hci_link_interface.h
@@ -26,12 +26,12 @@
 // This header contains functions for HCIF-Security Management to invoke
 //
 
-void btm_create_conn_cancel_complete(const uint8_t* p);
+void btm_create_conn_cancel_complete(const uint8_t* p, uint16_t evt_len);
 void btm_io_capabilities_req(const RawAddress& p);
 void btm_io_capabilities_rsp(const uint8_t* p);
 void btm_proc_sp_req_evt(tBTM_SP_EVT event, const uint8_t* p);
 void btm_read_inq_tx_power_complete(uint8_t* p);
-void btm_read_local_oob_complete(uint8_t* p);
+void btm_read_local_oob_complete(uint8_t* p, uint16_t evt_len);
 void btm_rem_oob_req(const uint8_t* p);
 void btm_sec_auth_complete(uint16_t handle, tHCI_STATUS status);
 void btm_sec_disconnected(uint16_t handle, tHCI_STATUS reason, std::string);
diff --git a/system/stack/sdp/sdp_db.cc b/system/stack/sdp/sdp_db.cc
index 297b312..acef4a5 100644
--- a/system/stack/sdp/sdp_db.cc
+++ b/system/stack/sdp/sdp_db.cc
@@ -355,6 +355,11 @@
   uint16_t xx, yy, zz;
   tSDP_RECORD* p_rec = &sdp_cb.server_db.record[0];
 
+  if (p_val == nullptr) {
+    SDP_TRACE_WARNING("Trying to add attribute with p_val == nullptr, skipped");
+    return (false);
+  }
+
   if (sdp_cb.trace_level >= BT_TRACE_LEVEL_DEBUG) {
     if ((attr_type == UINT_DESC_TYPE) ||
         (attr_type == TWO_COMP_INT_DESC_TYPE) ||
@@ -402,6 +407,13 @@
     if (p_rec->record_handle == handle) {
       tSDP_ATTRIBUTE* p_attr = &p_rec->attribute[0];
 
+      // error out early, no need to look up
+      if (p_rec->free_pad_ptr >= SDP_MAX_PAD_LEN) {
+        SDP_TRACE_ERROR("the free pad for SDP record with handle %d is "
+                        "full, skip adding the attribute", handle);
+        return (false);
+      }
+
       /* Found the record. Now, see if the attribute already exists */
       for (xx = 0; xx < p_rec->num_attributes; xx++, p_attr++) {
         /* The attribute exists. replace it */
@@ -440,15 +452,13 @@
           attr_len = 0;
       }
 
-      if ((attr_len > 0) && (p_val != 0)) {
+      if (attr_len > 0) {
         p_attr->len = attr_len;
         memcpy(&p_rec->attr_pad[p_rec->free_pad_ptr], p_val, (size_t)attr_len);
         p_attr->value_ptr = &p_rec->attr_pad[p_rec->free_pad_ptr];
         p_rec->free_pad_ptr += attr_len;
-      } else if ((attr_len == 0 &&
-                  p_attr->len !=
-                      0) || /* if truncate to 0 length, simply don't add */
-                 p_val == 0) {
+      } else if (attr_len == 0 && p_attr->len != 0) {
+        /* if truncate to 0 length, simply don't add */
         SDP_TRACE_ERROR(
             "SDP_AddAttribute fail, length exceed maximum: ID %d: attr_len:%d ",
             attr_id, attr_len);
diff --git a/system/stack/test/a2dp/AndroidTest.xml b/system/stack/test/a2dp/AndroidTest.xml
index 4f81751..71fc46c 100644
--- a/system/stack/test/a2dp/AndroidTest.xml
+++ b/system/stack/test/a2dp/AndroidTest.xml
@@ -34,6 +34,7 @@
   <!-- Only run tests in MTS if the Bluetooth Mainline module is installed. -->
   <object type="module_controller"
           class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
-      <option name="mainline-module-package-name" value="com.google.android.bluetooth" />
+      <option name="mainline-module-package-name" value="com.android.btservices" />
+      <option name="mainline-module-package-name" value="com.google.android.btservices" />
   </object>
 </configuration>
diff --git a/system/stack/test/ad_parser_unittest.cc b/system/stack/test/ad_parser_unittest.cc
index 36cc7f2..1f6eb29 100644
--- a/system/stack/test/ad_parser_unittest.cc
+++ b/system/stack/test/ad_parser_unittest.cc
@@ -174,4 +174,40 @@
   glued.insert(glued.end(), scan_resp.begin(), scan_resp.end());
 
   EXPECT_TRUE(AdvertiseDataParser::IsValid(glued));
+}
+
+TEST(AdvertiseDataParserTest, GetFieldByTypeInLoop) {
+  // Single field.
+  const uint8_t AD_TYPE_SVC_DATA = 0x16;
+  const std::vector<uint8_t> data0{
+    0x02, 0x01, 0x02,
+    0x07, 0x2e, 0x6a, 0xc1, 0x19, 0x52, 0x1e, 0x49,
+    0x09, 0x16, 0x4e, 0x18, 0x00, 0xff, 0x0f, 0x03, 0x00, 0x00,
+    0x02, 0x0a, 0x7f,
+    0x03, 0x16, 0x4f, 0x18,
+    0x04, 0x16, 0x53, 0x18, 0x00,
+    0x0f, 0x09, 0x48, 0x5f, 0x43, 0x33, 0x45, 0x41, 0x31, 0x36, 0x33, 0x46, 0x35, 0x36, 0x34, 0x46 };
+
+  const uint8_t* p_service_data = data0.data();
+  uint8_t service_data_len = 0;
+
+  int match_no = 0;
+  while ((p_service_data = AdvertiseDataParser::GetFieldByType(
+              p_service_data + service_data_len,
+              data0.size() - (p_service_data - data0.data()) - service_data_len,
+              AD_TYPE_SVC_DATA, &service_data_len))) {
+    auto position = (p_service_data - data0.data());
+    if (match_no == 0) {
+      EXPECT_EQ(position, 13);
+      EXPECT_EQ(service_data_len, 8);
+    } else if (match_no == 1) {
+      EXPECT_EQ(position, 26);
+      EXPECT_EQ(service_data_len, 2);
+    } else if (match_no == 2) {
+      EXPECT_EQ(position, 30);
+      EXPECT_EQ(service_data_len, 3);
+    }
+    match_no++;
+  }
+  EXPECT_EQ(match_no, 3);
 }
\ No newline at end of file
diff --git a/system/stack/test/btm/stack_btm_regression_tests.cc b/system/stack/test/btm/stack_btm_regression_tests.cc
new file mode 100644
index 0000000..93c3d68
--- /dev/null
+++ b/system/stack/test/btm/stack_btm_regression_tests.cc
@@ -0,0 +1,51 @@
+/*
+ *
+ *  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 <gtest/gtest.h>
+
+#include "bt_hdr.h"
+#include "btm_ble_api_types.h"
+#include "hci_error_code.h"
+#include "osi/include/allocator.h"
+#include "ble_hci_link_interface.h"
+
+namespace {
+
+class StackBTMRegressionTests : public ::testing::Test {
+ protected:
+  void SetUp() override {}
+  void TearDown() override {}
+};
+
+// regression test for b/260078907
+TEST_F(StackBTMRegressionTests,
+       OOB_in_btm_ble_add_resolving_list_entry_complete) {
+  BT_HDR* pevent = (BT_HDR*)osi_calloc(sizeof(BT_HDR));
+  btm_ble_add_resolving_list_entry_complete(pevent->data, 0);
+  osi_free(pevent);
+}
+
+// regression test for b/255304475
+TEST_F(StackBTMRegressionTests,
+       OOB_in_btm_ble_clear_resolving_list_complete) {
+  BT_HDR* pevent = (BT_HDR*)osi_calloc(sizeof(BT_HDR));
+  btm_ble_clear_resolving_list_complete(pevent->data, 0);
+  osi_free(pevent);
+}
+
+}  // namespace
diff --git a/system/stack/test/btm_iso_test.cc b/system/stack/test/btm_iso_test.cc
index a5554a0..5085918 100644
--- a/system/stack/test/btm_iso_test.cc
+++ b/system/stack/test/btm_iso_test.cc
@@ -24,6 +24,7 @@
 #include "mock_controller.h"
 #include "mock_hcic_layer.h"
 #include "osi/include/allocator.h"
+#include "stack/btm/btm_dev.h"
 #include "stack/include/bt_hdr.h"
 #include "stack/include/hci_error_code.h"
 #include "stack/include/hcidefs.h"
@@ -42,6 +43,10 @@
 // Iso Manager currently works on top of the legacy HCI layer
 bool bluetooth::shim::is_gd_shim_enabled() { return false; }
 
+tBTM_SEC_DEV_REC* btm_find_dev_by_handle(uint16_t handle) { return nullptr; }
+void BTM_LogHistory(const std::string& tag, const RawAddress& bd_addr,
+                    const std::string& msg, const std::string& extra) {}
+
 namespace bte {
 class BteInterface {
  public:
diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc
index 0d81f80..5bc9ef3 100644
--- a/system/stack/test/eatt/eatt_test.cc
+++ b/system/stack/test/eatt/eatt_test.cc
@@ -120,6 +120,82 @@
     ASSERT_TRUE(test_tcb.eatt == num_of_accepted_connections);
   }
 
+  void ConnectDeviceBothSides(int num_of_accepted_connections,
+                              std::vector<uint16_t>& incoming_cids) {
+    base::OnceCallback<void(const RawAddress&, uint8_t)> eatt_supp_feat_cb;
+
+    ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
+        .WillByDefault(
+            [&eatt_supp_feat_cb](
+                const RawAddress& addr,
+                base::OnceCallback<void(const RawAddress&, uint8_t)> cb) {
+              eatt_supp_feat_cb = std::move(cb);
+              return true;
+            });
+
+    // Return false to trigger supported features request
+    ON_CALL(gatt_interface_, GetEattSupport)
+        .WillByDefault([](const RawAddress& addr) { return false; });
+
+    std::vector<uint16_t> test_local_cids{61, 62, 63, 64, 65};
+    EXPECT_CALL(l2cap_interface_,
+                ConnectCreditBasedReq(BT_PSM_EATT, test_address, _))
+        .WillOnce(Return(test_local_cids));
+
+    eatt_instance_->Connect(test_address);
+
+    // Let the remote connect while we are trying to connect
+    EXPECT_CALL(
+        l2cap_interface_,
+        ConnectCreditBasedRsp(test_address, 1, incoming_cids, L2CAP_CONN_OK, _))
+        .WillOnce(Return(true));
+    l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(
+        test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1);
+
+    // Respond to feature request scheduled by the connect request
+    ASSERT_TRUE(eatt_supp_feat_cb);
+    if (eatt_supp_feat_cb) {
+      std::move(eatt_supp_feat_cb)
+          .Run(test_address, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK);
+    }
+
+    int i = 0;
+    for (uint16_t cid : test_local_cids) {
+      EattChannel* channel =
+          eatt_instance_->FindEattChannelByCid(test_address, cid);
+      ASSERT_TRUE(channel != nullptr);
+      ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_PENDING);
+
+      if (i < num_of_accepted_connections) {
+        l2cap_app_info_.pL2CA_CreditBasedConnectCfm_Cb(
+            test_address, cid, EATT_MIN_MTU_MPS, L2CAP_CONN_OK);
+        connected_cids_.push_back(cid);
+
+        ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED);
+        ASSERT_TRUE(channel->tx_mtu_ == EATT_MIN_MTU_MPS);
+      } else {
+        l2cap_app_info_.pL2CA_Error_Cb(cid, L2CAP_CONN_NO_RESOURCES);
+
+        EattChannel* channel =
+            eatt_instance_->FindEattChannelByCid(test_address, cid);
+        ASSERT_TRUE(channel == nullptr);
+      }
+      i++;
+    }
+
+    // Check the incoming CIDs as well
+    for (auto cid : incoming_cids) {
+      EattChannel* channel =
+          eatt_instance_->FindEattChannelByCid(test_address, cid);
+      ASSERT_NE(nullptr, channel);
+      ASSERT_EQ(channel->state_, EattChannelState::EATT_CHANNEL_OPENED);
+      ASSERT_TRUE(channel->tx_mtu_ == EATT_MIN_MTU_MPS);
+    }
+
+    ASSERT_EQ(test_tcb.eatt,
+              num_of_accepted_connections + incoming_cids.size());
+  }
+
   void DisconnectEattByPeer(void) {
     for (uint16_t cid : connected_cids_)
       l2cap_app_info_.pL2CA_DisconnectInd_Cb(cid, true);
@@ -140,6 +216,9 @@
     bluetooth::gatt::SetMockGattInterface(&gatt_interface_);
     controller::SetMockControllerInterface(&controller_interface);
 
+    // Clear the static memory for each test case
+    memset(&test_tcb, 0, sizeof(test_tcb));
+
     EXPECT_CALL(l2cap_interface_, RegisterLECoc(BT_PSM_EATT, _, _))
         .WillOnce(DoAll(SaveArg<1>(&l2cap_app_info_), Return(BT_PSM_EATT)));
 
@@ -317,6 +396,22 @@
   DisconnectEattDevice(incoming_cids);
 }
 
+TEST_F(EattTest, ConnectInitiatedWhenRemoteConnects) {
+  ON_CALL(btm_api_interface_, IsEncrypted)
+      .WillByDefault(
+          [](const RawAddress& addr, tBT_TRANSPORT transport) { return true; });
+
+  std::vector<uint16_t> incoming_cids{71, 72, 73, 74};
+  ConnectDeviceBothSides(1, incoming_cids);
+
+  std::vector<uint16_t> disconnecting_cids;
+  disconnecting_cids.insert(disconnecting_cids.end(), incoming_cids.begin(),
+                            incoming_cids.end());
+  disconnecting_cids.insert(disconnecting_cids.end(), connected_cids_.begin(),
+                            connected_cids_.end());
+  DisconnectEattDevice(disconnecting_cids);
+}
+
 TEST_F(EattTest, ConnectSucceedMultipleChannels) {
   ConnectDeviceEattSupported(5);
   DisconnectEattDevice(connected_cids_);
diff --git a/system/stack/test/gatt_connection_manager_test.cc b/system/stack/test/gatt_connection_manager_test.cc
index 194d8fc..a073551 100644
--- a/system/stack/test/gatt_connection_manager_test.cc
+++ b/system/stack/test/gatt_connection_manager_test.cc
@@ -78,6 +78,9 @@
   localAcceptlistMock->EnableTargetedAnnouncements(enable, p_results_cb);
 }
 
+void BTM_LogHistory(const std::string& tag, const RawAddress& bd_addr,
+                    const std::string& msg){};
+
 namespace bluetooth {
 namespace shim {
 bool is_gd_l2cap_enabled() { return false; }
diff --git a/system/stack/test/rfcomm/stack_rfcomm_test.cc b/system/stack/test/rfcomm/stack_rfcomm_test.cc
index f0812ce..af8a30d 100644
--- a/system/stack/test/rfcomm/stack_rfcomm_test.cc
+++ b/system/stack/test/rfcomm/stack_rfcomm_test.cc
@@ -474,7 +474,7 @@
   tL2CAP_APPL_INFO l2cap_appl_info_;
 };
 
-TEST_F(StackRfcommTest, SingleServerConnectionHelloWorld) {
+TEST_F(StackRfcommTest, DISABLED_SingleServerConnectionHelloWorld) {
   // Prepare a server channel at kTestChannelNumber0
   static const uint16_t acl_handle = 0x0009;
   static const uint16_t lcid = 0x0054;
@@ -497,7 +497,7 @@
                                         "\r!dlroW olleH", 4, acl_handle, lcid));
 }
 
-TEST_F(StackRfcommTest, MultiServerPortSameDeviceHelloWorld) {
+TEST_F(StackRfcommTest, DISABLED_MultiServerPortSameDeviceHelloWorld) {
   // Prepare a server channel at kTestChannelNumber0
   static const uint16_t acl_handle = 0x0009;
   static const uint16_t lcid = 0x0054;
@@ -546,7 +546,7 @@
       acl_handle, lcid));
 }
 
-TEST_F(StackRfcommTest, SameServerPortMultiDeviceHelloWorld) {
+TEST_F(StackRfcommTest, DISABLED_SameServerPortMultiDeviceHelloWorld) {
   // Prepare a server channel at kTestChannelNumber0
   static const uint16_t test_mtu = 1600;
   static const uint8_t test_scn = 3;
@@ -596,7 +596,7 @@
       acl_handle_1, lcid_1));
 }
 
-TEST_F(StackRfcommTest, SingleClientConnectionHelloWorld) {
+TEST_F(StackRfcommTest, DISABLED_SingleClientConnectionHelloWorld) {
   static const uint16_t acl_handle = 0x0009;
   static const uint16_t lcid = 0x0054;
   static const uint16_t test_uuid = 0x1112;
@@ -619,7 +619,7 @@
       lcid, 0));
 }
 
-TEST_F(StackRfcommTest, MultiClientPortSameDeviceHelloWorld) {
+TEST_F(StackRfcommTest, DISABLED_MultiClientPortSameDeviceHelloWorld) {
   static const uint16_t acl_handle = 0x0009;
   static const uint16_t lcid = 0x0054;
   static const uint16_t test_mtu = 1600;
@@ -665,7 +665,7 @@
       acl_handle, lcid, 1));
 }
 
-TEST_F(StackRfcommTest, SameClientPortMultiDeviceHelloWorld) {
+TEST_F(StackRfcommTest, DISABLED_SameClientPortMultiDeviceHelloWorld) {
   static const uint16_t test_uuid = 0x1112;
   static const uint8_t test_scn = 8;
   static const uint16_t test_mtu = 1600;
@@ -713,7 +713,7 @@
       acl_handle_1, lcid_1, 1));
 }
 
-TEST_F(StackRfcommTest, TestConnectionCollision) {
+TEST_F(StackRfcommTest, DISABLED_TestConnectionCollision) {
   static const uint16_t acl_handle = 0x0008;
   static const uint16_t old_lcid = 0x004a;
   static const uint16_t new_lcid = 0x005c;
diff --git a/system/test/mock/mock_stack_acl.cc b/system/test/mock/mock_stack_acl.cc
index 91ee709..771e256 100644
--- a/system/test/mock/mock_stack_acl.cc
+++ b/system/test/mock/mock_stack_acl.cc
@@ -621,9 +621,9 @@
   mock_function_count_map[__func__]++;
   test::mock::stack_acl::btm_read_failed_contact_counter_timeout(data);
 }
-void btm_read_link_quality_complete(uint8_t* p) {
+void btm_read_link_quality_complete(uint8_t* p, uint16_t evt_len) {
   mock_function_count_map[__func__]++;
-  test::mock::stack_acl::btm_read_link_quality_complete(p);
+  test::mock::stack_acl::btm_read_link_quality_complete(p, evt_len);
 }
 void btm_read_link_quality_timeout(UNUSED_ATTR void* data) {
   mock_function_count_map[__func__]++;
@@ -660,17 +660,17 @@
   test::mock::stack_acl::btm_read_remote_version_complete(
       status, handle, lmp_version, manufacturer, lmp_subversion);
 }
-void btm_read_rssi_complete(uint8_t* p) {
+void btm_read_rssi_complete(uint8_t* p, uint16_t evt_len) {
   mock_function_count_map[__func__]++;
-  test::mock::stack_acl::btm_read_rssi_complete(p);
+  test::mock::stack_acl::btm_read_rssi_complete(p, evt_len);
 }
 void btm_read_rssi_timeout(UNUSED_ATTR void* data) {
   mock_function_count_map[__func__]++;
   test::mock::stack_acl::btm_read_rssi_timeout(data);
 }
-void btm_read_tx_power_complete(uint8_t* p, bool is_ble) {
+void btm_read_tx_power_complete(uint8_t* p, uint16_t evt_len, bool is_ble) {
   mock_function_count_map[__func__]++;
-  test::mock::stack_acl::btm_read_tx_power_complete(p, is_ble);
+  test::mock::stack_acl::btm_read_tx_power_complete(p, evt_len, is_ble);
 }
 void btm_read_tx_power_timeout(UNUSED_ATTR void* data) {
   mock_function_count_map[__func__]++;
diff --git a/system/test/mock/mock_stack_acl.h b/system/test/mock/mock_stack_acl.h
index 8747ed7..d2efb0c 100644
--- a/system/test/mock/mock_stack_acl.h
+++ b/system/test/mock/mock_stack_acl.h
@@ -1091,8 +1091,8 @@
 // Params: uint8_t* p
 // Returns: void
 struct btm_read_link_quality_complete {
-  std::function<void(uint8_t* p)> body{[](uint8_t* p) { ; }};
-  void operator()(uint8_t* p) { body(p); };
+  std::function<void(uint8_t* p, uint16_t evt_len)> body{[](uint8_t* p, uint16_t evt_len) { ; }};
+  void operator()(uint8_t* p, uint16_t evt_len) { body(p, evt_len); };
 };
 extern struct btm_read_link_quality_complete btm_read_link_quality_complete;
 // Name: btm_read_link_quality_timeout
@@ -1180,8 +1180,9 @@
 // Params: uint8_t* p
 // Returns: void
 struct btm_read_rssi_complete {
-  std::function<void(uint8_t* p)> body{[](uint8_t* p) { ; }};
-  void operator()(uint8_t* p) { body(p); };
+  std::function<void(uint8_t* p, uint16_t evt_len)> body{
+      [](uint8_t* pm, uint16_t evt_len) { ; }};
+  void operator()(uint8_t* p, uint16_t evt_len) { body(p, evt_len); };
 };
 extern struct btm_read_rssi_complete btm_read_rssi_complete;
 // Name: btm_read_rssi_timeout
@@ -1197,9 +1198,11 @@
 // Params: uint8_t* p, bool is_ble
 // Returns: void
 struct btm_read_tx_power_complete {
-  std::function<void(uint8_t* p, bool is_ble)> body{
-      [](uint8_t* p, bool is_ble) { ; }};
-  void operator()(uint8_t* p, bool is_ble) { body(p, is_ble); };
+  std::function<void(uint8_t* p, uint16_t evt_len, bool is_ble)> body{
+      [](uint8_t* p, uint16_t evt_len, bool is_ble) { ; }};
+  void operator()(uint8_t* p, uint16_t evt_len, bool is_ble) {
+    body(p, evt_len, is_ble);
+  };
 };
 extern struct btm_read_tx_power_complete btm_read_tx_power_complete;
 // Name: btm_read_tx_power_timeout
diff --git a/system/test/mock/mock_stack_btm_ble.cc b/system/test/mock/mock_stack_btm_ble.cc
index 5ebd1a5..ee5ac6f 100644
--- a/system/test/mock/mock_stack_btm_ble.cc
+++ b/system/test/mock/mock_stack_btm_ble.cc
@@ -226,7 +226,7 @@
                                const Octet16& stk) {
   mock_function_count_map[__func__]++;
 }
-void btm_ble_rand_enc_complete(uint8_t* p, uint16_t op_code,
+void btm_ble_rand_enc_complete(uint8_t* p, uint16_t evt_len, uint16_t op_code,
                                tBTM_RAND_ENC_CB* p_enc_cplt_cback) {
   mock_function_count_map[__func__]++;
 }
diff --git a/system/test/mock/mock_stack_btm_ble_gap.cc b/system/test/mock/mock_stack_btm_ble_gap.cc
index cf3294c..aadc5f2 100644
--- a/system/test/mock/mock_stack_btm_ble_gap.cc
+++ b/system/test/mock/mock_stack_btm_ble_gap.cc
@@ -190,7 +190,7 @@
 void btm_ble_process_phy_update_pkt(uint8_t len, uint8_t* data) {
   mock_function_count_map[__func__]++;
 }
-void btm_ble_read_remote_features_complete(uint8_t* p) {
+void btm_ble_read_remote_features_complete(uint8_t* p, uint8_t length) {
   mock_function_count_map[__func__]++;
 }
 void btm_ble_read_remote_name_cmpl(bool status, const RawAddress& bda,
@@ -220,7 +220,7 @@
                                    tHCI_STATUS status) {
   mock_function_count_map[__func__]++;
 }
-void btm_ble_write_adv_enable_complete(uint8_t* p) {
+void btm_ble_write_adv_enable_complete(uint8_t* p, uint16_t evt_len) {
   mock_function_count_map[__func__]++;
 }
 void btm_clear_all_pending_le_entry(void) {
diff --git a/system/test/mock/mock_stack_btm_devctl.cc b/system/test/mock/mock_stack_btm_devctl.cc
index a51098f..1b9db1a 100644
--- a/system/test/mock/mock_stack_btm_devctl.cc
+++ b/system/test/mock/mock_stack_btm_devctl.cc
@@ -105,7 +105,8 @@
 }
 void BTM_db_reset(void) { mock_function_count_map[__func__]++; }
 void BTM_reset_complete() { mock_function_count_map[__func__]++; }
-void btm_delete_stored_link_key_complete(uint8_t* p) {
+void btm_delete_stored_link_key_complete(uint8_t* p,
+                                         UNUSED_ATTR uint16_t evt_len) {
   mock_function_count_map[__func__]++;
 }
 void btm_dev_free() { mock_function_count_map[__func__]++; }
diff --git a/system/test/mock/mock_stack_btm_sec.cc b/system/test/mock/mock_stack_btm_sec.cc
index 56a0db1..ea6ce17 100644
--- a/system/test/mock/mock_stack_btm_sec.cc
+++ b/system/test/mock/mock_stack_btm_sec.cc
@@ -239,7 +239,7 @@
 void NotifyBondingCanceled(tBTM_STATUS btm_status) {
   mock_function_count_map[__func__]++;
 }
-void btm_create_conn_cancel_complete(const uint8_t* p) {
+void btm_create_conn_cancel_complete(const uint8_t* p, uint16_t evt_len) {
   mock_function_count_map[__func__]++;
 }
 void btm_io_capabilities_req(const RawAddress& p) {
@@ -251,7 +251,7 @@
 void btm_proc_sp_req_evt(tBTM_SP_EVT event, const uint8_t* p) {
   mock_function_count_map[__func__]++;
 }
-void btm_read_local_oob_complete(uint8_t* p) {
+void btm_read_local_oob_complete(uint8_t* p, uint16_t evt_len) {
   mock_function_count_map[__func__]++;
 }
 void btm_rem_oob_req(const uint8_t* p) { mock_function_count_map[__func__]++; }