Merge "vc: Dont ignore VC devices connected by another gatt client" into tm-qpr-dev
diff --git a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
index d1ea7d4..7fad5b2 100644
--- a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
+++ b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java
@@ -41,6 +41,7 @@
 import com.android.bluetooth.gatt.AppAdvertiseStats;
 import com.android.bluetooth.gatt.ContextMap;
 import com.android.bluetooth.gatt.GattService;
+import com.android.bluetooth.opp.BluetoothOppNotification;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.obex.HeaderSet;
 
@@ -179,6 +180,13 @@
     }
 
     /**
+     * Proxies {@link Handler#sendEmptyMessage(int)}}.
+     */
+    public boolean handlerSendEmptyMessage(Handler handler, final int what) {
+        return handler.sendEmptyMessage(what);
+    }
+
+    /**
      * Proxies {@link HeaderSet#getHeader}.
      */
     public Object getHeader(HeaderSet headerSet, int headerId) throws IOException {
@@ -233,4 +241,13 @@
             ContextMap map, GattService service) {
         return new AppAdvertiseStats(appUid, id, name, map, service);
     }
+
+
+    /**
+     * Proxies {@link com.android.bluetooth.opp.BluetoothOppNotification#BluetoothOppNotification(
+     * Context)}.
+     */
+    public BluetoothOppNotification newBluetoothOppNotification(final Context context) {
+        return new BluetoothOppNotification(context);
+    }
 }
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
index d0cfa58..5cf76bd 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpVolumeManager.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 
 import com.android.bluetooth.audio_util.BTAudioEventLogger;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -42,7 +43,9 @@
     private static final String VOLUME_MAP = "bluetooth_volume_map";
     private static final String VOLUME_REJECTLIST = "absolute_volume_rejectlist";
     private static final String VOLUME_CHANGE_LOG_TITLE = "Volume Events";
-    private static final int AVRCP_MAX_VOL = 127;
+
+    @VisibleForTesting
+    static final int AVRCP_MAX_VOL = 127;
     private static final int STREAM_MUSIC = AudioManager.STREAM_MUSIC;
     private static final int VOLUME_CHANGE_LOGGER_SIZE = 30;
     private static int sDeviceMaxVolume = 0;
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java b/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java
index 19c7911..1c684bf 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapContent.java
@@ -40,6 +40,7 @@
 import com.android.bluetooth.BluetoothMethodProxy;
 import com.android.bluetooth.DeviceWorkArounds;
 import com.android.bluetooth.SignedLongLong;
+import com.android.bluetooth.Utils;
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
 import com.android.bluetooth.map.BluetoothMapbMessageMime.MimePart;
 import com.android.bluetooth.mapapi.BluetoothMapContract;
@@ -1339,9 +1340,15 @@
         }
 
         // Fix Subject Display issue with HONDA Carkit - Ignore subject Mask.
-        if (DeviceWorkArounds.addressStartsWith(BluetoothMapService.getRemoteDevice().getAddress(),
-                    DeviceWorkArounds.HONDA_CARKIT)
-                || (ap.getParameterMask() & MASK_SUBJECT) != 0) {
+        boolean isHondaCarkit;
+        if (Utils.isInstrumentationTestMode()) {
+            isHondaCarkit = false;
+        } else {
+            isHondaCarkit = DeviceWorkArounds.addressStartsWith(
+                    BluetoothMapService.getRemoteDevice().getAddress(),
+                    DeviceWorkArounds.HONDA_CARKIT);
+        }
+        if (isHondaCarkit || (ap.getParameterMask() & MASK_SUBJECT) != 0) {
             if (fi.mMsgType == FilterInfo.TYPE_SMS) {
                 subject = c.getString(fi.mSmsColSubject);
             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
@@ -2284,7 +2291,8 @@
                     if (D) {
                         Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where);
                     }
-                    smsCursor = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null,
+                    smsCursor = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                            Sms.CONTENT_URI, SMS_PROJECTION, where, null,
                             Sms.DATE + " DESC" + limit);
                     if (smsCursor != null) {
                         BluetoothMapMessageListingElement e = null;
@@ -2326,7 +2334,8 @@
                     if (D) {
                         Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where);
                     }
-                    mmsCursor = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, where, null,
+                    mmsCursor = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                            Mms.CONTENT_URI, MMS_PROJECTION, where, null,
                             Mms.DATE + " DESC" + limit);
                     if (mmsCursor != null) {
                         BluetoothMapMessageListingElement e = null;
@@ -2369,10 +2378,9 @@
                         Log.d(TAG, "msgType: " + fi.mMsgType + " where: " + where);
                     }
                     Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-                    emailCursor =
-                            mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
-                                    where, null,
-                                    BluetoothMapContract.MessageColumns.DATE + " DESC" + limit);
+                    emailCursor = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                            contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, where, null,
+                            BluetoothMapContract.MessageColumns.DATE + " DESC" + limit);
                     if (emailCursor != null) {
                         BluetoothMapMessageListingElement e = null;
                         // store column index so we dont have to look them up anymore (optimization)
@@ -2413,8 +2421,8 @@
                 }
 
                 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-                imCursor = mResolver.query(contentUri,
-                        BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null,
+                imCursor = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contentUri, BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null,
                         BluetoothMapContract.MessageColumns.DATE + " DESC" + limit);
                 if (imCursor != null) {
                     BluetoothMapMessageListingElement e = null;
@@ -2522,8 +2530,8 @@
         if (smsSelected(fi, ap) && folderElement.hasSmsMmsContent()) {
             fi.mMsgType = FilterInfo.TYPE_SMS;
             String where = setWhereFilter(folderElement, fi, ap);
-            Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null,
-                    Sms.DATE + " DESC");
+            Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                    Sms.CONTENT_URI, SMS_PROJECTION, where, null, Sms.DATE + " DESC");
             try {
                 if (c != null) {
                     cnt = c.getCount();
@@ -2538,8 +2546,8 @@
         if (mmsSelected(ap) && folderElement.hasSmsMmsContent()) {
             fi.mMsgType = FilterInfo.TYPE_MMS;
             String where = setWhereFilter(folderElement, fi, ap);
-            Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, where, null,
-                    Mms.DATE + " DESC");
+            Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                    Mms.CONTENT_URI, MMS_PROJECTION, where, null, Mms.DATE + " DESC");
             try {
                 if (c != null) {
                     cnt += c.getCount();
@@ -2556,8 +2564,9 @@
             String where = setWhereFilter(folderElement, fi, ap);
             if (!where.isEmpty()) {
                 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-                Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
-                        where, null, BluetoothMapContract.MessageColumns.DATE + " DESC");
+                Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, where, null,
+                        BluetoothMapContract.MessageColumns.DATE + " DESC");
                 try {
                     if (c != null) {
                         cnt += c.getCount();
@@ -2575,8 +2584,8 @@
             String where = setWhereFilter(folderElement, fi, ap);
             if (!where.isEmpty()) {
                 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-                Cursor c = mResolver.query(contentUri,
-                        BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null,
+                Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contentUri, BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null,
                         BluetoothMapContract.MessageColumns.DATE + " DESC");
                 try {
                     if (c != null) {
@@ -2618,8 +2627,8 @@
             String where = setWhereFilterFolderType(folderElement, fi);
             where += " AND " + Sms.READ + "=0 ";
             where += setWhereFilterPeriod(ap, fi);
-            Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null,
-                    Sms.DATE + " DESC");
+            Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                    Sms.CONTENT_URI, SMS_PROJECTION, where, null, Sms.DATE + " DESC");
             try {
                 if (c != null) {
                     cnt = c.getCount();
@@ -2636,8 +2645,8 @@
             String where = setWhereFilterFolderType(folderElement, fi);
             where += " AND " + Mms.READ + "=0 ";
             where += setWhereFilterPeriod(ap, fi);
-            Cursor c = mResolver.query(Mms.CONTENT_URI, MMS_PROJECTION, where, null,
-                    Sms.DATE + " DESC");
+            Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                    Mms.CONTENT_URI, MMS_PROJECTION, where, null, Sms.DATE + " DESC");
             try {
                 if (c != null) {
                     cnt += c.getCount();
@@ -2657,8 +2666,9 @@
                 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 ";
                 where += setWhereFilterPeriod(ap, fi);
                 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-                Cursor c = mResolver.query(contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION,
-                        where, null, BluetoothMapContract.MessageColumns.DATE + " DESC");
+                Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contentUri, BluetoothMapContract.BT_MESSAGE_PROJECTION, where, null,
+                        BluetoothMapContract.MessageColumns.DATE + " DESC");
                 try {
                     if (c != null) {
                         cnt += c.getCount();
@@ -2678,8 +2688,8 @@
                 where += " AND " + BluetoothMapContract.MessageColumns.FLAG_READ + "=0 ";
                 where += setWhereFilterPeriod(ap, fi);
                 Uri contentUri = Uri.parse(mBaseUri + BluetoothMapContract.TABLE_MESSAGE);
-                Cursor c = mResolver.query(contentUri,
-                        BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null,
+                Cursor c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        contentUri, BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION, where, null,
                         BluetoothMapContract.MessageColumns.DATE + " DESC");
                 try {
                     if (c != null) {
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index 1862f33..c6adaf7 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -1264,7 +1264,8 @@
         }
     }
 
-    private void initMsgList() throws RemoteException {
+    @VisibleForTesting
+    void initMsgList() throws RemoteException {
         if (V) {
             Log.d(TAG, "initMsgList");
         }
@@ -1278,7 +1279,8 @@
 
             Cursor c;
             try {
-                c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION_SHORT, null, null, null);
+                c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        Sms.CONTENT_URI, SMS_PROJECTION_SHORT, null, null, null);
             } catch (SQLiteException e) {
                 Log.e(TAG, "Failed to initialize the list of messages: " + e.toString());
                 return;
@@ -1309,7 +1311,8 @@
 
             HashMap<Long, Msg> msgListMms = new HashMap<Long, Msg>();
 
-            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);
             try {
                 if (c != null && c.moveToFirst()) {
                     do {
@@ -1419,7 +1422,8 @@
         }
     }
 
-    private void handleMsgListChangesSms() {
+    @VisibleForTesting
+    void handleMsgListChangesSms() {
         if (V) {
             Log.d(TAG, "handleMsgListChangesSms");
         }
@@ -1430,9 +1434,11 @@
         Cursor c;
         synchronized (getMsgListSms()) {
             if (mMapEventReportVersion == BluetoothMapUtils.MAP_EVENT_REPORT_V10) {
-                c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION_SHORT, null, null, null);
+                c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        Sms.CONTENT_URI, SMS_PROJECTION_SHORT, null, null, null);
             } else {
-                c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION_SHORT_EXT, null, null, null);
+                c = BluetoothMethodProxy.getInstance().contentResolverQuery(mResolver,
+                        Sms.CONTENT_URI, SMS_PROJECTION_SHORT_EXT, null, null, null);
             }
             try {
                 if (c != null && c.moveToFirst()) {
@@ -1461,14 +1467,8 @@
                             if (mTransmitEvents && // extract contact details only if needed
                                     mMapEventReportVersion
                                             > BluetoothMapUtils.MAP_EVENT_REPORT_V10) {
-                                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 date = BluetoothMapUtils.getDateTimeString(
+                                        c.getLong(c.getColumnIndex(Sms.DATE)));
                                 String subject = c.getString(c.getColumnIndex(Sms.BODY));
                                 if (subject == null) {
                                     subject = "";
@@ -1639,14 +1639,8 @@
                             if (mTransmitEvents && // extract contact details only if needed
                                     mMapEventReportVersion
                                             != BluetoothMapUtils.MAP_EVENT_REPORT_V10) {
-                                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 date = BluetoothMapUtils.getDateTimeString(
+                                        c.getLong(c.getColumnIndex(Mms.DATE)));
                                 String subject = c.getString(c.getColumnIndex(Mms.SUBJECT));
                                 if (subject == null || subject.length() == 0) {
                                     /* Get subject from mms text body parts - if any exists */
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
index f855c78..c02fe4a 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapObexServer.java
@@ -1308,7 +1308,8 @@
      * @param overwrite True: The msgType will be overwritten to match the message types supported
      * by this MAS instance. False: any unsupported message types will be masked out.
      */
-    private void setMsgTypeFilterParams(BluetoothMapAppParams appParams, boolean overwrite) {
+    @VisibleForTesting
+    void setMsgTypeFilterParams(BluetoothMapAppParams appParams, boolean overwrite) {
         int masFilterMask = 0;
         if (!mEnableSmsMms) {
             masFilterMask |= BluetoothMapAppParams.FILTER_NO_SMS_CDMA;
diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java b/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java
index 7d7047f..a3710a3 100644
--- a/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java
+++ b/android/app/src/com/android/bluetooth/map/BluetoothMapUtils.java
@@ -677,21 +677,6 @@
         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/opp/BluetoothOppLauncherActivity.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
index 301b39f..b5dd0f7 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java
@@ -422,7 +422,8 @@
             launchDevicePicker();
             finish();
         } catch (IllegalArgumentException exception) {
-            showToast(exception.getMessage());
+            String message = exception.getMessage();
+            showToast(message != null ? message : "IllegalArgumentException");
             finish();
         }
     }
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java
index af138f2..5194c89 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppNotification.java
@@ -61,7 +61,7 @@
  * where there is an ongoing transfer, incoming transfer need confirm and
  * complete (successful or failed) transfer.
  */
-class BluetoothOppNotification {
+public class BluetoothOppNotification {
     private static final String TAG = "BluetoothOppNotification";
     private static final boolean V = Constants.VERBOSE;
 
@@ -152,7 +152,7 @@
      * @param ctx The context to use to obtain access to the Notification
      *            Service
      */
-    BluetoothOppNotification(Context ctx) {
+    public BluetoothOppNotification(Context ctx) {
         mContext = ctx;
         mNotificationMgr = mContext.getSystemService(NotificationManager.class);
         mNotificationChannel = new NotificationChannel(OPP_NOTIFICATION_CHANNEL,
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
index 773d771..6f20106 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java
@@ -55,6 +55,7 @@
 import android.sysprop.BluetoothProperties;
 import android.util.Log;
 
+import com.android.bluetooth.BluetoothMethodProxy;
 import com.android.bluetooth.BluetoothObexTransport;
 import com.android.bluetooth.IObexConnectionHandler;
 import com.android.bluetooth.ObexServerSockets;
@@ -128,7 +129,8 @@
     private BluetoothShareContentObserver mObserver;
 
     /** Class to handle Notification Manager updates */
-    private BluetoothOppNotification mNotifier;
+    @VisibleForTesting
+    BluetoothOppNotification mNotifier;
 
     private boolean mPendingUpdate;
 
@@ -136,9 +138,11 @@
 
     private boolean mUpdateThreadRunning;
 
-    private ArrayList<BluetoothOppShareInfo> mShares;
+    @VisibleForTesting
+    ArrayList<BluetoothOppShareInfo> mShares;
 
-    private ArrayList<BluetoothOppBatch> mBatches;
+    @VisibleForTesting
+    ArrayList<BluetoothOppBatch> mBatches;
 
     private BluetoothOppTransfer mTransfer;
 
@@ -182,7 +186,8 @@
                     + BluetoothShare.USER_CONFIRMATION + "="
                     + BluetoothShare.USER_CONFIRMATION_PENDING;
 
-    private static final String WHERE_INVISIBLE_UNCONFIRMED =
+    @VisibleForTesting
+    static final String WHERE_INVISIBLE_UNCONFIRMED =
             "(" + BluetoothShare.STATUS + " > " + BluetoothShare.STATUS_SUCCESS + " AND "
                     + INVISIBLE + ") OR (" + WHERE_CONFIRM_PENDING_INBOUND + ")";
 
@@ -261,7 +266,7 @@
         mAdapterService = AdapterService.getAdapterService();
         mObserver = new BluetoothShareContentObserver();
         getContentResolver().registerContentObserver(BluetoothShare.CONTENT_URI, true, mObserver);
-        mNotifier = new BluetoothOppNotification(this);
+        mNotifier = BluetoothMethodProxy.getInstance().newBluetoothOppNotification(this);
         mNotifier.mNotificationMgr.cancelAll();
         mNotifier.updateNotification();
         updateFromProvider();
@@ -990,7 +995,8 @@
     /**
      * Removes the local copy of the info about a share.
      */
-    private void deleteShare(int arrayPos) {
+    @VisibleForTesting
+    void deleteShare(int arrayPos) {
         BluetoothOppShareInfo info = mShares.get(arrayPos);
 
         /*
@@ -1114,18 +1120,25 @@
     }
 
     // Run in a background thread at boot.
-    private static void trimDatabase(ContentResolver contentResolver) {
+    @VisibleForTesting
+    static void trimDatabase(ContentResolver contentResolver) {
+        if (contentResolver.acquireContentProviderClient(BluetoothShare.CONTENT_URI) == null) {
+            Log.w(TAG, "ContentProvider doesn't exist");
+            return;
+        }
+
         // remove the invisible/unconfirmed inbound shares
-        int delNum = contentResolver.delete(BluetoothShare.CONTENT_URI, WHERE_INVISIBLE_UNCONFIRMED,
-                null);
+        int delNum = BluetoothMethodProxy.getInstance().contentResolverDelete(
+                contentResolver, BluetoothShare.CONTENT_URI, WHERE_INVISIBLE_UNCONFIRMED, null);
+
         if (V) {
             Log.v(TAG, "Deleted shares, number = " + delNum);
         }
 
         // Keep the latest inbound and successful shares.
-        Cursor cursor =
-                contentResolver.query(BluetoothShare.CONTENT_URI, new String[]{BluetoothShare._ID},
-                        WHERE_INBOUND_SUCCESS, null, BluetoothShare._ID); // sort by id
+        Cursor cursor = BluetoothMethodProxy.getInstance().contentResolverQuery(
+                contentResolver, BluetoothShare.CONTENT_URI, new String[]{BluetoothShare._ID},
+                WHERE_INBOUND_SUCCESS, null, BluetoothShare._ID); // sort by id
         if (cursor == null) {
             return;
         }
@@ -1136,8 +1149,8 @@
             if (cursor.moveToPosition(numToDelete)) {
                 int columnId = cursor.getColumnIndexOrThrow(BluetoothShare._ID);
                 long id = cursor.getLong(columnId);
-                delNum = contentResolver.delete(BluetoothShare.CONTENT_URI,
-                        BluetoothShare._ID + " < " + id, null);
+                delNum = BluetoothMethodProxy.getInstance().contentResolverDelete(contentResolver,
+                        BluetoothShare.CONTENT_URI, BluetoothShare._ID + " < " + id, null);
                 if (V) {
                     Log.v(TAG, "Deleted old inbound success share: " + delNum);
                 }
diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
index 00fb596..eb10b23 100644
--- a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
+++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java
@@ -92,23 +92,27 @@
 
     private BluetoothAdapter mAdapter;
 
-    private BluetoothDevice mDevice;
+    @VisibleForTesting
+    BluetoothDevice mDevice;
 
     private BluetoothOppBatch mBatch;
 
     private BluetoothOppObexSession mSession;
 
-    private BluetoothOppShareInfo mCurrentShare;
+    @VisibleForTesting
+    BluetoothOppShareInfo mCurrentShare;
 
     private ObexTransport mTransport;
 
     private HandlerThread mHandlerThread;
 
-    private EventHandler mSessionHandler;
+    @VisibleForTesting
+    EventHandler mSessionHandler;
 
     private long mTimestamp;
 
-    private class OppConnectionReceiver extends BroadcastReceiver {
+    @VisibleForTesting
+    class OppConnectionReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
@@ -139,8 +143,8 @@
                         // Remove the timeout message triggered earlier during Obex Put
                         mSessionHandler.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
                         // Now reuse the same message to clean up the session.
-                        mSessionHandler.sendMessage(mSessionHandler.obtainMessage(
-                                BluetoothOppObexSession.MSG_CONNECT_TIMEOUT));
+                        BluetoothMethodProxy.getInstance().handlerSendEmptyMessage(mSessionHandler,
+                                BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
                     }
                 } catch (Exception e) {
                     e.printStackTrace();
@@ -160,7 +164,11 @@
                         Log.w(TAG, "OPP SDP search, target device is null, ignoring result");
                         return;
                     }
-                    if (!device.getIdentityAddress().equalsIgnoreCase(mDevice.getIdentityAddress())) {
+                    String deviceIdentityAddress = device.getIdentityAddress();
+                    String transferDeviceIdentityAddress = mDevice.getIdentityAddress();
+                    if (deviceIdentityAddress == null || transferDeviceIdentityAddress == null
+                            || !deviceIdentityAddress.equalsIgnoreCase(
+                                    transferDeviceIdentityAddress)) {
                         Log.w(TAG, " OPP SDP search for wrong device, ignoring!!");
                         return;
                     }
@@ -676,10 +684,12 @@
     @VisibleForTesting
     SocketConnectThread mConnectThread;
 
-    private class SocketConnectThread extends Thread {
+    @VisibleForTesting
+    class SocketConnectThread extends Thread {
         private final String mHost;
 
-        private final BluetoothDevice mDevice;
+        @VisibleForTesting
+        final BluetoothDevice mDevice;
 
         private final int mChannel;
 
@@ -695,7 +705,8 @@
 
         private boolean mSdpInitiated = false;
 
-        private boolean mIsInterrupted = false;
+        @VisibleForTesting
+        boolean mIsInterrupted = false;
 
         /* create a Rfcomm/L2CAP Socket */
         SocketConnectThread(BluetoothDevice device, boolean retry) {
@@ -863,7 +874,8 @@
                 Log.e(TAG, "Error when close socket");
             }
         }
-        mSessionHandler.obtainMessage(TRANSPORT_ERROR).sendToTarget();
+        BluetoothMethodProxy.getInstance().handlerSendEmptyMessage(mSessionHandler,
+                TRANSPORT_ERROR);
         return;
     }
 
diff --git a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
index 46eb260..7cc5bac 100644
--- a/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
+++ b/android/app/src/com/android/bluetooth/telephony/BluetoothInCallService.java
@@ -708,7 +708,7 @@
             // We do, however want to send conferences that have no children to the bluetooth
             // device (e.g. IMS Conference).
             boolean isConferenceWithNoChildren = isConferenceWithNoChildren(call);
-            Log.i(TAG, "sendListOfCalls isConferenceWithNoChildren " + isConferenceWithNoChildren 
+            Log.i(TAG, "sendListOfCalls isConferenceWithNoChildren " + isConferenceWithNoChildren
                 + ", call.getChildrenIds() size " + call.getChildrenIds().size());
             if (!call.isConference() || isConferenceWithNoChildren) {
                 sendClccForCall(call, shouldLog);
@@ -1479,7 +1479,9 @@
         mBluetoothLeCallControl.currentCallsList(tbsCalls);
     }
 
-    private final BluetoothLeCallControl.Callback mBluetoothLeCallControlCallback = new BluetoothLeCallControl.Callback() {
+    @VisibleForTesting
+    final BluetoothLeCallControl.Callback mBluetoothLeCallControlCallback =
+            new BluetoothLeCallControl.Callback() {
 
         @Override
         public void onAcceptCall(int requestId, UUID callId) {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/BluetoothPrefsTest.java b/android/app/tests/unit/src/com/android/bluetooth/BluetoothPrefsTest.java
deleted file mode 100644
index dc8ba43..0000000
--- a/android/app/tests/unit/src/com/android/bluetooth/BluetoothPrefsTest.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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/audio_util/GPMWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/GPMWrapperTest.java
index 0ef8a29..7f03b37 100644
--- 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
@@ -111,6 +111,6 @@
 
         GPMWrapper wrapper = new GPMWrapper(mContext, mMediaController, null);
 
-        assertThat(wrapper.isMetadataSynced()).isFalse();
+        assertThat(wrapper.isMetadataSynced()).isTrue();
     }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpVolumeManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpVolumeManagerTest.java
new file mode 100644
index 0000000..dd57998
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/avrcp/AvrcpVolumeManagerTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.avrcp;
+
+import static com.android.bluetooth.avrcp.AvrcpVolumeManager.AVRCP_MAX_VOL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.media.AudioManager;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+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;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AvrcpVolumeManagerTest {
+    private static final String REMOTE_DEVICE_ADDRESS = "00:01:02:03:04:05";
+    private static final int TEST_DEVICE_MAX_VOUME = 25;
+
+    @Mock
+    AvrcpNativeInterface mNativeInterface;
+
+    @Mock
+    AudioManager mAudioManager;
+
+    Context mContext;
+    BluetoothDevice mRemoteDevice;
+    AvrcpVolumeManager mAvrcpVolumeManager;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContext = InstrumentationRegistry.getTargetContext();
+        when(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
+                .thenReturn(TEST_DEVICE_MAX_VOUME);
+        mRemoteDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(REMOTE_DEVICE_ADDRESS);
+        mAvrcpVolumeManager = new AvrcpVolumeManager(mContext, mAudioManager, mNativeInterface);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mAvrcpVolumeManager.removeStoredVolumeForDevice(mRemoteDevice);
+    }
+
+    @Test
+    public void avrcpToSystemVolume() {
+        assertThat(AvrcpVolumeManager.avrcpToSystemVolume(0)).isEqualTo(0);
+        assertThat(AvrcpVolumeManager.avrcpToSystemVolume(AVRCP_MAX_VOL))
+                .isEqualTo(TEST_DEVICE_MAX_VOUME);
+    }
+
+    @Test
+    public void dump() {
+        StringBuilder sb = new StringBuilder();
+        mAvrcpVolumeManager.dump(sb);
+
+        assertThat(sb.toString()).isNotEmpty();
+    }
+
+    @Test
+    public void sendVolumeChanged() {
+        mAvrcpVolumeManager.sendVolumeChanged(mRemoteDevice, TEST_DEVICE_MAX_VOUME);
+
+        verify(mNativeInterface).sendVolumeChanged(REMOTE_DEVICE_ADDRESS, AVRCP_MAX_VOL);
+    }
+
+    @Test
+    public void setVolume() {
+        mAvrcpVolumeManager.setVolume(mRemoteDevice, AVRCP_MAX_VOL);
+
+        verify(mAudioManager).setStreamVolume(eq(AudioManager.STREAM_MUSIC),
+                eq(TEST_DEVICE_MAX_VOUME), anyInt());
+    }
+}
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 8c5ee71..e4a706d 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
@@ -242,6 +242,31 @@
         Assert.assertFalse("Connect expected to fail", mService.connect(mDevice));
     }
 
+    @Test
+    public void getConnectionState_whenNoDevicesAreConnected_returnsDisconnectedState() {
+        Assert.assertEquals(mService.getConnectionState(mDevice),
+                BluetoothProfile.STATE_DISCONNECTED);
+    }
+
+    @Test
+    public void getDevices_whenNoDevicesAreConnected_returnsEmptyList() {
+        Assert.assertTrue(mService.getDevices().isEmpty());
+    }
+
+    @Test
+    public void getDevicesMatchingConnectionStates() {
+        when(mAdapterService.getBondedDevices()).thenReturn(new BluetoothDevice[] {mDevice});
+        int states[] = new int[] {BluetoothProfile.STATE_DISCONNECTED};
+
+        Assert.assertTrue(mService.getDevicesMatchingConnectionStates(states).contains(mDevice));
+    }
+
+    @Test
+    public void setConnectionPolicy() {
+        Assert.assertTrue(mService.setConnectionPolicy(
+                mDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN));
+    }
+
     /**
      *  Helper function to test okToConnect() method
      *
diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/PeriodicAdvertisementResultTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/PeriodicAdvertisementResultTest.java
new file mode 100644
index 0000000..b80c949
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/PeriodicAdvertisementResultTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.bass_client;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PeriodicAdvertisementResultTest {
+    private static final String REMOTE_DEVICE_ADDRESS = "00:01:02:03:04:05";
+
+    BluetoothDevice mDevice;
+
+    @Before
+    public void setUp() {
+        mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(REMOTE_DEVICE_ADDRESS);
+    }
+
+    @Test
+    public void constructor() {
+        int addressType = 1;
+        int syncHandle = 2;
+        int advSid = 3;
+        int paInterval = 4;
+        int broadcastId = 5;
+        PeriodicAdvertisementResult result = new PeriodicAdvertisementResult(
+                mDevice, addressType, syncHandle, advSid, paInterval, broadcastId);
+
+        assertThat(result.getAddressType()).isEqualTo(addressType);
+        assertThat(result.getSyncHandle()).isEqualTo(syncHandle);
+        assertThat(result.getAdvSid()).isEqualTo(advSid);
+        assertThat(result.getAdvInterval()).isEqualTo(paInterval);
+        assertThat(result.getBroadcastId()).isEqualTo(broadcastId);
+    }
+
+    @Test
+    public void updateMethods() {
+        int addressType = 1;
+        int syncHandle = 2;
+        int advSid = 3;
+        int paInterval = 4;
+        int broadcastId = 5;
+        PeriodicAdvertisementResult result = new PeriodicAdvertisementResult(
+                mDevice, addressType, syncHandle, advSid, paInterval, broadcastId);
+
+        int newAddressType = 6;
+        result.updateAddressType(newAddressType);
+        assertThat(result.getAddressType()).isEqualTo(newAddressType);
+
+        int newSyncHandle = 7;
+        result.updateSyncHandle(newSyncHandle);
+        assertThat(result.getSyncHandle()).isEqualTo(newSyncHandle);
+
+        int newAdvSid = 8;
+        result.updateAdvSid(newAdvSid);
+        assertThat(result.getAdvSid()).isEqualTo(newAdvSid);
+
+        int newAdvInterval = 9;
+        result.updateAdvInterval(newAdvInterval);
+        assertThat(result.getAdvInterval()).isEqualTo(newAdvInterval);
+
+        int newBroadcastId = 10;
+        result.updateBroadcastId(newBroadcastId);
+        assertThat(result.getBroadcastId()).isEqualTo(newBroadcastId);
+    }
+
+    @Test
+    public void print_doesNotCrash() {
+        int addressType = 1;
+        int syncHandle = 2;
+        int advSid = 3;
+        int paInterval = 4;
+        int broadcastId = 5;
+        PeriodicAdvertisementResult result = new PeriodicAdvertisementResult(
+                mDevice, addressType, syncHandle, advSid, paInterval, broadcastId);
+
+        result.print();
+    }
+}
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 29414ca..4d30966 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
@@ -738,6 +738,7 @@
     /**
      * Test: Check if obfuscated Bluetooth address stays the same after toggling Bluetooth
      */
+    @Ignore("b/265588558")
     @Test
     public void testObfuscateBluetoothAddress_PersistentBetweenToggle() {
         Assert.assertFalse(mAdapterService.getState() == BluetoothAdapter.STATE_ON);
diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java
index a73d24c..141ee85 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
@@ -58,6 +58,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;
@@ -88,7 +89,6 @@
     @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;
@@ -119,7 +119,6 @@
 
         mService.mClientMap = mClientMap;
         mService.mScannerMap = mScannerMap;
-        mService.mPeriodicScanManager = mPeriodicScanManager;
         mService.mScanManager = mScanManager;
         mService.mReliableQueue = mReliableQueue;
         mService.mServerMap = mServerMap;
@@ -626,6 +625,7 @@
                 mAttributionSource);
     }
 
+    @Ignore("b/265327402")
     @Test
     public void registerSync() {
         ScanResult scanResult = new ScanResult(mDevice, 1, 2, 3, 4, 5, 6, 7, null, 8);
@@ -634,7 +634,6 @@
         IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
 
         mService.registerSync(scanResult, skip, timeout, callback, mAttributionSource);
-        verify(mPeriodicScanManager).startSync(scanResult, skip, timeout, callback);
     }
 
     @Test
@@ -643,9 +642,9 @@
         int syncHandle = 2;
 
         mService.transferSync(mDevice, serviceData, syncHandle, mAttributionSource);
-        verify(mPeriodicScanManager).transferSync(mDevice, serviceData, syncHandle);
     }
 
+    @Ignore("b/265327402")
     @Test
     public void transferSetInfo() {
         int serviceData = 1;
@@ -654,15 +653,14 @@
 
         mService.transferSetInfo(mDevice, serviceData, advHandle, callback,
                 mAttributionSource);
-        verify(mPeriodicScanManager).transferSetInfo(mDevice, serviceData, advHandle, callback);
     }
 
+    @Ignore("b/265327402")
     @Test
     public void unregisterSync() {
         IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class);
 
         mService.unregisterSync(callback, mAttributionSource);
-        verify(mPeriodicScanManager).stopSync(callback);
     }
 
     @Test
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
index 971e53b..a3e58d9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java
@@ -57,6 +57,7 @@
 import org.junit.Assert;
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -1186,6 +1187,7 @@
         Assert.assertEquals(mHeadsetStateMachine.parseUnknownAt(atString), "A\"command\"");
     }
 
+    @Ignore("b/265556073")
     @Test
     public void testHandleAccessPermissionResult_withNoChangeInAtCommandResult() {
         when(mIntent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).thenReturn(null);
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 5661492..8068857 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
@@ -31,6 +31,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.provider.ContactsContract;
 import android.provider.Telephony;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.Sms;
@@ -45,6 +46,7 @@
 import com.android.bluetooth.SignedLongLong;
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
 import com.android.bluetooth.mapapi.BluetoothMapContract;
+import com.android.bluetooth.mapapi.BluetoothMapContract.MessageColumns;
 import com.android.obex.ResponseCodes;
 
 import com.google.android.mms.pdu.PduHeaders;
@@ -92,6 +94,8 @@
     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 int TEST_SMS_TYPE_ALL = Telephony.TextBasedSmsColumns.MESSAGE_TYPE_ALL;
+    static final int TEST_SMS_TYPE_INBOX = Telephony.BaseMmsColumns.MESSAGE_BOX_INBOX;
     static final Uri TEST_URI = Mms.CONTENT_URI;
     static final String TEST_AUTHORITY = "test_authority";
 
@@ -822,9 +826,75 @@
     }
 
     @Test
+    public void initMsgList_withMsgSms() throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+                Sms.READ});
+        cursor.addRow(new Object[] {(long) TEST_ID, TEST_SMS_TYPE_ALL, TEST_THREAD_ID,
+                TEST_READ_FLAG_ONE});
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContentObserver.SMS_PROJECTION_SHORT), any(), any(), any());
+        cursor.moveToFirst();
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        mObserver.setMsgListMsg(map, true);
+
+        mObserver.initMsgList();
+
+        BluetoothMapContentObserver.Msg msg = mObserver.getMsgListSms().get((long) TEST_ID);
+        Assert.assertEquals(msg.id, TEST_ID);
+        Assert.assertEquals(msg.type, TEST_SMS_TYPE_ALL);
+        Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
+        Assert.assertEquals(msg.flagRead, TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void initMsgList_withMsgMms() throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Mms._ID, Mms.MESSAGE_BOX,
+                Mms.THREAD_ID, Mms.READ});
+        cursor.addRow(new Object[] {(long) TEST_ID, TEST_MMS_TYPE_ALL, TEST_THREAD_ID,
+                TEST_READ_FLAG_ZERO});
+        doReturn(null).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContentObserver.SMS_PROJECTION_SHORT), any(), any(), any());
+        doReturn(cursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContentObserver.MMS_PROJECTION_SHORT), any(), any(), any());
+        cursor.moveToFirst();
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        mObserver.setMsgListMsg(map, true);
+
+        mObserver.initMsgList();
+
+        BluetoothMapContentObserver.Msg msg = mObserver.getMsgListMms().get((long) TEST_ID);
+        Assert.assertEquals(msg.id, TEST_ID);
+        Assert.assertEquals(msg.type, TEST_MMS_TYPE_ALL);
+        Assert.assertEquals(msg.threadId, TEST_THREAD_ID);
+        Assert.assertEquals(msg.flagRead, TEST_READ_FLAG_ZERO);
+    }
+
+    @Test
+    public void initMsgList_withMsg() throws Exception {
+        MatrixCursor cursor = new MatrixCursor(new String[] {MessageColumns._ID,
+                MessageColumns.FOLDER_ID, MessageColumns.FLAG_READ});
+        cursor.addRow(new Object[] {(long) TEST_ID, TEST_INBOX_FOLDER_ID, TEST_READ_FLAG_ONE});
+        doReturn(null).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContentObserver.SMS_PROJECTION_SHORT), any(), any(), any());
+        doReturn(null).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContentObserver.MMS_PROJECTION_SHORT), any(), any(), any());
+        when(mProviderClient.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+        cursor.moveToFirst();
+        Map<Long, BluetoothMapContentObserver.Msg> map = new HashMap<>();
+        mObserver.setMsgListMsg(map, true);
+
+        mObserver.initMsgList();
+
+        BluetoothMapContentObserver.Msg msg = mObserver.getMsgListMsg().get((long) TEST_ID);
+        Assert.assertEquals(msg.id, TEST_ID);
+        Assert.assertEquals(msg.folderId, TEST_INBOX_FOLDER_ID);
+        Assert.assertEquals(msg.flagRead, TEST_READ_FLAG_ONE);
+    }
+
+    @Test
     public void initContactsList() throws Exception {
         MatrixCursor cursor = new MatrixCursor(
-                new String[]{BluetoothMapContract.ConvoContactColumns.CONVO_ID,
+                new String[] {BluetoothMapContract.ConvoContactColumns.CONVO_ID,
                         BluetoothMapContract.ConvoContactColumns.NAME,
                         BluetoothMapContract.ConvoContactColumns.NICKNAME,
                         BluetoothMapContract.ConvoContactColumns.X_BT_UID,
@@ -1102,7 +1172,7 @@
     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});
+                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});
@@ -1304,6 +1374,179 @@
     }
 
     @Test
+    public void handleMsgListChangesSms_withNonExistingMessage_andVersion11() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+                Sms.READ, Sms.DATE, Sms.BODY, Sms.ADDRESS, ContactsContract.Contacts.DISPLAY_NAME});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SMS_TYPE_INBOX, TEST_THREAD_ID,
+                TEST_READ_FLAG_ONE, TEST_DATE, TEST_SUBJECT, TEST_ADDRESS, 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 handleMsgListChangesSms()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_SMS_TYPE_ALL, TEST_READ_FLAG_ONE);
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListSms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V11;
+
+        mObserver.handleMsgListChangesSms();
+
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type,
+                TEST_SMS_TYPE_INBOX);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesSms_withNonExistingMessage_andVersion12() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+                Sms.READ, Sms.DATE, Sms.BODY, Sms.ADDRESS});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SMS_TYPE_ALL, TEST_THREAD_ID,
+                TEST_READ_FLAG_ONE, TEST_DATE, "", 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 handleMsgListChangesSms()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_SMS_TYPE_INBOX, TEST_READ_FLAG_ONE);
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListSms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesSms();
+
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type,
+                TEST_SMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesSms_withNonExistingMessage_andVersion10() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+                Sms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SMS_TYPE_ALL, 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 handleMsgListChangesSms()
+        // function for a non-existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_TWO,
+                TEST_SMS_TYPE_INBOX, TEST_READ_FLAG_ONE);
+        map.put(TEST_HANDLE_TWO, msg);
+        mObserver.setMsgListSms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V10;
+
+        mObserver.handleMsgListChangesSms();
+
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type,
+                TEST_SMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesSms_withExistingMessage_withNonEqualType() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+                Sms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SMS_TYPE_ALL, 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 handleMsgListChangesSms()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_SMS_TYPE_INBOX, TEST_THREAD_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        mObserver.setMsgListSms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesSms();
+
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type,
+                TEST_SMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId,
+                TEST_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesSms_withExistingMessage_withDeletedThreadId() {
+        MatrixCursor cursor = new MatrixCursor(new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+                Sms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SMS_TYPE_ALL,
+                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 handleMsgListChangesSms()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_SMS_TYPE_ALL, TEST_THREAD_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        mObserver.setMsgListSms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesSms();
+
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId,
+                BluetoothMapContentObserver.DELETED_THREAD_ID);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
+    public void handleMsgListChangesSms_withExistingMessage_withUndeletedThreadId() {
+        int undeletedThreadId = 0;
+        MatrixCursor cursor = new MatrixCursor(new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+                Sms.READ});
+        cursor.addRow(new Object[] {TEST_HANDLE_ONE, TEST_SMS_TYPE_ALL, 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 handleMsgListChangesSms()
+        // function for an existing message
+        BluetoothMapContentObserver.Msg msg = new BluetoothMapContentObserver.Msg(TEST_HANDLE_ONE,
+                TEST_SMS_TYPE_ALL, TEST_THREAD_ID, TEST_READ_FLAG_ZERO);
+        map.put(TEST_HANDLE_ONE, msg);
+        mObserver.setMsgListSms(map, true);
+        mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+
+        mObserver.handleMsgListChangesSms();
+
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).id, TEST_HANDLE_ONE);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).type, TEST_SMS_TYPE_ALL);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).threadId,
+                undeletedThreadId);
+        Assert.assertEquals(mObserver.getMsgListSms().get(TEST_HANDLE_ONE).flagRead,
+                TEST_READ_FLAG_ONE);
+    }
+
+    @Test
     public void handleMmsSendIntent_withMnsClientNotConnected() {
         when(mClient.isConnected()).thenReturn(false);
 
@@ -1348,6 +1591,41 @@
     }
 
     @Test
+    public void actionMessageSentDisconnected_withTypeMms() {
+        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);
+        // This mock sets type to MMS
+        doReturn(4).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_MSG_TYPE, TYPE.NONE.ordinal());
+
+        mObserver.actionMessageSentDisconnected(mContext, mIntent, 1);
+
+        Assert.assertTrue(mmsMsgList.containsKey(TEST_HANDLE_ONE));
+    }
+
+    @Test
+    public void actionMessageSentDisconnected_withTypeEmail() {
+        // 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);
+        // This mock sets type to Email
+        doReturn(1).when(mIntent).getIntExtra(
+                BluetoothMapContentObserver.EXTRA_MESSAGE_SENT_MSG_TYPE, TYPE.NONE.ordinal());
+        clearInvocations(mContext);
+
+        mObserver.actionMessageSentDisconnected(mContext, mIntent, Activity.RESULT_FIRST_USER);
+
+        verify(mContext, never()).getContentResolver();
+    }
+
+    @Test
     public void actionMmsSent_withInvalidHandle() {
         Map<Long, BluetoothMapContentObserver.Msg> mmsMsgList = new HashMap<>();
         BluetoothMapContentObserver.Msg msg = createSimpleMsg();
@@ -1571,6 +1849,7 @@
         map.put(TEST_UCI, contact);
         mObserver.setContactList(map, true);
         mObserver.mMapEventReportVersion = BluetoothMapUtils.MAP_EVENT_REPORT_V12;
+        when(mTelephonyManager.getLine1Number()).thenReturn("");
 
         mObserver.handleContactListChanges(uri);
 
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 6824582..9e1f738 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
@@ -48,6 +48,8 @@
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
 import com.android.bluetooth.mapapi.BluetoothMapContract;
 
+import com.google.android.mms.pdu.PduHeaders;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -70,8 +72,10 @@
     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_SMS = 4;
+    private static final long TEST_DATE_MMS = 3;
     private static final long TEST_DATE_EMAIL = 2;
+    private static final long TEST_DATE_IM = 1;
     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";
@@ -87,6 +91,21 @@
     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";
+    private static final int TEST_SIZE = 1;
+    private static final int TEST_TEXT_ONLY = 1;
+    private static final int TEST_READ_TRUE = 1;
+    private static final int TEST_READ_FALSE = 0;
+    private static final int TEST_PRIORITY_HIGH = 1;
+    private static final int TEST_SENT_YES = 2;
+    private static final int TEST_SENT_NO = 1;
+    private static final int TEST_PROTECTED = 1;
+    private static final int TEST_ATTACHMENT_TRUE = 1;
+    private static final String TEST_DELIVERY_STATE = "delivered";
+    private static final long TEST_THREAD_ID = 1;
+    private static final String TEST_ATTACHMENT_MIME_TYPE = "test_mime_type";
+    private static final String TEST_YES = "yes";
+    private static final String TEST_NO = "no";
+    private static final String TEST_RECEPTION_STATUS = "complete";
 
     @Mock
     private BluetoothMapAccountItem mAccountItem;
@@ -574,7 +593,7 @@
     }
 
     @Test
-    public void setSenderAddressing_withFilterMSgTypeSms_andSmsMsgTypeDraft() {
+    public void setSenderAddressing_withFilterMsgTypeSms_andSmsMsgTypeDraft() {
         when(mParams.getParameterMask()).thenReturn(
                 (long) BluetoothMapContent.MASK_SENDER_ADDRESSING);
         mInfo.mMsgType = FilterInfo.TYPE_SMS;
@@ -1089,12 +1108,12 @@
         BluetoothMapConvoListing listing = mContent.convoListing(mParams, false);
 
         assertThat(listing.getCount()).isEqualTo(2);
-        BluetoothMapConvoListingElement emailElement = listing.getList().get(0);
+        BluetoothMapConvoListingElement emailElement = listing.getList().get(1);
         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);
+        BluetoothMapConvoListingElement smsElement = listing.getList().get(0);
         assertThat(smsElement.getType()).isEqualTo(TYPE.SMS_GSM);
         assertThat(smsElement.getLastActivity()).isEqualTo(TEST_DATE_SMS);
         assertThat(smsElement.getName()).isEqualTo("");
@@ -1156,15 +1175,310 @@
         BluetoothMapConvoListing listing = mContent.convoListing(mParams, false);
 
         assertThat(listing.getCount()).isEqualTo(2);
-        BluetoothMapConvoListingElement imElement = listing.getList().get(0);
+        BluetoothMapConvoListingElement imElement = listing.getList().get(1);
         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);
+        BluetoothMapConvoListingElement smsElement = listing.getList().get(0);
         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 msgListing_withSmsCursorOnly() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        int noMms = BluetoothMapAppParams.FILTER_NO_MMS;
+        when(mParams.getFilterMessageType()).thenReturn(noMms);
+        when(mParams.getMaxListCount()).thenReturn(1);
+        when(mParams.getStartOffset()).thenReturn(0);
+
+        mCurrentFolder.setHasSmsMmsContent(true);
+        mCurrentFolder.setFolderId(TEST_ID);
+        mContent.mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V11;
+
+        MatrixCursor smsCursor = new MatrixCursor(new String[] {BaseColumns._ID, Telephony.Sms.TYPE,
+                Telephony.Sms.READ, Telephony.Sms.BODY, Telephony.Sms.ADDRESS, Telephony.Sms.DATE,
+                Telephony.Sms.THREAD_ID, ContactsContract.Contacts.DISPLAY_NAME});
+        smsCursor.addRow(new Object[] {TEST_ID, TEST_SENT_NO, TEST_READ_TRUE, TEST_SUBJECT,
+                TEST_ADDRESS, TEST_DATE_SMS, TEST_THREAD_ID, TEST_PHONE_NAME});
+        doReturn(smsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.SMS_PROJECTION), any(), any(), any());
+        doReturn(smsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(new String[] {ContactsContract.Contacts._ID,
+                        ContactsContract.Contacts.DISPLAY_NAME}), any(), any(), any());
+
+        BluetoothMapMessageListing listing = mContent.msgListing(mCurrentFolder, mParams);
+        assertThat(listing.getCount()).isEqualTo(1);
+
+        BluetoothMapMessageListingElement smsElement = listing.getList().get(0);
+        assertThat(smsElement.getHandle()).isEqualTo(TEST_ID);
+        assertThat(smsElement.getDateTime()).isEqualTo(TEST_DATE_SMS);
+        assertThat(smsElement.getType()).isEqualTo(TYPE.SMS_GSM);
+        assertThat(smsElement.getReadBool()).isTrue();
+        assertThat(smsElement.getSenderAddressing()).isEqualTo(
+                PhoneNumberUtils.extractNetworkPortion(TEST_ADDRESS));
+        assertThat(smsElement.getSenderName()).isEqualTo(TEST_PHONE_NAME);
+        assertThat(smsElement.getSize()).isEqualTo(TEST_SUBJECT.length());
+        assertThat(smsElement.getPriority()).isEqualTo(TEST_NO);
+        assertThat(smsElement.getSent()).isEqualTo(TEST_NO);
+        assertThat(smsElement.getProtect()).isEqualTo(TEST_NO);
+        assertThat(smsElement.getReceptionStatus()).isEqualTo(TEST_RECEPTION_STATUS);
+        assertThat(smsElement.getAttachmentSize()).isEqualTo(0);
+        assertThat(smsElement.getDeliveryStatus()).isEqualTo(TEST_DELIVERY_STATE);
+    }
+
+    @Test
+    public void msgListing_withMmsCursorOnly() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        int onlyMms =
+                BluetoothMapAppParams.FILTER_NO_EMAIL | BluetoothMapAppParams.FILTER_NO_SMS_CDMA
+                        | BluetoothMapAppParams.FILTER_NO_SMS_GSM
+                        | BluetoothMapAppParams.FILTER_NO_IM;
+        when(mParams.getFilterMessageType()).thenReturn(onlyMms);
+        when(mParams.getMaxListCount()).thenReturn(1);
+        when(mParams.getStartOffset()).thenReturn(0);
+
+        mCurrentFolder.setHasSmsMmsContent(true);
+        mCurrentFolder.setFolderId(TEST_ID);
+        mContent.mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V11;
+
+        MatrixCursor mmsCursor = new MatrixCursor(new String[] {BaseColumns._ID,
+                Telephony.Mms.MESSAGE_BOX, Telephony.Mms.READ, Telephony.Mms.MESSAGE_SIZE,
+                Telephony.Mms.TEXT_ONLY, Telephony.Mms.DATE, Telephony.Mms.SUBJECT,
+                Telephony.Mms.THREAD_ID, Telephony.Mms.Addr.ADDRESS,
+                ContactsContract.Contacts.DISPLAY_NAME, Telephony.Mms.PRIORITY});
+        mmsCursor.addRow(new Object[] {TEST_ID, TEST_SENT_NO, TEST_READ_FALSE, TEST_SIZE,
+                TEST_TEXT_ONLY, TEST_DATE_MMS, TEST_SUBJECT, TEST_THREAD_ID, TEST_PHONE,
+                TEST_PHONE_NAME, PduHeaders.PRIORITY_HIGH});
+        doReturn(mmsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.MMS_PROJECTION), any(), any(), any());
+        doReturn(mmsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(new String[] {Telephony.Mms.Addr.ADDRESS}), any(), any(), any());
+        doReturn(mmsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(new String[] {ContactsContract.Contacts._ID,
+                        ContactsContract.Contacts.DISPLAY_NAME}), any(), any(), any());
+
+        BluetoothMapMessageListing listing = mContent.msgListing(mCurrentFolder, mParams);
+        assertThat(listing.getCount()).isEqualTo(1);
+
+        BluetoothMapMessageListingElement mmsElement = listing.getList().get(0);
+        assertThat(mmsElement.getHandle()).isEqualTo(TEST_ID);
+        assertThat(mmsElement.getDateTime()).isEqualTo(TEST_DATE_MMS * 1000L);
+        assertThat(mmsElement.getType()).isEqualTo(TYPE.MMS);
+        assertThat(mmsElement.getReadBool()).isFalse();
+        assertThat(mmsElement.getSenderAddressing()).isEqualTo(TEST_PHONE);
+        assertThat(mmsElement.getSenderName()).isEqualTo(TEST_PHONE_NAME);
+        assertThat(mmsElement.getSize()).isEqualTo(TEST_SIZE);
+        assertThat(mmsElement.getPriority()).isEqualTo(TEST_YES);
+        assertThat(mmsElement.getSent()).isEqualTo(TEST_NO);
+        assertThat(mmsElement.getProtect()).isEqualTo(TEST_NO);
+        assertThat(mmsElement.getReceptionStatus()).isEqualTo(TEST_RECEPTION_STATUS);
+        assertThat(mmsElement.getAttachmentSize()).isEqualTo(0);
+        assertThat(mmsElement.getDeliveryStatus()).isEqualTo(TEST_DELIVERY_STATE);
+    }
+
+    @Test
+    public void msgListing_withEmailCursorOnly() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        int onlyEmail =
+                BluetoothMapAppParams.FILTER_NO_MMS | BluetoothMapAppParams.FILTER_NO_SMS_CDMA
+                        | BluetoothMapAppParams.FILTER_NO_SMS_GSM
+                        | BluetoothMapAppParams.FILTER_NO_IM;
+        when(mParams.getFilterMessageType()).thenReturn(onlyEmail);
+        when(mParams.getMaxListCount()).thenReturn(1);
+        when(mParams.getStartOffset()).thenReturn(0);
+
+        mCurrentFolder.setHasEmailContent(true);
+        mCurrentFolder.setFolderId(TEST_ID);
+        mContent.mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V11;
+
+        MatrixCursor emailCursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.DATE,
+                BluetoothMapContract.MessageColumns.SUBJECT,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.MESSAGE_SIZE,
+                BluetoothMapContract.MessageColumns.FROM_LIST,
+                BluetoothMapContract.MessageColumns.TO_LIST,
+                BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT,
+                BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE,
+                BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY,
+                BluetoothMapContract.MessageColumns.FLAG_PROTECTED,
+                BluetoothMapContract.MessageColumns.RECEPTION_STATE,
+                BluetoothMapContract.MessageColumns.DEVILERY_STATE,
+                BluetoothMapContract.MessageColumns.THREAD_ID,
+                BluetoothMapContract.MessageColumns.CC_LIST,
+                BluetoothMapContract.MessageColumns.BCC_LIST,
+                BluetoothMapContract.MessageColumns.REPLY_TO_LIST});
+        emailCursor.addRow(new Object[] {TEST_ID, TEST_DATE_EMAIL, TEST_SUBJECT, TEST_SENT_YES,
+                TEST_READ_TRUE, TEST_SIZE, TEST_FROM_ADDRESS, TEST_TO_ADDRESS, TEST_ATTACHMENT_TRUE,
+                0, TEST_PRIORITY_HIGH, TEST_PROTECTED, 0, TEST_DELIVERY_STATE,
+                TEST_THREAD_ID, TEST_CC_ADDRESS, TEST_BCC_ADDRESS, TEST_TO_ADDRESS});
+        doReturn(emailCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_MESSAGE_PROJECTION), any(), any(), any());
+
+        BluetoothMapMessageListing listing = mContent.msgListing(mCurrentFolder, mParams);
+        assertThat(listing.getCount()).isEqualTo(1);
+
+        BluetoothMapMessageListingElement emailElement = listing.getList().get(0);
+        assertThat(emailElement.getHandle()).isEqualTo(TEST_ID);
+        assertThat(emailElement.getDateTime()).isEqualTo(TEST_DATE_EMAIL);
+        assertThat(emailElement.getType()).isEqualTo(TYPE.EMAIL);
+        assertThat(emailElement.getReadBool()).isTrue();
+        StringBuilder expectedAddress = new StringBuilder();
+        expectedAddress.append(Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getAddress());
+        assertThat(emailElement.getSenderAddressing()).isEqualTo(expectedAddress.toString());
+        StringBuilder expectedName = new StringBuilder();
+        expectedName.append(Rfc822Tokenizer.tokenize(TEST_FROM_ADDRESS)[0].getName());
+        assertThat(emailElement.getSenderName()).isEqualTo(expectedName.toString());
+        assertThat(emailElement.getSize()).isEqualTo(TEST_SIZE);
+        assertThat(emailElement.getPriority()).isEqualTo(TEST_YES);
+        assertThat(emailElement.getSent()).isEqualTo(TEST_YES);
+        assertThat(emailElement.getProtect()).isEqualTo(TEST_YES);
+        assertThat(emailElement.getReceptionStatus()).isEqualTo(TEST_RECEPTION_STATUS);
+        assertThat(emailElement.getAttachmentSize()).isEqualTo(TEST_SIZE);
+        assertThat(emailElement.getDeliveryStatus()).isEqualTo(TEST_DELIVERY_STATE);
+    }
+
+    @Test
+    public void msgListing_withImCursorOnly() {
+        when(mParams.getParameterMask()).thenReturn(
+                (long) BluetoothMapAppParams.INVALID_VALUE_PARAMETER);
+        int onlyIm = BluetoothMapAppParams.FILTER_NO_MMS | BluetoothMapAppParams.FILTER_NO_SMS_CDMA
+                | BluetoothMapAppParams.FILTER_NO_SMS_GSM | BluetoothMapAppParams.FILTER_NO_EMAIL;
+        when(mParams.getFilterMessageType()).thenReturn(onlyIm);
+        when(mParams.getMaxListCount()).thenReturn(1);
+        when(mParams.getStartOffset()).thenReturn(0);
+
+        mCurrentFolder.setHasImContent(true);
+        mCurrentFolder.setFolderId(TEST_ID);
+        mContent.mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V11;
+
+        MatrixCursor imCursor = new MatrixCursor(new String[] {
+                BluetoothMapContract.MessageColumns._ID,
+                BluetoothMapContract.MessageColumns.DATE,
+                BluetoothMapContract.MessageColumns.SUBJECT,
+                BluetoothMapContract.MessageColumns.FOLDER_ID,
+                BluetoothMapContract.MessageColumns.FLAG_READ,
+                BluetoothMapContract.MessageColumns.MESSAGE_SIZE,
+                BluetoothMapContract.MessageColumns.FROM_LIST,
+                BluetoothMapContract.MessageColumns.TO_LIST,
+                BluetoothMapContract.MessageColumns.FLAG_ATTACHMENT,
+                BluetoothMapContract.MessageColumns.ATTACHMENT_SIZE,
+                BluetoothMapContract.MessageColumns.FLAG_HIGH_PRIORITY,
+                BluetoothMapContract.MessageColumns.FLAG_PROTECTED,
+                BluetoothMapContract.MessageColumns.RECEPTION_STATE,
+                BluetoothMapContract.MessageColumns.DEVILERY_STATE,
+                BluetoothMapContract.MessageColumns.THREAD_ID,
+                BluetoothMapContract.MessageColumns.THREAD_NAME,
+                BluetoothMapContract.MessageColumns.ATTACHMENT_MINE_TYPES,
+                BluetoothMapContract.MessageColumns.BODY,
+                BluetoothMapContract.ConvoContactColumns.UCI,
+                BluetoothMapContract.ConvoContactColumns.NAME});
+        imCursor.addRow(new Object[] {TEST_ID, TEST_DATE_IM, TEST_SUBJECT, TEST_SENT_NO,
+                TEST_READ_FALSE, TEST_SIZE, TEST_ID, TEST_TO_ADDRESS, TEST_ATTACHMENT_TRUE,
+                0 /*=attachment size*/, TEST_PRIORITY_HIGH, TEST_PROTECTED, 0, TEST_DELIVERY_STATE,
+                TEST_THREAD_ID, TEST_NAME, TEST_ATTACHMENT_MIME_TYPE, 0, TEST_ADDRESS, TEST_NAME});
+        doReturn(imCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION), any(), any(), any());
+        doReturn(imCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_CONTACT_PROJECTION), any(), any(), any());
+
+        BluetoothMapMessageListing listing = mContent.msgListing(mCurrentFolder, mParams);
+        assertThat(listing.getCount()).isEqualTo(1);
+
+        BluetoothMapMessageListingElement imElement = listing.getList().get(0);
+        assertThat(imElement.getHandle()).isEqualTo(TEST_ID);
+        assertThat(imElement.getDateTime()).isEqualTo(TEST_DATE_IM);
+        assertThat(imElement.getType()).isEqualTo(TYPE.IM);
+        assertThat(imElement.getReadBool()).isFalse();
+        assertThat(imElement.getSenderAddressing()).isEqualTo(TEST_ADDRESS);
+        assertThat(imElement.getSenderName()).isEqualTo(TEST_NAME);
+        assertThat(imElement.getSize()).isEqualTo(TEST_SIZE);
+        assertThat(imElement.getPriority()).isEqualTo(TEST_YES);
+        assertThat(imElement.getSent()).isEqualTo(TEST_NO);
+        assertThat(imElement.getProtect()).isEqualTo(TEST_YES);
+        assertThat(imElement.getReceptionStatus()).isEqualTo(TEST_RECEPTION_STATUS);
+        assertThat(imElement.getAttachmentSize()).isEqualTo(TEST_SIZE);
+        assertThat(imElement.getAttachmentMimeTypes()).isEqualTo(TEST_ATTACHMENT_MIME_TYPE);
+        assertThat(imElement.getDeliveryStatus()).isEqualTo(TEST_DELIVERY_STATE);
+        assertThat(imElement.getThreadName()).isEqualTo(TEST_NAME);
+    }
+
+    @Test
+    public void msgListingSize() {
+        when(mParams.getFilterMessageType()).thenReturn(TEST_NO_FILTER);
+        mCurrentFolder.setHasSmsMmsContent(true);
+        mCurrentFolder.setHasEmailContent(true);
+        mCurrentFolder.setHasImContent(true);
+        mCurrentFolder.setFolderId(TEST_ID);
+
+        MatrixCursor smsCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        smsCursor.addRow(new Object[] {1});
+        doReturn(smsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.SMS_PROJECTION), any(), any(), any());
+
+        MatrixCursor mmsCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        mmsCursor.addRow(new Object[] {1});
+        doReturn(mmsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.MMS_PROJECTION), any(), any(), any());
+
+        MatrixCursor emailCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        emailCursor.addRow(new Object[] {1});
+        doReturn(emailCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_MESSAGE_PROJECTION), any(), any(), any());
+
+        MatrixCursor imCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        imCursor.addRow(new Object[] {1});
+        doReturn(imCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION), any(), any(), any());
+
+        assertThat(mContent.msgListingSize(mCurrentFolder, mParams)).isEqualTo(4);
+    }
+
+    @Test
+    public void msgListingHasUnread() {
+        when(mParams.getFilterMessageType()).thenReturn(TEST_NO_FILTER);
+        mCurrentFolder.setHasSmsMmsContent(true);
+        mCurrentFolder.setHasEmailContent(true);
+        mCurrentFolder.setHasImContent(true);
+        mCurrentFolder.setFolderId(TEST_ID);
+
+        MatrixCursor smsCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        smsCursor.addRow(new Object[] {1});
+        doReturn(smsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.SMS_PROJECTION), any(), any(), any());
+
+        MatrixCursor mmsCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        mmsCursor.addRow(new Object[] {1});
+        doReturn(mmsCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContent.MMS_PROJECTION), any(), any(), any());
+
+        MatrixCursor emailCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        emailCursor.addRow(new Object[] {1});
+        doReturn(emailCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_MESSAGE_PROJECTION), any(), any(), any());
+
+        MatrixCursor imCursor = new MatrixCursor(new String[] {"Placeholder"});
+        // Making cursor.getCount() as 1
+        imCursor.addRow(new Object[] {1});
+        doReturn(imCursor).when(mMapMethodProxy).contentResolverQuery(any(), any(),
+                eq(BluetoothMapContract.BT_INSTANT_MESSAGE_PROJECTION), any(), any(), any());
+
+        assertThat(mContent.msgListingHasUnread(mCurrentFolder, mParams)).isTrue();
+    }
 }
\ No newline at end of file
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
index 0a6f6bd..4b252fc 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapObexServerTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
@@ -39,6 +40,7 @@
 import com.android.bluetooth.BluetoothMethodProxy;
 import com.android.bluetooth.mapapi.BluetoothMapContract;
 import com.android.obex.ResponseCodes;
+import com.android.obex.Operation;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -185,6 +187,53 @@
         assertThat(parentFolder.getFolderById(childId)).isNotNull();
     }
 
+    @Test
+    public void setMsgTypeFilterParams_withAccountNull_andOverwriteTrue() throws Exception {
+        BluetoothMapObexServer obexServer = new BluetoothMapObexServer(null, mContext, mObserver,
+                mMasInstance, null, false);
+
+        obexServer.setMsgTypeFilterParams(mParams, true);
+
+        int expectedMask = 0;
+        expectedMask |= BluetoothMapAppParams.FILTER_NO_SMS_CDMA;
+        expectedMask |= BluetoothMapAppParams.FILTER_NO_SMS_GSM;
+        expectedMask |= BluetoothMapAppParams.FILTER_NO_MMS;
+        expectedMask |= BluetoothMapAppParams.FILTER_NO_EMAIL;
+        expectedMask |= BluetoothMapAppParams.FILTER_NO_IM;
+        assertThat(mParams.getFilterMessageType()).isEqualTo(expectedMask);
+    }
+
+    @Test
+    public void setMsgTypeFilterParams_withInvalidFilterMessageType() throws Exception {
+        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);
+
+        // Passing mParams without any previous settings pass invalid filter message type
+        assertThrows(IllegalArgumentException.class,
+                () -> obexServer.setMsgTypeFilterParams(mParams, false));
+    }
+
+    @Test
+    public void setMsgTypeFilterParams_withValidFilterMessageType() throws Exception {
+        BluetoothMapAccountItem accountItemWithTypeIm = BluetoothMapAccountItem.create(TEST_ID,
+                TEST_NAME, TEST_PACKAGE_NAME, TEST_PROVIDER_AUTHORITY, TEST_DRAWABLE,
+                BluetoothMapUtils.TYPE.IM, TEST_UCI, TEST_UCI_PREFIX);
+        BluetoothMapObexServer obexServer = new BluetoothMapObexServer(null, mContext, mObserver,
+                mMasInstance, accountItemWithTypeIm, TEST_ENABLE_SMS_MMS);
+        int expectedMask = 1;
+        mParams.setFilterMessageType(expectedMask);
+
+        obexServer.setMsgTypeFilterParams(mParams, false);
+
+        int masFilterMask = 0;
+        masFilterMask |= BluetoothMapAppParams.FILTER_NO_EMAIL;
+        expectedMask |= masFilterMask;
+        assertThat(mParams.getFilterMessageType()).isEqualTo(expectedMask);
+    }
+
     private void setUpBluetoothMapAppParams(BluetoothMapAppParams params) {
         params.setPresenceAvailability(1);
         params.setPresenceStatus("test_presence_status");
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java
new file mode 100644
index 0000000..9c6a307
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesFilterTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.mapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MessagesFilterTest {
+
+    @Test
+    public void setOriginator() {
+        MessagesFilter filter = new MessagesFilter();
+
+        String originator = "test_originator";
+        filter.setOriginator(originator);
+        assertThat(filter.originator).isEqualTo(originator);
+
+        filter.setOriginator("");
+        assertThat(filter.originator).isEqualTo(null); // Empty string is stored as null
+
+        filter.setOriginator(null);
+        assertThat(filter.originator).isEqualTo(null);
+    }
+
+    @Test
+    public void setPriority() {
+        MessagesFilter filter = new MessagesFilter();
+
+        byte priority = 5;
+        filter.setPriority(priority);
+
+        assertThat(filter.priority).isEqualTo(priority);
+    }
+
+    @Test
+    public void setReadStatus() {
+        MessagesFilter filter = new MessagesFilter();
+
+        byte readStatus = 5;
+        filter.setReadStatus(readStatus);
+
+        assertThat(filter.readStatus).isEqualTo(readStatus);
+    }
+
+    @Test
+    public void setRecipient() {
+        MessagesFilter filter = new MessagesFilter();
+
+        String recipient = "test_originator";
+        filter.setRecipient(recipient);
+        assertThat(filter.recipient).isEqualTo(recipient);
+
+        filter.setRecipient("");
+        assertThat(filter.recipient).isEqualTo(null); // Empty string is stored as null
+
+        filter.setRecipient(null);
+        assertThat(filter.recipient).isEqualTo(null);
+    }
+
+}
diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesListingTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesListingTest.java
new file mode 100644
index 0000000..48ece84
--- /dev/null
+++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MessagesListingTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.mapclient;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MessagesListingTest {
+
+    @Test
+    public void constructor() {
+        String handle = "FFAB";
+        String subject = "test_subject";
+        final StringBuilder xml = new StringBuilder();
+        xml.append("<msg\n");
+        xml.append("handle=\"" + handle + "\"\n");
+        xml.append("subject=\"" + subject + "\"\n");
+        xml.append("/>\n");
+        ByteArrayInputStream stream = new ByteArrayInputStream(xml.toString().getBytes());
+
+        MessagesListing listing = new MessagesListing(stream);
+
+        assertThat(listing.getList()).hasSize(1);
+        Message msg = listing.getList().get(0);
+        assertThat(msg.getHandle()).isEqualTo(handle);
+        assertThat(msg.getSubject()).isEqualTo(subject);
+    }
+}
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 e16e310..d9dbbb1 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
@@ -33,6 +33,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.spy;
@@ -187,7 +188,6 @@
         assertThat(file.length()).isGreaterThan(shareContent.length());
     }
 
-    @Ignore("b/263754734")
     @Test
     public void sendFileInfo_finishImmediately() throws Exception {
         doReturn(true).when(mMethodProxy).bluetoothAdapterIsEnabled(any());
@@ -195,7 +195,7 @@
         mIntent.setAction("unsupported-action");
         ActivityScenario<BluetoothOppLauncherActivity> scenario = ActivityScenario.launch(mIntent);
         doThrow(new IllegalArgumentException()).when(mBluetoothOppManager).saveSendingFileInfo(
-                any(), any(String.class), any(), any());
+                any(), any(String.class), anyBoolean(), anyBoolean());
         scenario.onActivity(activity -> {
             activity.sendFileInfo("text/plain", "content:///abc.txt", false, false);
         });
@@ -208,18 +208,4 @@
         Thread.sleep(2_000);
         assertThat(activityScenario.getState()).isEqualTo(state);
     }
-
-
-    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,
-                BluetoothOppLauncherActivity.class);
-        mTargetContext.getPackageManager().setComponentEnabledSetting(
-                activityName, enabledState, DONT_KILL_APP);
-    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
index d011a6d..96b7084 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppServiceTest.java
@@ -15,17 +15,28 @@
  */
 package com.android.bluetooth.opp;
 
+import static com.android.bluetooth.opp.BluetoothOppService.WHERE_INVISIBLE_UNCONFIRMED;
+
+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.anyString;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
+import android.app.NotificationManager;
 import android.bluetooth.BluetoothAdapter;
-import android.content.Context;
+import android.content.ContentResolver;
+import android.database.MatrixCursor;
+import android.os.Handler;
 
-import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
+import com.android.bluetooth.BluetoothMethodProxy;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.btservice.AdapterService;
 
@@ -39,14 +50,14 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@MediumTest
 @RunWith(AndroidJUnit4.class)
 public class BluetoothOppServiceTest {
-    private BluetoothOppService mService = null;
-    private BluetoothAdapter mAdapter = null;
-
     @Rule
     public final ServiceTestRule mServiceRule = new ServiceTestRule();
+    @Mock
+    BluetoothMethodProxy mMethodProxy;
+    private BluetoothOppService mService = null;
+    private BluetoothAdapter mAdapter = null;
 
     @Mock
     private AdapterService mAdapterService;
@@ -56,8 +67,18 @@
         Assume.assumeTrue("Ignore test when BluetoothOppService is not enabled",
                 BluetoothOppService.isEnabled());
         MockitoAnnotations.initMocks(this);
+
+        BluetoothMethodProxy.setInstanceForTesting(mMethodProxy);
+
+        // To void mockito multi-thread inter-tests problem
+        // If the thread still run in the next test, it will raise un-related mockito error
+        BluetoothOppNotification bluetoothOppNotification = mock(BluetoothOppNotification.class);
+        bluetoothOppNotification.mNotificationMgr = mock(NotificationManager.class);
+        doReturn(bluetoothOppNotification).when(mMethodProxy).newBluetoothOppNotification(any());
+
         TestUtils.setAdapterService(mAdapterService);
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
+
         TestUtils.startService(mServiceRule, BluetoothOppService.class);
         mService = BluetoothOppService.getBluetoothOppService();
         Assert.assertNotNull(mService);
@@ -68,6 +89,7 @@
 
     @After
     public void tearDown() throws Exception {
+        BluetoothMethodProxy.setInstanceForTesting(null);
         if (!BluetoothOppService.isEnabled()) {
             return;
         }
@@ -79,4 +101,74 @@
     public void testInitialize() {
         Assert.assertNotNull(BluetoothOppService.getBluetoothOppService());
     }
-}
+
+    @Test
+    public void deleteShare_deleteShareAndCorrespondingBatch() {
+        int infoTimestamp = 123456789;
+        int infoTimestamp2 = 123489;
+
+        BluetoothOppShareInfo shareInfo = mock(BluetoothOppShareInfo.class);
+        shareInfo.mTimestamp = infoTimestamp;
+        shareInfo.mDestination = "AA:BB:CC:DD:EE:FF";
+        BluetoothOppShareInfo shareInfo2 = mock(BluetoothOppShareInfo.class);
+        shareInfo2.mTimestamp = infoTimestamp2;
+        shareInfo2.mDestination = "00:11:22:33:44:55";
+
+        mService.mShares.clear();
+        mService.mShares.add(shareInfo);
+        mService.mShares.add(shareInfo2);
+
+        // batch1 will be removed
+        BluetoothOppBatch batch1 = new BluetoothOppBatch(mService, shareInfo);
+        BluetoothOppBatch batch2 = new BluetoothOppBatch(mService, shareInfo2);
+        batch2.mStatus = Constants.BATCH_STATUS_FINISHED;
+        mService.mBatches.clear();
+        mService.mBatches.add(batch1);
+        mService.mBatches.add(batch2);
+
+        mService.deleteShare(0);
+        assertThat(mService.mShares.size()).isEqualTo(1);
+        assertThat(mService.mBatches.size()).isEqualTo(1);
+        assertThat(mService.mShares.get(0)).isEqualTo(shareInfo2);
+        assertThat(mService.mBatches.get(0)).isEqualTo(batch2);
+    }
+
+    @Test
+    public void dump_shouldNotThrow() {
+        BluetoothOppShareInfo info = mock(BluetoothOppShareInfo.class);
+
+        mService.mShares.add(info);
+
+        // should not throw
+        mService.dump(new StringBuilder());
+    }
+
+    @Test
+    public void trimDatabase_trimsOldOrInvisibleRecords() {
+        ContentResolver contentResolver = InstrumentationRegistry
+                .getInstrumentation().getTargetContext().getContentResolver();
+        Assume.assumeTrue("Ignore test when there is no content provider",
+                contentResolver.acquireContentProviderClient(BluetoothShare.CONTENT_URI) != null);
+
+        doReturn(1 /* any int is Ok */).when(mMethodProxy).contentResolverDelete(
+                eq(contentResolver), eq(BluetoothShare.CONTENT_URI), anyString(), any());
+
+        MatrixCursor cursor = new MatrixCursor(new String[]{BluetoothShare._ID}, 500);
+        for (long i = 0; i < Constants.MAX_RECORDS_IN_DATABASE + 20; i++) {
+            cursor.addRow(new Object[]{i});
+        }
+
+        doReturn(cursor).when(mMethodProxy).contentResolverQuery(eq(contentResolver),
+                eq(BluetoothShare.CONTENT_URI), any(), any(), any(), any());
+
+        BluetoothOppService.trimDatabase(contentResolver);
+
+        // check trimmed invisible records
+        verify(mMethodProxy).contentResolverDelete(eq(contentResolver),
+                eq(BluetoothShare.CONTENT_URI), eq(WHERE_INVISIBLE_UNCONFIRMED), any());
+
+        // check trimmed old records
+        verify(mMethodProxy).contentResolverDelete(eq(contentResolver),
+                eq(BluetoothShare.CONTENT_URI), eq(BluetoothShare._ID + " < " + 20), any());
+    }
+}
\ No newline at end of file
diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java
index 35042b4..f1e2a77 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java
@@ -21,6 +21,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
@@ -28,10 +29,15 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothUuid;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.Looper;
@@ -51,7 +57,8 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
+
+import java.util.Objects;
 
 @MediumTest
 @RunWith(AndroidJUnit4.class)
@@ -72,8 +79,8 @@
 
     @Mock
     BluetoothOppObexSession mSession;
-    @Spy
-    BluetoothMethodProxy mCallProxy = BluetoothMethodProxy.getInstance();
+    @Mock
+    BluetoothMethodProxy mCallProxy;
     Context mContext;
     BluetoothOppBatch mBluetoothOppBatch;
     BluetoothOppTransfer mTransfer;
@@ -257,4 +264,84 @@
         verify(mContext).sendBroadcast(argThat(
                 arg -> arg.getAction().equals(BluetoothShare.USER_CONFIRMATION_TIMEOUT_ACTION)));
     }
+
+    @Test
+    public void socketConnectThreadConstructors() {
+        String address = "AA:BB:CC:EE:DD:11";
+        BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class))
+                .getAdapter().getRemoteDevice(address);
+        BluetoothOppTransfer transfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch);
+        BluetoothOppTransfer.SocketConnectThread socketConnectThread =
+                transfer.new SocketConnectThread(device, true);
+        BluetoothOppTransfer.SocketConnectThread socketConnectThread2 =
+                transfer.new SocketConnectThread(device, true, false, 0);
+        assertThat(Objects.equals(socketConnectThread.mDevice, device)).isTrue();
+        assertThat(Objects.equals(socketConnectThread2.mDevice, device)).isTrue();
+    }
+
+    @Test
+    public void socketConnectThreadInterrupt() {
+        String address = "AA:BB:CC:EE:DD:11";
+        BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class))
+                .getAdapter().getRemoteDevice(address);
+        BluetoothOppTransfer transfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch);
+        BluetoothOppTransfer.SocketConnectThread socketConnectThread =
+                transfer.new SocketConnectThread(device, true);
+        socketConnectThread.interrupt();
+        assertThat(socketConnectThread.mIsInterrupted).isTrue();
+    }
+
+    @Test
+    @SuppressWarnings("DoNotCall")
+    public void socketConnectThreadRun_bluetoothDisabled_connectionFailed() {
+        String address = "AA:BB:CC:EE:DD:11";
+        BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class))
+                .getAdapter().getRemoteDevice(address);
+        BluetoothOppTransfer transfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch);
+        BluetoothOppTransfer.SocketConnectThread socketConnectThread =
+                transfer.new SocketConnectThread(device, true);
+        transfer.mSessionHandler = mEventHandler;
+
+        socketConnectThread.run();
+        verify(mCallProxy).handlerSendEmptyMessage(any(), eq(TRANSPORT_ERROR));
+    }
+
+    @Test
+    public void oppConnectionReceiver_onReceiveWithActionAclDisconnected_sendsConnectTimeout() {
+        BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class))
+                .getAdapter().getRemoteDevice(mDestination);
+        BluetoothOppTransfer transfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch);
+        transfer.mCurrentShare = mInitShareInfo;
+        transfer.mCurrentShare.mConfirm = BluetoothShare.USER_CONFIRMATION_PENDING;
+        BluetoothOppTransfer.OppConnectionReceiver receiver = transfer.new OppConnectionReceiver();
+        Intent intent = new Intent();
+        intent.setAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+
+        transfer.mSessionHandler = mEventHandler;
+        receiver.onReceive(mContext, intent);
+        verify(mCallProxy).handlerSendEmptyMessage(any(),
+                eq(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT));
+    }
+
+    @Test
+    public void oppConnectionReceiver_onReceiveWithActionSdpRecord_sendsNoMessage() {
+        BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class))
+                .getAdapter().getRemoteDevice(mDestination);
+        BluetoothOppTransfer transfer = new BluetoothOppTransfer(mContext, mBluetoothOppBatch);
+        transfer.mCurrentShare = mInitShareInfo;
+        transfer.mCurrentShare.mConfirm = BluetoothShare.USER_CONFIRMATION_PENDING;
+        transfer.mDevice = device;
+        transfer.mSessionHandler = mEventHandler;
+        BluetoothOppTransfer.OppConnectionReceiver receiver = transfer.new OppConnectionReceiver();
+        Intent intent = new Intent();
+        intent.setAction(BluetoothDevice.ACTION_SDP_RECORD);
+        intent.putExtra(BluetoothDevice.EXTRA_UUID, BluetoothUuid.OBEX_OBJECT_PUSH);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+
+        receiver.onReceive(mContext, intent);
+
+        // bluetooth device name is null => skip without interaction
+        verifyNoMoreInteractions(mCallProxy);
+    }
 }
diff --git a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
index 9cd29f4..9332eea 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/telephony/BluetoothInCallServiceTest.java
@@ -1354,6 +1354,66 @@
         Assert.assertFalse(mBluetoothInCallService.mOnCreateCalled);
     }
 
+    @Test
+    public void testLeCallControlCallback_onAcceptCall_withUnknownCallId() {
+        BluetoothLeCallControlProxy callControlProxy = mock(BluetoothLeCallControlProxy.class);
+        mBluetoothInCallService.mBluetoothLeCallControl = callControlProxy;
+        BluetoothLeCallControl.Callback callback =
+                mBluetoothInCallService.mBluetoothLeCallControlCallback;
+
+        int requestId = 1;
+        UUID unknownCallId = UUID.randomUUID();
+        callback.onAcceptCall(requestId, unknownCallId);
+
+        verify(callControlProxy).requestResult(
+                requestId, BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID);
+    }
+
+    @Test
+    public void testLeCallControlCallback_onTerminateCall_withUnknownCallId() {
+        BluetoothLeCallControlProxy callControlProxy = mock(BluetoothLeCallControlProxy.class);
+        mBluetoothInCallService.mBluetoothLeCallControl = callControlProxy;
+        BluetoothLeCallControl.Callback callback =
+                mBluetoothInCallService.mBluetoothLeCallControlCallback;
+
+        int requestId = 1;
+        UUID unknownCallId = UUID.randomUUID();
+        callback.onTerminateCall(requestId, unknownCallId);
+
+        verify(callControlProxy).requestResult(
+                requestId, BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID);
+    }
+
+    @Test
+    public void testLeCallControlCallback_onHoldCall_withUnknownCallId() {
+        BluetoothLeCallControlProxy callControlProxy = mock(BluetoothLeCallControlProxy.class);
+        mBluetoothInCallService.mBluetoothLeCallControl = callControlProxy;
+        BluetoothLeCallControl.Callback callback =
+                mBluetoothInCallService.mBluetoothLeCallControlCallback;
+
+        int requestId = 1;
+        UUID unknownCallId = UUID.randomUUID();
+        callback.onHoldCall(requestId, unknownCallId);
+
+        verify(callControlProxy).requestResult(
+                requestId, BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID);
+    }
+
+    @Test
+    public void testLeCallControlCallback_onUnholdCall_withUnknownCallId() {
+        BluetoothLeCallControlProxy callControlProxy = mock(BluetoothLeCallControlProxy.class);
+        mBluetoothInCallService.mBluetoothLeCallControl = callControlProxy;
+        BluetoothLeCallControl.Callback callback =
+                mBluetoothInCallService.mBluetoothLeCallControlCallback;
+
+        int requestId = 1;
+        UUID unknownCallId = UUID.randomUUID();
+        callback.onUnholdCall(requestId, unknownCallId);
+
+        verify(callControlProxy).requestResult(
+                requestId, BluetoothLeCallControl.RESULT_ERROR_UNKNOWN_CALL_ID);
+    }
+
     private void addCallCapability(BluetoothCall call, int capability) {
         when(call.can(capability)).thenReturn(true);
     }
diff --git a/service/java/com/android/server/bluetooth/BluetoothManagerService.java b/service/java/com/android/server/bluetooth/BluetoothManagerService.java
index 2b8d66a..16e0296 100644
--- a/service/java/com/android/server/bluetooth/BluetoothManagerService.java
+++ b/service/java/com/android/server/bluetooth/BluetoothManagerService.java
@@ -302,6 +302,8 @@
     // Save a ProfileServiceConnections object for each of the bound
     // bluetooth profile services
     private final Map<Integer, ProfileServiceConnections> mProfileServices = new HashMap<>();
+    @GuardedBy("mProfileServices")
+    private boolean mUnbindingAll = false;
 
     private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
         @Override
@@ -1598,13 +1600,16 @@
                 } catch (IllegalArgumentException e) {
                     Log.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
                 }
-                mProfileServices.remove(profile);
+                if (!mUnbindingAll) {
+                    mProfileServices.remove(profile);
+                }
             }
         }
     }
 
     private void unbindAllBluetoothProfileServices() {
         synchronized (mProfileServices) {
+            mUnbindingAll = true;
             for (Integer i : mProfileServices.keySet()) {
                 ProfileServiceConnections psc = mProfileServices.get(i);
                 try {
@@ -1614,6 +1619,7 @@
                 }
                 psc.removeAllProxies();
             }
+            mUnbindingAll = false;
             mProfileServices.clear();
         }
     }
diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc
index 5b1a936..c9a89bc 100644
--- a/system/bta/le_audio/client.cc
+++ b/system/bta/le_audio/client.cc
@@ -460,6 +460,8 @@
         ToString(group->GetTargetState()).c_str());
     group->SetTargetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
 
+    group->PrintDebugState();
+
     /* There is an issue with a setting up stream or any other operation which
      * are gatt operations. It means peer is not responsable. Lets close ACL
      */
diff --git a/system/bta/le_audio/devices.cc b/system/bta/le_audio/devices.cc
index d368750..f12c923 100644
--- a/system/bta/le_audio/devices.cc
+++ b/system/bta/le_audio/devices.cc
@@ -882,16 +882,12 @@
   return iter == leAudioDevices_.end();
 }
 
-bool LeAudioDeviceGroup::HaveAllActiveDevicesCisDisc(void) {
-  auto iter =
-      std::find_if(leAudioDevices_.begin(), leAudioDevices_.end(), [](auto& d) {
-        if (d.expired())
-          return false;
-        else
-          return !(((d.lock()).get())->HaveAllAsesCisDisc());
-      });
-
-  return iter == leAudioDevices_.end();
+bool LeAudioDeviceGroup::HaveAllCisesDisconnected(void) {
+  for (auto const dev : leAudioDevices_) {
+    if (dev.expired()) continue;
+    if (dev.lock().get()->HaveAnyCisConnected()) return false;
+  }
+  return true;
 }
 
 uint8_t LeAudioDeviceGroup::GetFirstFreeCisId(void) {
@@ -2400,13 +2396,15 @@
   return iter == ases_.end();
 }
 
-bool LeAudioDevice::HaveAllAsesCisDisc(void) {
-  auto iter = std::find_if(ases_.begin(), ases_.end(), [](const auto& ase) {
-    return ase.active &&
-           (ase.data_path_state != AudioStreamDataPathState::CIS_ASSIGNED);
-  });
-
-  return iter == ases_.end();
+bool LeAudioDevice::HaveAnyCisConnected(void) {
+  /* Pending and Disconnecting is considered as connected in this function */
+  for (auto const ase : ases_) {
+    if (ase.data_path_state != AudioStreamDataPathState::CIS_ASSIGNED &&
+        ase.data_path_state != AudioStreamDataPathState::IDLE) {
+      return true;
+    }
+  }
+  return false;
 }
 
 bool LeAudioDevice::HasCisId(uint8_t id) {
diff --git a/system/bta/le_audio/devices.h b/system/bta/le_audio/devices.h
index 4ffb3f4..d9f4900 100644
--- a/system/bta/le_audio/devices.h
+++ b/system/bta/le_audio/devices.h
@@ -155,7 +155,7 @@
   bool IsReadyToCreateStream(void);
   bool IsReadyToSuspendStream(void);
   bool HaveAllActiveAsesCisEst(void);
-  bool HaveAllAsesCisDisc(void);
+  bool HaveAnyCisConnected(void);
   bool HasCisId(uint8_t id);
   uint8_t GetMatchingBidirectionCisId(const struct types::ase* base_ase);
   const struct types::acs_ac_record* GetCodecConfigurationSupportedPac(
@@ -286,7 +286,7 @@
   bool IsDeviceInTheGroup(LeAudioDevice* leAudioDevice);
   bool HaveAllActiveDevicesAsesTheSameState(types::AseState state);
   bool IsGroupStreamReady(void);
-  bool HaveAllActiveDevicesCisDisc(void);
+  bool HaveAllCisesDisconnected(void);
   uint8_t GetFirstFreeCisId(void);
   uint8_t GetFirstFreeCisId(types::CisType cis_type);
   void CigGenerateCisIds(types::LeAudioContextType context_type);
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index 4530617..d891850 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -227,8 +227,8 @@
         }
 
         while (leAudioDevice) {
-          PrepareAndSendUpdateMetadata(group, leAudioDevice,
-                                       metadata_context_type, ccid_list);
+          PrepareAndSendUpdateMetadata(leAudioDevice, metadata_context_type,
+                                       ccid_list);
           leAudioDevice = group->GetNextActiveDevice(leAudioDevice);
         }
         break;
@@ -638,7 +638,7 @@
     LOG_DEBUG(
         " device: %s, group connected: %d, all active ase disconnected:: %d",
         leAudioDevice->address_.ToString().c_str(),
-        group->IsAnyDeviceConnected(), group->HaveAllActiveDevicesCisDisc());
+        group->IsAnyDeviceConnected(), group->HaveAllCisesDisconnected());
 
     /* Update the current group audio context availability which could change
      * due to disconnected group member.
@@ -649,8 +649,7 @@
      * If there is active CIS, do nothing here. Just update the available
      * contexts table.
      */
-    if (group->IsAnyDeviceConnected() &&
-        !group->HaveAllActiveDevicesCisDisc()) {
+    if (group->IsAnyDeviceConnected() && !group->HaveAllCisesDisconnected()) {
       if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
         /* We keep streaming but want others to let know user that it might be
          * need to update offloader with new CIS configuration
@@ -693,7 +692,7 @@
        * or pending. If CIS is established, this will be handled in disconnected
        * complete event
        */
-      if (group->HaveAllActiveDevicesCisDisc()) {
+      if (group->HaveAllCisesDisconnected()) {
         RemoveCigForGroup(group);
       }
 
@@ -837,7 +836,7 @@
          * If there is other device connected and streaming, just leave it as it
          * is, otherwise stop the stream.
          */
-        if (!group->HaveAllActiveDevicesCisDisc()) {
+        if (!group->HaveAllCisesDisconnected()) {
           /* There is ASE streaming for some device. Continue streaming. */
           LOG_WARN(
               "Group member disconnected during streaming. Cis handle 0x%04x",
@@ -863,7 +862,7 @@
          */
         if ((group->GetState() ==
              AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) &&
-            group->HaveAllActiveDevicesCisDisc()) {
+            group->HaveAllCisesDisconnected()) {
           /* No more transition for group */
           alarm_cancel(watchdog_);
 
@@ -873,15 +872,58 @@
         }
         break;
       case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
-      case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
+      case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
         /* Those two are used when closing the stream and CIS disconnection is
          * expected */
-        if (group->HaveAllActiveDevicesCisDisc()) {
-          RemoveCigForGroup(group);
+        if (!group->HaveAllCisesDisconnected()) {
+          LOG_DEBUG(
+              "Still waiting for all CISes being disconnected for group:%d",
+              group->group_id_);
           return;
         }
 
-        break;
+        auto current_group_state = group->GetState();
+        LOG_INFO("group %d current state: %s, target state: %s",
+                 group->group_id_,
+                 bluetooth::common::ToString(current_group_state).c_str(),
+                 bluetooth::common::ToString(target_state).c_str());
+        /* It might happen that controller notified about CIS disconnection
+         * later, after ASE state already changed.
+         * In such an event, there is need to notify upper layer about state
+         * from here.
+         */
+        if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
+          LOG_INFO(
+              "Cises disconnected for group %d, we are good in Idle state.",
+              group->group_id_);
+          ReleaseCisIds(group);
+          state_machine_callbacks_->StatusReportCb(group->group_id_,
+                                                   GroupStreamStatus::IDLE);
+        } else if (current_group_state ==
+                   AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
+          auto reconfig = group->IsPendingConfiguration();
+          LOG_INFO(
+              "Cises disconnected for group: %d, we are good in Configured "
+              "state, reconfig=%d.",
+              group->group_id_, reconfig);
+          if (reconfig) {
+            group->ClearPendingConfiguration();
+            state_machine_callbacks_->StatusReportCb(
+                group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
+            /* No more transition for group */
+            alarm_cancel(watchdog_);
+          } else {
+            /* This is Autonomous change if both, target and current state
+             * is CODEC_CONFIGURED
+             */
+            if (target_state == current_group_state) {
+              state_machine_callbacks_->StatusReportCb(
+                  group->group_id_, GroupStreamStatus::CONFIGURED_AUTONOMOUS);
+            }
+          }
+        }
+        RemoveCigForGroup(group);
+      } break;
       default:
         break;
     }
@@ -1468,9 +1510,21 @@
           PrepareAndSendRelease(leAudioDeviceNext);
         } else {
           /* Last node is in releasing state*/
-          if (alarm_is_scheduled(watchdog_)) alarm_cancel(watchdog_);
-
           group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
+
+          group->PrintDebugState();
+          /* If all CISes are disconnected, notify upper layer about IDLE state,
+           * otherwise wait for */
+          if (!group->HaveAllCisesDisconnected()) {
+            LOG_WARN(
+                "Not all CISes removed before going to IDLE for group %d, "
+                "waiting...",
+                group->group_id_);
+            group->PrintDebugState();
+            return;
+          }
+
+          if (alarm_is_scheduled(watchdog_)) alarm_cancel(watchdog_);
           ReleaseCisIds(group);
           state_machine_callbacks_->StatusReportCb(group->group_id_,
                                                    GroupStreamStatus::IDLE);
@@ -1651,6 +1705,19 @@
                   AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED &&
               group->IsPendingConfiguration()) {
             LOG_INFO(" Configured state completed ");
+
+            /* If all CISes are disconnected, notify upper layer about IDLE
+             * state, otherwise wait for */
+            if (!group->HaveAllCisesDisconnected()) {
+              LOG_WARN(
+                  "Not all CISes removed before going to CONFIGURED for group "
+                  "%d, "
+                  "waiting...",
+                  group->group_id_);
+              group->PrintDebugState();
+              return;
+            }
+
             group->ClearPendingConfiguration();
             state_machine_callbacks_->StatusReportCb(
                 group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER);
@@ -1789,7 +1856,6 @@
           PrepareAndSendRelease(leAudioDeviceNext);
         } else {
           /* Last node is in releasing state*/
-          if (alarm_is_scheduled(watchdog_)) alarm_cancel(watchdog_);
 
           group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
           /* Remote device has cache and keep staying in configured state after
@@ -1797,6 +1863,16 @@
            * remote device.
            */
           group->SetTargetState(group->GetState());
+
+          if (!group->HaveAllCisesDisconnected()) {
+            LOG_WARN(
+                "Not all CISes removed before going to IDLE for group %d, "
+                "waiting...",
+                group->group_id_);
+            group->PrintDebugState();
+            return;
+          }
+
           state_machine_callbacks_->StatusReportCb(
               group->group_id_, GroupStreamStatus::CONFIGURED_AUTONOMOUS);
         }
@@ -1885,7 +1961,7 @@
 
         group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
 
-        if (!group->HaveAllActiveDevicesCisDisc()) return;
+        if (!group->HaveAllCisesDisconnected()) return;
 
         if (group->GetTargetState() ==
             AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
@@ -2046,52 +2122,58 @@
                                       GATT_WRITE_NO_RSP, NULL, NULL);
   }
 
-  void PrepareAndSendUpdateMetadata(LeAudioDeviceGroup* group,
-                                    LeAudioDevice* leAudioDevice,
+  void PrepareAndSendUpdateMetadata(LeAudioDevice* leAudioDevice,
                                     le_audio::types::AudioContexts context_type,
                                     const std::vector<uint8_t>& ccid_list) {
     std::vector<struct le_audio::client_parser::ascs::ctp_update_metadata>
         confs;
 
-    for (; leAudioDevice;
-         leAudioDevice = group->GetNextActiveDevice(leAudioDevice)) {
-      if (!leAudioDevice->IsMetadataChanged(context_type, ccid_list)) continue;
+    if (!leAudioDevice->IsMetadataChanged(context_type, ccid_list)) return;
 
-      /* Request server to update ASEs with new metadata */
-      for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
-           ase = leAudioDevice->GetNextActiveAse(ase)) {
-        LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
-                  leAudioDevice->address_.ToString().c_str(), ase->id,
-                  ase->cis_id, ToString(ase->state).c_str());
+    /* Request server to update ASEs with new metadata */
+    for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
+         ase = leAudioDevice->GetNextActiveAse(ase)) {
+      LOG_DEBUG("device: %s, ase_id: %d, cis_id: %d, ase state: %s",
+                leAudioDevice->address_.ToString().c_str(), ase->id,
+                ase->cis_id, ToString(ase->state).c_str());
 
-        /* Filter multidirectional audio context for each ase direction */
-        auto directional_audio_context =
-            context_type & leAudioDevice->GetAvailableContexts(ase->direction);
-        if (directional_audio_context.any()) {
-          ase->metadata =
-              leAudioDevice->GetMetadata(directional_audio_context, ccid_list);
-        } else {
-          ase->metadata = leAudioDevice->GetMetadata(
-              AudioContexts(LeAudioContextType::UNSPECIFIED),
-              std::vector<uint8_t>());
-        }
-
-        struct le_audio::client_parser::ascs::ctp_update_metadata conf;
-
-        conf.ase_id = ase->id;
-        conf.metadata = ase->metadata;
-
-        confs.push_back(conf);
+      if (ase->state != AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING &&
+          ase->state != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+        /* This might happen when update metadata happens on late connect */
+        LOG_DEBUG(
+            "Metadata for ase_id %d cannot be updated due to invalid ase state "
+            "- see log above",
+            ase->id);
+        continue;
       }
 
+      /* Filter multidirectional audio context for each ase direction */
+      auto directional_audio_context =
+          context_type & leAudioDevice->GetAvailableContexts(ase->direction);
+      if (directional_audio_context.any()) {
+        ase->metadata =
+            leAudioDevice->GetMetadata(directional_audio_context, ccid_list);
+      } else {
+        ase->metadata = leAudioDevice->GetMetadata(
+            AudioContexts(LeAudioContextType::UNSPECIFIED),
+            std::vector<uint8_t>());
+      }
+
+      struct le_audio::client_parser::ascs::ctp_update_metadata conf;
+
+      conf.ase_id = ase->id;
+      conf.metadata = ase->metadata;
+
+      confs.push_back(conf);
+    }
+
+    if (confs.size() != 0) {
       std::vector<uint8_t> value;
       le_audio::client_parser::ascs::PrepareAseCtpUpdateMetadata(confs, value);
 
       BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_,
                                         leAudioDevice->ctp_hdls_.val_hdl, value,
                                         GATT_WRITE_NO_RSP, NULL, NULL);
-
-      return;
     }
   }
 
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index b51937d..18e8125 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -2885,15 +2885,15 @@
                                   uint8_t reason) {
   bluetooth::hci::iso_manager::cis_disconnected_evt event;
 
-  auto* ase = leAudioDevice->GetFirstActiveAse();
-  while (ase) {
-    event.reason = reason;
-    event.cig_id = group->group_id_;
-    event.cis_conn_hdl = ase->cis_conn_hdl;
-    LeAudioGroupStateMachine::Get()->ProcessHciNotifCisDisconnected(
-        group, leAudioDevice, &event);
-
-    ase = leAudioDevice->GetNextActiveAse(ase);
+  for (auto const ase : leAudioDevice->ases_) {
+    if (ase.data_path_state != types::AudioStreamDataPathState::CIS_ASSIGNED &&
+        ase.data_path_state != types::AudioStreamDataPathState::IDLE) {
+      event.reason = reason;
+      event.cig_id = group->group_id_;
+      event.cis_conn_hdl = ase.cis_conn_hdl;
+      LeAudioGroupStateMachine::Get()->ProcessHciNotifCisDisconnected(
+          group, leAudioDevice, &event);
+    }
   }
 }
 
@@ -3332,5 +3332,253 @@
 
   testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
 }
+
+TEST_F(StateMachineTest, lateCisDisconnectedEvent_ConfiguredByUser) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 6;
+  const auto num_devices = 1;
+
+  ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);
+
+  // Prepare multiple fake connected devices in a group
+  auto* group =
+      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group, 0, true);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+  PrepareDisableHandler(group);
+  PrepareReleaseHandler(group);
+
+  auto* leAudioDevice = group->GetFirstDevice();
+  auto expected_devices_written = 0;
+
+  /* Three Writes:
+   * 1: Codec Config
+   * 2: Codec QoS
+   * 3: Enabling
+   */
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
+                                              leAudioDevice->ctp_hdls_.val_hdl,
+                                              _, GATT_WRITE_NO_RSP, _, _))
+      .Times(AtLeast(3));
+  expected_devices_written++;
+
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);
+
+  InjectInitialIdleNotification(group);
+
+  // Start the configuration and stream Media content
+  LeAudioGroupStateMachine::Get()->StartStream(
+      group, static_cast<LeAudioContextType>(context_type),
+      types::AudioContexts(context_type));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);
+
+  /* Prepare DisconnectCis mock to not symulate CisDisconnection */
+  ON_CALL(*mock_iso_manager_, DisconnectCis).WillByDefault(Return());
+
+  /* Do reconfiguration */
+  group->SetPendingConfiguration();
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(leaudio_group_id,
+                     bluetooth::le_audio::GroupStreamStatus::RELEASING));
+
+  EXPECT_CALL(mock_callbacks_,
+              StatusReportCb(
+                  leaudio_group_id,
+                  bluetooth::le_audio::GroupStreamStatus::CONFIGURED_BY_USER))
+      .Times(0);
+  LeAudioGroupStateMachine::Get()->StopStream(group);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+
+  EXPECT_CALL(mock_callbacks_,
+              StatusReportCb(
+                  leaudio_group_id,
+                  bluetooth::le_audio::GroupStreamStatus::CONFIGURED_BY_USER));
+
+  // Inject CIS and ACL disconnection of first device
+  InjectCisDisconnected(group, leAudioDevice, HCI_ERR_CONN_CAUSE_LOCAL_HOST);
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+}
+
+TEST_F(StateMachineTest, lateCisDisconnectedEvent_AutonomousConfigured) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 6;
+  const auto num_devices = 1;
+
+  ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);
+
+  // Prepare multiple fake connected devices in a group
+  auto* group =
+      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group, 0, true);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+  PrepareDisableHandler(group);
+  PrepareReleaseHandler(group);
+
+  auto* leAudioDevice = group->GetFirstDevice();
+  auto expected_devices_written = 0;
+
+  /* Three Writes:
+   * 1: Codec Config
+   * 2: Codec QoS
+   * 3: Enabling
+   */
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
+                                              leAudioDevice->ctp_hdls_.val_hdl,
+                                              _, GATT_WRITE_NO_RSP, _, _))
+      .Times(AtLeast(3));
+  expected_devices_written++;
+
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);
+
+  InjectInitialIdleNotification(group);
+
+  // Start the configuration and stream Media content
+  LeAudioGroupStateMachine::Get()->StartStream(
+      group, static_cast<LeAudioContextType>(context_type),
+      types::AudioContexts(context_type));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);
+
+  /* Prepare DisconnectCis mock to not symulate CisDisconnection */
+  ON_CALL(*mock_iso_manager_, DisconnectCis).WillByDefault(Return());
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(leaudio_group_id,
+                     bluetooth::le_audio::GroupStreamStatus::RELEASING));
+
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(
+          leaudio_group_id,
+          bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS))
+      .Times(0);
+
+  // Stop the stream
+  LeAudioGroupStateMachine::Get()->StopStream(group);
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(
+          leaudio_group_id,
+          bluetooth::le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS));
+
+  // Inject CIS and ACL disconnection of first device
+  InjectCisDisconnected(group, leAudioDevice, HCI_ERR_CONN_CAUSE_LOCAL_HOST);
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+}
+
+TEST_F(StateMachineTest, lateCisDisconnectedEvent_Idle) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 6;
+  const auto num_devices = 1;
+
+  ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);
+
+  // Prepare multiple fake connected devices in a group
+  auto* group =
+      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
+  ASSERT_EQ(group->Size(), num_devices);
+
+  PrepareConfigureCodecHandler(group);
+  PrepareConfigureQosHandler(group);
+  PrepareEnableHandler(group);
+  PrepareDisableHandler(group);
+  PrepareReleaseHandler(group);
+
+  auto* leAudioDevice = group->GetFirstDevice();
+  auto expected_devices_written = 0;
+
+  /* Three Writes:
+   * 1: Codec Config
+   * 2: Codec QoS
+   * 3: Enabling
+   */
+  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
+                                              leAudioDevice->ctp_hdls_.val_hdl,
+                                              _, GATT_WRITE_NO_RSP, _, _))
+      .Times(AtLeast(3));
+  expected_devices_written++;
+
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
+  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);
+
+  InjectInitialIdleNotification(group);
+
+  // Start the configuration and stream Media content
+  LeAudioGroupStateMachine::Get()->StartStream(
+      group, static_cast<LeAudioContextType>(context_type),
+      types::AudioContexts(context_type));
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(),
+            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
+  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);
+
+  /* Prepare DisconnectCis mock to not symulate CisDisconnection */
+  ON_CALL(*mock_iso_manager_, DisconnectCis).WillByDefault(Return());
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(
+      mock_callbacks_,
+      StatusReportCb(leaudio_group_id,
+                     bluetooth::le_audio::GroupStreamStatus::RELEASING));
+
+  EXPECT_CALL(mock_callbacks_,
+              StatusReportCb(leaudio_group_id,
+                             bluetooth::le_audio::GroupStreamStatus::IDLE))
+      .Times(0);
+
+  // Stop the stream
+  LeAudioGroupStateMachine::Get()->StopStream(group);
+
+  // Check if group has transitioned to a proper state
+  ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+
+  EXPECT_CALL(mock_callbacks_,
+              StatusReportCb(leaudio_group_id,
+                             bluetooth::le_audio::GroupStreamStatus::IDLE));
+
+  // Inject CIS and ACL disconnection of first device
+  InjectCisDisconnected(group, leAudioDevice, HCI_ERR_CONN_CAUSE_LOCAL_HOST);
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+}
 }  // namespace internal
 }  // namespace le_audio
diff --git a/system/stack/btm/btm_ble.cc b/system/stack/btm/btm_ble.cc
index 2c77945..f36a0f2 100644
--- a/system/stack/btm/btm_ble.cc
+++ b/system/stack/btm/btm_ble.cc
@@ -1607,13 +1607,30 @@
   BTM_TRACE_ERROR("key size = %d", p_rec->ble.keys.key_size);
   if (use_stk) {
     btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, stk);
-  } else /* calculate LTK using peer device  */
-  {
-    if (p_rec->ble.key_type & BTM_LE_KEY_LENC)
-      btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, p_rec->ble.keys.lltk);
-    else
-      btsnd_hcic_ble_ltk_req_neg_reply(btm_cb.enc_handle);
+    return;
   }
+  /* calculate LTK using peer device  */
+  if (p_rec->ble.key_type & BTM_LE_KEY_LENC) {
+    btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, p_rec->ble.keys.lltk);
+    return;
+  }
+
+  p_rec = btm_find_dev_with_lenc(bda);
+  if (!p_rec) {
+    btsnd_hcic_ble_ltk_req_neg_reply(btm_cb.enc_handle);
+    return;
+  }
+
+  LOG_INFO("Found second sec_dev_rec for device that have LTK");
+  /* This can happen when remote established LE connection using RPA to this
+   * device, but then pair with us using Classing transport while still keeping
+   * LE connection. If remote attempts to encrypt the LE connection, we might
+   * end up here. We will eventually consolidate both entries, this is to avoid
+   * race conditions. */
+
+  LOG_ASSERT(p_rec->ble.key_type & BTM_LE_KEY_LENC);
+  p_cb->key_size = p_rec->ble.keys.key_size;
+  btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, p_rec->ble.keys.lltk);
 }
 
 /*******************************************************************************
diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc
index 4067e3d..5904115 100644
--- a/system/stack/btm/btm_dev.cc
+++ b/system/stack/btm/btm_dev.cc
@@ -30,6 +30,7 @@
 #include <string.h>
 
 #include "btm_api.h"
+#include "btm_ble_int.h"
 #include "device/include/controller.h"
 #include "l2c_api.h"
 #include "main/shim/btm_api.h"
@@ -372,6 +373,32 @@
   return NULL;
 }
 
+static bool has_lenc_and_address_is_equal(void* data, void* context) {
+  tBTM_SEC_DEV_REC* p_dev_rec = static_cast<tBTM_SEC_DEV_REC*>(data);
+  if (!(p_dev_rec->ble.key_type & BTM_LE_KEY_LENC)) return false;
+
+  return is_address_equal(data, context);
+}
+
+/*******************************************************************************
+ *
+ * Function         btm_find_dev_with_lenc
+ *
+ * Description      Look for the record in the device database with LTK and
+ *                  specified BD address
+ *
+ * Returns          Pointer to the record or NULL
+ *
+ ******************************************************************************/
+tBTM_SEC_DEV_REC* btm_find_dev_with_lenc(const RawAddress& bd_addr) {
+  if (btm_cb.sec_dev_rec == nullptr) return nullptr;
+
+  list_node_t* n = list_foreach(btm_cb.sec_dev_rec, has_lenc_and_address_is_equal,
+                                (void*)&bd_addr);
+  if (n) return static_cast<tBTM_SEC_DEV_REC*>(list_node(n));
+
+  return NULL;
+}
 /*******************************************************************************
  *
  * Function         btm_consolidate_dev
@@ -429,6 +456,63 @@
   }
 }
 
+/* combine security records of established LE connections after Classic pairing
+ * succeeded. */
+void btm_dev_consolidate_existing_connections(const RawAddress& bd_addr) {
+  tBTM_SEC_DEV_REC* p_target_rec = btm_find_dev(bd_addr);
+  if (!p_target_rec) {
+    LOG_ERROR("No security record for just bonded device!?!?");
+    return;
+  }
+
+  if (p_target_rec->ble_hci_handle != HCI_INVALID_HANDLE) {
+    LOG_INFO("Not consolidating - already have LE connection");
+    return;
+  }
+
+  LOG_INFO("%s", PRIVATE_ADDRESS(bd_addr));
+
+  list_node_t* end = list_end(btm_cb.sec_dev_rec);
+  list_node_t* node = list_begin(btm_cb.sec_dev_rec);
+  while (node != end) {
+    tBTM_SEC_DEV_REC* p_dev_rec =
+        static_cast<tBTM_SEC_DEV_REC*>(list_node(node));
+
+    // we do list_remove in some cases, must grab next before removing
+    node = list_next(node);
+
+    if (p_target_rec == p_dev_rec) continue;
+
+    /* an RPA device entry is a duplicate of the target record */
+    if (btm_ble_addr_resolvable(p_dev_rec->bd_addr, p_target_rec)) {
+      if (p_dev_rec->ble_hci_handle == HCI_INVALID_HANDLE) {
+        LOG_INFO("already disconnected - erasing entry %s",
+                 PRIVATE_ADDRESS(p_dev_rec->bd_addr));
+        wipe_secrets_and_remove(p_dev_rec);
+        continue;
+      }
+
+      LOG_INFO(
+          "Found existing LE connection to just bonded device on %s handle 0x%04x",
+          PRIVATE_ADDRESS(p_dev_rec->bd_addr), p_dev_rec->ble_hci_handle);
+
+      RawAddress ble_conn_addr = p_dev_rec->bd_addr;
+      p_target_rec->ble_hci_handle = p_dev_rec->ble_hci_handle;
+
+      /* remove the old LE record */
+      wipe_secrets_and_remove(p_dev_rec);
+
+      /* To avoid race conditions between central/peripheral starting encryption
+       * at same time, initiate it just from central. */
+      if (L2CA_GetBleConnRole(ble_conn_addr) == HCI_ROLE_CENTRAL) {
+        LOG_INFO("Will encrypt existing connection");
+        BTM_SetEncryption(ble_conn_addr, BT_TRANSPORT_LE, nullptr, nullptr,
+                          BTM_BLE_SEC_ENCRYPT);
+      }
+    }
+  }
+}
+
 /*******************************************************************************
  *
  * Function         btm_find_or_alloc_dev
diff --git a/system/stack/btm/btm_dev.h b/system/stack/btm/btm_dev.h
index 9da2e89..7591129 100644
--- a/system/stack/btm/btm_dev.h
+++ b/system/stack/btm/btm_dev.h
@@ -111,8 +111,20 @@
 
 /*******************************************************************************
  *
+ * Function         btm_find_dev_with_lenc
+ *
+ * Description      Look for the record in the device database with LTK and
+ *                  specified BD address
+ *
+ * Returns          Pointer to the record or NULL
+ *
+ ******************************************************************************/
+tBTM_SEC_DEV_REC* btm_find_dev_with_lenc(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
  * Function         btm_consolidate_dev
-5**
+ *
  * Description      combine security records if identified as same peer
  *
  * Returns          none
@@ -122,6 +134,20 @@
 
 /*******************************************************************************
  *
+ * Function         btm_consolidate_dev
+ *
+ * Description      When pairing is finished (i.e. on BR/EDR), this function
+ *                  checks if there are existing LE connections to same device
+ *                  that can now be encrypted and used for profiles requiring
+ *                  encryption.
+ *
+ * Returns          none
+ *
+ ******************************************************************************/
+void btm_dev_consolidate_existing_connections(const RawAddress& bd_addr);
+
+/*******************************************************************************
+ *
  * Function         btm_find_or_alloc_dev
  *
  * Description      Look for the record in the device database for the record
diff --git a/system/stack/gatt/connection_manager.cc b/system/stack/gatt/connection_manager.cc
index 5c2e880..b2f48ff 100644
--- a/system/stack/gatt/connection_manager.cc
+++ b/system/stack/gatt/connection_manager.cc
@@ -29,6 +29,7 @@
 
 #include "bind_helpers.h"
 #include "internal_include/bt_trace.h"
+#include "main/shim/dumpsys.h"
 #include "main/shim/le_scanning_manager.h"
 #include "main/shim/shim.h"
 #include "osi/include/alarm.h"
@@ -396,6 +397,10 @@
   return true;
 }
 
+bool is_background_connection(const RawAddress& address) {
+  return bgconn_dev.find(address) != bgconn_dev.end();
+}
+
 /** deregister all related background connetion device. */
 void on_app_deregistered(uint8_t app_id) {
   LOG_DEBUG("app_id=%d", static_cast<int>(app_id));
@@ -526,13 +531,15 @@
             address.ToString().c_str());
   auto it = bgconn_dev.find(address);
   if (it == bgconn_dev.end()) {
-    LOG_WARN("Unable to find background connection to remove");
+    LOG_WARN("Unable to find background connection to remove peer:%s",
+             PRIVATE_ADDRESS(address));
     return false;
   }
 
   auto app_it = it->second.doing_direct_conn.find(app_id);
   if (app_it == it->second.doing_direct_conn.end()) {
-    LOG_WARN("Unable to find direct connection to remove");
+    LOG_WARN("Unable to find direct connection to remove peer:%s",
+             PRIVATE_ADDRESS(address));
     return false;
   }
 
diff --git a/system/stack/gatt/connection_manager.h b/system/stack/gatt/connection_manager.h
index 0dff252..0c407c8 100644
--- a/system/stack/gatt/connection_manager.h
+++ b/system/stack/gatt/connection_manager.h
@@ -61,4 +61,6 @@
 extern void on_connection_timed_out(uint8_t app_id, const RawAddress& address);
 extern void on_connection_timed_out_from_shim(const RawAddress& address);
 
+extern bool is_background_connection(const RawAddress& address);
+
 }  // namespace connection_manager
diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc
index cd5099c..38f91a0 100644
--- a/system/stack/gatt/gatt_utils.cc
+++ b/system/stack/gatt/gatt_utils.cc
@@ -1466,11 +1466,18 @@
   }
 
   if (!connection_manager::direct_connect_remove(gatt_if, bda)) {
-    BTM_AcceptlistRemove(bda);
-    LOG_INFO(
-        "GATT connection manager has no record but removed filter acceptlist "
-        "gatt_if:%hhu peer:%s",
-        gatt_if, PRIVATE_ADDRESS(bda));
+    if (!connection_manager::is_background_connection(bda)) {
+      BTM_AcceptlistRemove(bda);
+      LOG_INFO(
+          "Gatt connection manager has no background record but "
+          " removed filter acceptlist gatt_if:%hhu peer:%s",
+          gatt_if, PRIVATE_ADDRESS(bda));
+    } else {
+      LOG_INFO(
+          "Gatt connection manager maintains a background record"
+          " preserving filter acceptlist gatt_if:%hhu peer:%s",
+          gatt_if, PRIVATE_ADDRESS(bda));
+    }
   }
   return true;
 }
diff --git a/system/stack/smp/smp_act.cc b/system/stack/smp/smp_act.cc
index dad83c0..3c8218e 100644
--- a/system/stack/smp/smp_act.cc
+++ b/system/stack/smp/smp_act.cc
@@ -1042,7 +1042,7 @@
   tBTM_LE_KEY_VALUE pid_key = {
       .pid_key = {},
   };
-  ;
+
   STREAM_TO_UINT8(pid_key.pid_key.identity_addr_type, p);
   STREAM_TO_BDADDR(pid_key.pid_key.identity_addr, p);
   pid_key.pid_key.irk = p_cb->tk;
diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc
index 22fcb71..71fa55d 100644
--- a/system/stack/smp/smp_utils.cc
+++ b/system/stack/smp/smp_utils.cc
@@ -41,6 +41,8 @@
 #include "stack/include/stack_metrics_logging.h"
 #include "types/raw_address.h"
 
+void btm_dev_consolidate_existing_connections(const RawAddress& bd_addr);
+
 #define SMP_PAIRING_REQ_SIZE 7
 #define SMP_CONFIRM_CMD_SIZE (OCTET16_LEN + 1)
 #define SMP_RAND_CMD_SIZE (OCTET16_LEN + 1)
@@ -978,6 +980,10 @@
                            smp_status_text(evt_data.cmplt.reason).c_str()));
   }
 
+  if (p_cb->status == SMP_SUCCESS && p_cb->smp_over_br) {
+    btm_dev_consolidate_existing_connections(pairing_bda);
+  }
+
   smp_reset_control_value(p_cb);
 
   if (p_callback) (*p_callback)(SMP_COMPLT_EVT, pairing_bda, &evt_data);
diff --git a/system/stack/test/gatt/gatt_sr_test.cc b/system/stack/test/gatt/gatt_sr_test.cc
index 7e6ab31..f142bae 100644
--- a/system/stack/test/gatt/gatt_sr_test.cc
+++ b/system/stack/test/gatt/gatt_sr_test.cc
@@ -63,6 +63,8 @@
 bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
   return false;
 }
+bool is_background_connection(const RawAddress& address) { return false; }
+
 }  // namespace connection_manager
 
 BT_HDR* attp_build_sr_msg(tGATT_TCB& tcb, uint8_t op_code, tGATT_SR_MSG* p_msg,
diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc
index d416e98..59d0022 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -29,6 +29,7 @@
 bool direct_connect_remove(uint8_t app_id, const RawAddress& address) {
   return false;
 }
+bool is_background_connection(const RawAddress& address) { return false; }
 }  // namespace connection_manager
 
 /** stack/gatt/att_protocol.cc */
diff --git a/system/test/mock/mock_stack_btm_dev.cc b/system/test/mock/mock_stack_btm_dev.cc
index a01217b..8a65ab8 100644
--- a/system/test/mock/mock_stack_btm_dev.cc
+++ b/system/test/mock/mock_stack_btm_dev.cc
@@ -109,3 +109,6 @@
 void wipe_secrets_and_remove(tBTM_SEC_DEV_REC* p_dev_rec) {
   mock_function_count_map[__func__]++;
 }
+void btm_dev_consolidate_existing_connections(const RawAddress& bd_addr) {
+  mock_function_count_map[__func__]++;
+}
diff --git a/system/test/mock/mock_stack_gatt_connection_manager.cc b/system/test/mock/mock_stack_gatt_connection_manager.cc
index 99f2904..372b4f0 100644
--- a/system/test/mock/mock_stack_gatt_connection_manager.cc
+++ b/system/test/mock/mock_stack_gatt_connection_manager.cc
@@ -92,3 +92,8 @@
 void connection_manager::reset(bool after_reset) {
   mock_function_count_map[__func__]++;
 }
+
+bool connection_manager::is_background_connection(const RawAddress& address) {
+  mock_function_count_map[__func__]++;
+  return false;
+}