Merge "Implement basic metrics for GATT." into main
diff --git a/android/app/aidl/android/bluetooth/IBluetooth.aidl b/android/app/aidl/android/bluetooth/IBluetooth.aidl
index 4569237..158e378 100644
--- a/android/app/aidl/android/bluetooth/IBluetooth.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetooth.aidl
@@ -338,4 +338,10 @@
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)")
     oneway void killBluetoothProcess();
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)")
+    boolean isLeCocSocketOffloadSupported(in AttributionSource source);
+
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)")
+    boolean isRfcommSocketOffloadSupported(in AttributionSource source);
 }
diff --git a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl
index 1bbf692..05f647d 100644
--- a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl
+++ b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl
@@ -29,8 +29,12 @@
 {
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
     @nullable ParcelFileDescriptor connectSocket(in BluetoothDevice device, int type, in @nullable ParcelUuid uuid, int port, int flag);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    @nullable ParcelFileDescriptor connectSocketwithOffload(in BluetoothDevice device, int type, in @nullable ParcelUuid uuid, int port, int flag, int dataPath, in String socketName, long hubId, long endpointId, int maximumPacketSize);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
     @nullable ParcelFileDescriptor createSocketChannel(int type, in @nullable String serviceName, in @nullable ParcelUuid uuid, int port, int flag);
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
+    @nullable ParcelFileDescriptor createSocketChannelWithOffload(int type, in @nullable String serviceName, in @nullable ParcelUuid uuid, int port, int flag, int dataPath, in String socketName, long hubId, long endpointId, int maximumPacketSize);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)")
     void requestMaximumTxDataLength(in BluetoothDevice device);
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
diff --git a/android/app/jni/com_android_bluetooth_avrcp_target.cpp b/android/app/jni/com_android_bluetooth_avrcp_target.cpp
index 35208d9..9ed217a 100644
--- a/android/app/jni/com_android_bluetooth_avrcp_target.cpp
+++ b/android/app/jni/com_android_bluetooth_avrcp_target.cpp
@@ -74,7 +74,7 @@
 static uint16_t getCurrentPlayerId();
 static std::vector<MediaPlayerInfo> getMediaPlayerList();
 using SetBrowsedPlayerCb = MediaInterface::SetBrowsedPlayerCallback;
-static void setBrowsedPlayer(uint16_t player_id, std::string current_path, SetBrowsedPlayerCb);
+static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb);
 static uint16_t setAddressedPlayer(uint16_t player_id);
 using GetFolderItemsCb = MediaInterface::FolderItemsCallback;
 static void getFolderItems(uint16_t player_id, std::string media_id, GetFolderItemsCb cb);
@@ -165,9 +165,8 @@
     cb.Run(current_player);
   }
 
-  void SetBrowsedPlayer(uint16_t player_id, std::string current_path,
-                        SetBrowsedPlayerCallback browse_cb) override {
-    setBrowsedPlayer(player_id, current_path, browse_cb);
+  void SetBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCallback browse_cb) override {
+    setBrowsedPlayer(player_id, browse_cb);
   }
 
   void SetAddressedPlayer(uint16_t player_id, SetAddressedPlayerCallback addressed_cb) override {
@@ -694,7 +693,7 @@
   return ret_list;
 }
 
-static void setBrowsedPlayer(uint16_t player_id, std::string current_path, SetBrowsedPlayerCb cb) {
+static void setBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCb cb) {
   log::debug("");
   std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
   CallbackEnv sCallbackEnv(__func__);
@@ -703,22 +702,21 @@
   }
 
   set_browsed_player_cb = cb;
-  jstring j_current_path = sCallbackEnv->NewStringUTF(current_path.c_str());
-  sCallbackEnv->CallVoidMethod(mJavaInterface, method_setBrowsedPlayer, player_id, j_current_path);
+  sCallbackEnv->CallVoidMethod(mJavaInterface, method_setBrowsedPlayer, player_id);
 }
 
 static void setBrowsedPlayerResponseNative(JNIEnv* env, jobject /* object */, jint /* player_id */,
-                                           jboolean success, jstring current_path, jint num_items) {
+                                           jboolean success, jstring root_id, jint num_items) {
   log::debug("");
 
-  std::string path;
-  if (current_path != nullptr) {
-    const char* value = env->GetStringUTFChars(current_path, nullptr);
-    path = std::string(value);
-    env->ReleaseStringUTFChars(current_path, value);
+  std::string root;
+  if (root_id != nullptr) {
+    const char* value = env->GetStringUTFChars(root_id, nullptr);
+    root = std::string(value);
+    env->ReleaseStringUTFChars(root_id, value);
   }
 
-  set_browsed_player_cb.Run(success == JNI_TRUE, path, num_items);
+  set_browsed_player_cb.Run(success == JNI_TRUE, root, num_items);
 }
 
 static uint16_t setAddressedPlayer(uint16_t player_id) {
@@ -1103,7 +1101,7 @@
           {"getNowPlayingList", "()Ljava/util/List;", &method_getNowPlayingList},
           {"getCurrentPlayerId", "()I", &method_getCurrentPlayerId},
           {"getMediaPlayerList", "()Ljava/util/List;", &method_getMediaPlayerList},
-          {"setBrowsedPlayer", "(ILjava/lang/String;)V", &method_setBrowsedPlayer},
+          {"setBrowsedPlayer", "(I)V", &method_setBrowsedPlayer},
           {"setAddressedPlayer", "(I)I", &method_setAddressedPlayer},
           {"getFolderItemsRequest", "(ILjava/lang/String;)V", &method_getFolderItemsRequest},
           {"playItem", "(IZLjava/lang/String;)V", &method_playItem},
diff --git a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
index 3a0a8d1..805e22b 100644
--- a/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -1782,11 +1782,14 @@
 }
 
 static jint connectSocketNative(JNIEnv* env, jobject /* obj */, jbyteArray address, jint type,
-                                jbyteArray uuid, jint port, jint flag, jint callingUid) {
+                                jbyteArray uuid, jint port, jint flag, jint callingUid,
+                                jint dataPath, jstring socketName, jlong hubId, jlong endPointId,
+                                jint maxRxPacketSize) {
   int socket_fd = INVALID_FD;
   jbyte* addr = nullptr;
   jbyte* uuidBytes = nullptr;
   Uuid btUuid;
+  const char* nativeSocketName = nullptr;
 
   if (!sBluetoothSocketInterface) {
     goto done;
@@ -1799,9 +1802,13 @@
   }
 
   btUuid = Uuid::From128BitBE(reinterpret_cast<uint8_t*>(uuidBytes));
+  if (socketName != nullptr) {
+    nativeSocketName = env->GetStringUTFChars(socketName, nullptr);
+  }
   if (sBluetoothSocketInterface->connect(reinterpret_cast<RawAddress*>(addr), (btsock_type_t)type,
-                                         &btUuid, port, &socket_fd, flag,
-                                         callingUid) != BT_STATUS_SUCCESS) {
+                                         &btUuid, port, &socket_fd, flag, callingUid,
+                                         (btsock_data_path_t)dataPath, nativeSocketName, hubId,
+                                         endPointId, maxRxPacketSize) != BT_STATUS_SUCCESS) {
     socket_fd = INVALID_FD;
   }
 
@@ -1812,16 +1819,21 @@
   if (uuidBytes) {
     env->ReleaseByteArrayElements(uuid, uuidBytes, 0);
   }
+  if (nativeSocketName) {
+    env->ReleaseStringUTFChars(socketName, nativeSocketName);
+  }
   return socket_fd;
 }
 
 static jint createSocketChannelNative(JNIEnv* env, jobject /* obj */, jint type,
                                       jstring serviceName, jbyteArray uuid, jint port, jint flag,
-                                      jint callingUid) {
+                                      jint callingUid, jint dataPath, jstring socketName,
+                                      jlong hubId, jlong endPointId, jint maxRxPacketSize) {
   int socket_fd = INVALID_FD;
   jbyte* uuidBytes = nullptr;
   Uuid btUuid;
   const char* nativeServiceName = nullptr;
+  const char* nativeSocketName = nullptr;
 
   if (!sBluetoothSocketInterface) {
     goto done;
@@ -1835,9 +1847,14 @@
     goto done;
   }
   btUuid = Uuid::From128BitBE(reinterpret_cast<uint8_t*>(uuidBytes));
+  if (socketName != nullptr) {
+    nativeSocketName = env->GetStringUTFChars(socketName, nullptr);
+  }
 
   if (sBluetoothSocketInterface->listen((btsock_type_t)type, nativeServiceName, &btUuid, port,
-                                        &socket_fd, flag, callingUid) != BT_STATUS_SUCCESS) {
+                                        &socket_fd, flag, callingUid, (btsock_data_path_t)dataPath,
+                                        nativeSocketName, hubId, endPointId,
+                                        maxRxPacketSize) != BT_STATUS_SUCCESS) {
     socket_fd = INVALID_FD;
   }
 
@@ -1848,6 +1865,9 @@
   if (nativeServiceName) {
     env->ReleaseStringUTFChars(serviceName, nativeServiceName);
   }
+  if (nativeSocketName) {
+    env->ReleaseStringUTFChars(socketName, nativeSocketName);
+  }
   return socket_fd;
 }
 
@@ -2267,8 +2287,9 @@
           {"setBufferLengthMillisNative", "(II)Z",
            reinterpret_cast<void*>(setBufferLengthMillisNative)},
           {"getMetricIdNative", "([B)I", reinterpret_cast<void*>(getMetricIdNative)},
-          {"connectSocketNative", "([BI[BIII)I", reinterpret_cast<void*>(connectSocketNative)},
-          {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I",
+          {"connectSocketNative", "([BI[BIIIILjava/lang/String;JJI)I",
+           reinterpret_cast<void*>(connectSocketNative)},
+          {"createSocketChannelNative", "(ILjava/lang/String;[BIIIILjava/lang/String;JJI)I",
            reinterpret_cast<void*>(createSocketChannelNative)},
           {"requestMaximumTxDataLengthNative", "([B)V",
            reinterpret_cast<void*>(requestMaximumTxDataLengthNative)},
diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
index e9e0f8c..8154e2e 100644
--- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
+++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java
@@ -117,7 +117,7 @@
         void run(boolean availablePlayers, boolean addressedPlayers, boolean uids);
     }
 
-    public interface SetBrowsedPlayerCallback {
+    public interface GetPlayerRootCallback {
         void run(int playerId, boolean success, String rootId, int numItems);
     }
 
@@ -371,34 +371,22 @@
         mMediaSessionManager.dispatchMediaKeyEvent(event, false);
     }
 
-    /** Sets the {@link #mBrowsingPlayerId} and returns the number of items in current path */
-    public void setBrowsedPlayer(int playerId, String currentPath, SetBrowsedPlayerCallback cb) {
+    public void getPlayerRoot(int playerId, GetPlayerRootCallback cb) {
         if (Flags.browsingRefactor()) {
             if (!haveMediaBrowser(playerId)) {
                 cb.run(playerId, false, "", 0);
                 return;
             }
-
             mBrowsingPlayerId = playerId;
             MediaBrowserWrapper wrapper = mMediaBrowserWrappers.get(playerId);
-
-            // If player is different than actual or if the given path is wrong, process rootId
-            if (playerId != mBrowsingPlayerId || currentPath.equals("")) {
-                wrapper.getRootId(
-                        (rootId) -> {
-                            wrapper.getFolderItems(
-                                    rootId,
-                                    (parentId, itemList) -> {
-                                        cb.run(playerId, true, rootId, itemList.size());
-                                    });
-                        });
-            } else {
-                wrapper.getFolderItems(
-                        currentPath,
-                        (parentId, itemList) -> {
-                            cb.run(playerId, true, currentPath, itemList.size());
-                        });
-            }
+            wrapper.getRootId(
+                    (rootId) -> {
+                        wrapper.getFolderItems(
+                                rootId,
+                                (parentId, itemList) -> {
+                                    cb.run(playerId, true, rootId, itemList.size());
+                                });
+                    });
         } else {
             // Fix PTS AVRCP/TG/MCN/CB/BI-02-C
             if (Utils.isPtsTestMode()) {
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java
index 1dd106b..4d366ac 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java
@@ -172,23 +172,24 @@
         return mAvrcpService.getMediaPlayerList();
     }
 
-    void setBrowsedPlayer(int playerId, String currentPath) {
-        d("setBrowsedPlayer: playerId=" + playerId + ", currentPath= " + currentPath);
-        mAvrcpService.setBrowsedPlayer(
-                playerId, currentPath, (a, b, c, d) -> setBrowsedPlayerResponse(a, b, c, d));
+    // TODO(apanicke): This shouldn't be named setBrowsedPlayer as it doesn't actually connect
+    // anything internally. It just returns the number of items in the root folder.
+    void setBrowsedPlayer(int playerId) {
+        d("setBrowsedPlayer: playerId=" + playerId);
+        mAvrcpService.getPlayerRoot(playerId, (a, b, c, d) -> setBrowsedPlayerResponse(a, b, c, d));
     }
 
-    void setBrowsedPlayerResponse(int playerId, boolean success, String currentPath, int numItems) {
+    void setBrowsedPlayerResponse(int playerId, boolean success, String rootId, int numItems) {
         d(
                 "setBrowsedPlayerResponse: playerId="
                         + playerId
                         + " success="
                         + success
-                        + " currentPath="
-                        + currentPath
+                        + " rootId="
+                        + rootId
                         + " numItems="
                         + numItems);
-        setBrowsedPlayerResponseNative(playerId, success, currentPath, numItems);
+        setBrowsedPlayerResponseNative(playerId, success, rootId, numItems);
     }
 
     int setAddressedPlayer(int playerId) {
diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
index 93579a8..67da944 100644
--- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
+++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java
@@ -461,10 +461,9 @@
         return mMediaPlayerList.getMediaPlayerList();
     }
 
-    /** See {@link MediaPlayerList#setBrowsedPlayer}. */
-    void setBrowsedPlayer(
-            int playerId, String currentPath, MediaPlayerList.SetBrowsedPlayerCallback cb) {
-        mMediaPlayerList.setBrowsedPlayer(playerId, currentPath, cb);
+    /** See {@link MediaPlayerList#getPlayerRoot}. */
+    void getPlayerRoot(int playerId, MediaPlayerList.GetPlayerRootCallback cb) {
+        mMediaPlayerList.getPlayerRoot(playerId, cb);
     }
 
     /** See {@link MediaPlayerList#setAddressedPlayer}. */
diff --git a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
index f1f0b51..981b2db 100644
--- a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
+++ b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java
@@ -49,6 +49,7 @@
     static final int BT_PROPERTY_REMOTE_ASHA_CAPABILITY = 0X15;
     static final int BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID = 0X16;
     static final int BT_PROPERTY_REMOTE_MODEL_NUM = 0x17;
+    static final int BT_PROPERTY_LPP_OFFLOAD_FEATURES = 0x1B;
 
     public static final int BT_DEVICE_TYPE_BREDR = 0x01;
     public static final int BT_DEVICE_TYPE_BLE = 0x02;
diff --git a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
index bc05fe9..4cf943f 100644
--- a/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
+++ b/android/app/src/com/android/bluetooth/btservice/ActiveDeviceManager.java
@@ -709,7 +709,6 @@
                             + device
                             + ", mHfpActiveDevice="
                             + mHfpActiveDevice);
-
             if (!Objects.equals(mHfpActiveDevice, device)) {
                 if (device != null) {
                     setHearingAidActiveDevice(null, true);
@@ -717,7 +716,7 @@
 
                 updateLeAudioActiveDeviceIfDualMode(mHfpActiveDevice, device);
 
-                if (!Utils.isDualModeAudioEnabled() || device == null) {
+                if ((!Utils.isDualModeAudioEnabled() && device == null)) {
                     Log.d(TAG, "HFP active device is null. Try to fallback to the active device.");
                     synchronized (mLock) {
                         setFallbackDeviceActiveLocked(null);
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
index 33bf3c3..64b5a0d 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java
@@ -195,13 +195,56 @@
         return getMetricIdNative(address);
     }
 
-    int connectSocket(byte[] address, int type, byte[] uuid, int port, int flag, int callingUid) {
-        return connectSocketNative(address, type, uuid, port, flag, callingUid);
+    int connectSocket(
+            byte[] address,
+            int type,
+            byte[] uuid,
+            int port,
+            int flag,
+            int callingUid,
+            int dataPath,
+            String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize) {
+        return connectSocketNative(
+                address,
+                type,
+                uuid,
+                port,
+                flag,
+                callingUid,
+                dataPath,
+                socketName,
+                hubId,
+                endpointId,
+                maximumPacketSize);
     }
 
     int createSocketChannel(
-            int type, String serviceName, byte[] uuid, int port, int flag, int callingUid) {
-        return createSocketChannelNative(type, serviceName, uuid, port, flag, callingUid);
+            int type,
+            String serviceName,
+            byte[] uuid,
+            int port,
+            int flag,
+            int callingUid,
+            int dataPath,
+            String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize) {
+        return createSocketChannelNative(
+                type,
+                serviceName,
+                uuid,
+                port,
+                flag,
+                callingUid,
+                dataPath,
+                socketName,
+                hubId,
+                endpointId,
+                maximumPacketSize);
     }
 
     void requestMaximumTxDataLength(byte[] address) {
@@ -359,10 +402,30 @@
     private native int getMetricIdNative(byte[] address);
 
     private native int connectSocketNative(
-            byte[] address, int type, byte[] uuid, int port, int flag, int callingUid);
+            byte[] address,
+            int type,
+            byte[] uuid,
+            int port,
+            int flag,
+            int callingUid,
+            int dataPath,
+            String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize);
 
     private native int createSocketChannelNative(
-            int type, String serviceName, byte[] uuid, int port, int flag, int callingUid);
+            int type,
+            String serviceName,
+            byte[] uuid,
+            int port,
+            int flag,
+            int callingUid,
+            int dataPath,
+            String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize);
 
     private native void requestMaximumTxDataLengthNative(byte[] address);
 
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
index 851df2c..e0c3f0c 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
@@ -134,6 +134,9 @@
     private boolean mIsLeIsochronousBroadcasterSupported;
     private boolean mIsLeChannelSoundingSupported;
 
+    private int mNumberOfSupportedOffloadedLeCocSockets;
+    private int mNumberOfSupportedOffloadedRfcommSockets = 0;
+
     // Lock for all getters and setters.
     // If finer grained locking is needer, more locks
     // can be added here.
@@ -908,6 +911,10 @@
                         updateDynamicAudioBufferSupport(val);
                         break;
 
+                    case AbstractionLayer.BT_PROPERTY_LPP_OFFLOAD_FEATURES:
+                        updateLppOffloadFeatureSupport(val);
+                        break;
+
                     default:
                         Log.e(TAG, "Property change not handled in Java land:" + type);
                 }
@@ -1022,6 +1029,37 @@
         mBufferConstraintList.complete(bufferConstraintList);
     }
 
+    /**
+     * @return the mNumberOfSupportedOffloadedLeCocSockets
+     */
+    int getNumberOfSupportedOffloadedLeCocSockets() {
+        return mNumberOfSupportedOffloadedLeCocSockets;
+    }
+
+    /**
+     * @return the mNumberOfSupportedOffloadedRfcommSockets
+     */
+    int getNumberOfSupportedOffloadedRfcommSockets() {
+        return mNumberOfSupportedOffloadedRfcommSockets;
+    }
+
+    private void updateLppOffloadFeatureSupport(byte[] val) {
+        if (val.length < 1) {
+            Log.e(TAG, "BT_PROPERTY_LPP_OFFLOAD_FEATURES: invalid value length");
+            return;
+        }
+        // TODO(b/342012881) Read mNumberOfSupportedOffloadedRfcommSockets from host stack
+        mNumberOfSupportedOffloadedLeCocSockets = (0xFF & ((int) val[0]));
+
+        Log.d(
+                TAG,
+                "BT_PROPERTY_LPP_OFFLOAD_FEATURES: update from Offload HAL"
+                        + " mNumberOfSupportedOffloadedLeCocSockets = "
+                        + mNumberOfSupportedOffloadedLeCocSockets
+                        + " mNumberOfSupportedOffloadedRfcommSockets = "
+                        + mNumberOfSupportedOffloadedRfcommSockets);
+    }
+
     void onBluetoothReady() {
         debugLog(
                 "onBluetoothReady, state="
diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
index 3a3e3c1..3be4cee 100644
--- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java
@@ -278,10 +278,6 @@
 
     private final BluetoothHciVendorSpecificDispatcher mBluetoothHciVendorSpecificDispatcher =
             new BluetoothHciVendorSpecificDispatcher();
-    private final BluetoothHciVendorSpecificNativeInterface
-            mBluetoothHciVendorSpecificNativeInterface =
-                    new BluetoothHciVendorSpecificNativeInterface(
-                            mBluetoothHciVendorSpecificDispatcher);
 
     private final Looper mLooper;
     private final AdapterServiceHandler mHandler;
@@ -346,6 +342,7 @@
     private BassClientService mBassClientService;
     private BatteryService mBatteryService;
     private BluetoothQualityReportNativeInterface mBluetoothQualityReportNativeInterface;
+    private BluetoothHciVendorSpecificNativeInterface mBluetoothHciVendorSpecificNativeInterface;
     private GattService mGattService;
     private ScanController mScanController;
 
@@ -696,7 +693,11 @@
         mBluetoothQualityReportNativeInterface.init();
 
         if (Flags.hciVendorSpecificExtension()) {
-            mBluetoothHciVendorSpecificNativeInterface.init();
+            mBluetoothHciVendorSpecificNativeInterface =
+                    requireNonNull(
+                            mBluetoothHciVendorSpecificNativeInterface.getInstance(),
+                            "mBluetoothHciVendorSpecificNativeInterface cannot be null");
+            mBluetoothHciVendorSpecificNativeInterface.init(mBluetoothHciVendorSpecificDispatcher);
         }
 
         mSdpManager = new SdpManager(this, mLooper);
@@ -4387,6 +4388,26 @@
             service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
             return service.mDatabaseManager.isMicrophonePreferredForCalls(device);
         }
+
+        @Override
+        public boolean isLeCocSocketOffloadSupported(AttributionSource source) {
+            AdapterService service = getService();
+            if (service == null) {
+                return false;
+            }
+            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+            return service.isLeCocSocketOffloadSupported();
+        }
+
+        @Override
+        public boolean isRfcommSocketOffloadSupported(AttributionSource source) {
+            AdapterService service = getService();
+            if (service == null) {
+                return false;
+            }
+            service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+            return service.isRfcommSocketOffloadSupported();
+        }
     }
 
     /**
@@ -7156,4 +7177,26 @@
             Log.e(TAG, "Error happened while removing contents: ", e);
         }
     }
+
+    /** Get the number of the supported offloaded LE COC sockets. */
+    public int getNumberOfSupportedOffloadedLeCocSockets() {
+        return mAdapterProperties.getNumberOfSupportedOffloadedLeCocSockets();
+    }
+
+    /** Check if the offloaded LE COC socket is supported. */
+    public boolean isLeCocSocketOffloadSupported() {
+        int val = getNumberOfSupportedOffloadedLeCocSockets();
+        return val > 0;
+    }
+
+    /** Get the number of the supported offloaded RFCOMM sockets. */
+    public int getNumberOfSupportedOffloadedRfcommSockets() {
+        return mAdapterProperties.getNumberOfSupportedOffloadedRfcommSockets();
+    }
+
+    /** Check if the offloaded RFCOMM socket is supported. */
+    public boolean isRfcommSocketOffloadSupported() {
+        int val = getNumberOfSupportedOffloadedRfcommSockets();
+        return val > 0;
+    }
 }
diff --git a/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java
index 7acf26f..dc35171 100644
--- a/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java
+++ b/android/app/src/com/android/bluetooth/btservice/BluetoothHciVendorSpecificNativeInterface.java
@@ -16,16 +16,39 @@
 
 package com.android.bluetooth.btservice;
 
-class BluetoothHciVendorSpecificNativeInterface {
-    private static final String TAG = "BluetoothHciVendorSpecificNativeInterface";
-    private final BluetoothHciVendorSpecificDispatcher mDispatcher;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
-    public BluetoothHciVendorSpecificNativeInterface(
-            BluetoothHciVendorSpecificDispatcher dispatcher) {
-        mDispatcher = dispatcher;
+public class BluetoothHciVendorSpecificNativeInterface {
+    private static final String TAG = "BluetoothHciVendorSpecificNativeInterface";
+
+    @GuardedBy("INSTANCE_LOCK")
+    private static BluetoothHciVendorSpecificNativeInterface sInstance;
+
+    private static final Object INSTANCE_LOCK = new Object();
+
+    /** Get singleton instance. */
+    public static BluetoothHciVendorSpecificNativeInterface getInstance() {
+        synchronized (INSTANCE_LOCK) {
+            if (sInstance == null) {
+                sInstance = new BluetoothHciVendorSpecificNativeInterface();
+            }
+            return sInstance;
+        }
     }
 
-    void init() {
+    /** Set singleton instance. */
+    @VisibleForTesting
+    static void setInstance(BluetoothHciVendorSpecificNativeInterface instance) {
+        synchronized (INSTANCE_LOCK) {
+            sInstance = instance;
+        }
+    }
+
+    private BluetoothHciVendorSpecificDispatcher mDispatcher;
+
+    void init(BluetoothHciVendorSpecificDispatcher dispatcher) {
+        mDispatcher = dispatcher;
         initNative();
     }
 
diff --git a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java
index d3062c3..05b0362 100644
--- a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java
+++ b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java
@@ -20,6 +20,7 @@
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothSocket;
+import android.bluetooth.BluetoothSocketSettings;
 import android.bluetooth.IBluetoothSocketManager;
 import android.content.AttributionSource;
 import android.os.Binder;
@@ -86,7 +87,85 @@
                                 Utils.uuidToByteArray(uuid),
                                 port,
                                 flag,
-                                Binder.getCallingUid()));
+                                Binder.getCallingUid(),
+                                0,
+                                "",
+                                0,
+                                0,
+                                0));
+    }
+
+    @Override
+    public ParcelFileDescriptor connectSocketwithOffload(
+            BluetoothDevice device,
+            int type,
+            ParcelUuid uuid,
+            int port,
+            int flag,
+            int dataPath,
+            String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize) {
+
+        enforceActiveUser();
+
+        if (!Utils.checkConnectPermissionForPreflight(mService)) {
+            return null;
+        }
+
+        if (dataPath != BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) {
+            mService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+            if (type != BluetoothSocket.TYPE_LE || !mService.isLeCocSocketOffloadSupported()) {
+                throw new IllegalStateException("Unsupported socket type for offload " + type);
+            }
+        }
+
+        String brEdrAddress =
+                Flags.identityAddressNullIfNotKnown()
+                        ? Utils.getBrEdrAddress(device)
+                        : mService.getIdentityAddress(device.getAddress());
+
+        Log.i(
+                TAG,
+                "connectSocketwithOffload: device="
+                        + device
+                        + ", type="
+                        + type
+                        + ", uuid="
+                        + uuid
+                        + ", port="
+                        + port
+                        + ", from "
+                        + Utils.getUidPidString()
+                        + ", dataPath="
+                        + dataPath
+                        + ", socketName="
+                        + socketName
+                        + ", hubId="
+                        + hubId
+                        + ", endpointId="
+                        + endpointId
+                        + ", maximumPacketSize="
+                        + maximumPacketSize);
+
+        return marshalFd(
+                mService.getNative()
+                        .connectSocket(
+                                Utils.getBytesFromAddress(
+                                        type == BluetoothSocket.TYPE_L2CAP_LE
+                                                ? device.getAddress()
+                                                : brEdrAddress),
+                                type,
+                                Utils.uuidToByteArray(uuid),
+                                port,
+                                flag,
+                                Binder.getCallingUid(),
+                                dataPath,
+                                socketName,
+                                hubId,
+                                endpointId,
+                                maximumPacketSize));
     }
 
     @Override
@@ -120,7 +199,77 @@
                                 Utils.uuidToByteArray(uuid),
                                 port,
                                 flag,
-                                Binder.getCallingUid()));
+                                Binder.getCallingUid(),
+                                0,
+                                "",
+                                0,
+                                0,
+                                0));
+    }
+
+    @Override
+    public ParcelFileDescriptor createSocketChannelWithOffload(
+            int type,
+            String serviceName,
+            ParcelUuid uuid,
+            int port,
+            int flag,
+            int dataPath,
+            String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize) {
+
+        enforceActiveUser();
+
+        if (!Utils.checkConnectPermissionForPreflight(mService)) {
+            return null;
+        }
+
+        if (dataPath != BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) {
+            mService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null);
+            if (type != BluetoothSocket.TYPE_LE || !mService.isLeCocSocketOffloadSupported()) {
+                throw new IllegalStateException("Unsupported socket type for offload " + type);
+            }
+        }
+
+        Log.i(
+                TAG,
+                "createSocketChannelWithOffload: type="
+                        + type
+                        + ", serviceName="
+                        + serviceName
+                        + ", uuid="
+                        + uuid
+                        + ", port="
+                        + port
+                        + ", from "
+                        + Utils.getUidPidString()
+                        + ", dataPath="
+                        + dataPath
+                        + ", socketName="
+                        + socketName
+                        + ", hubId="
+                        + hubId
+                        + ", endpointId="
+                        + endpointId
+                        + ", maximumPacketSize="
+                        + maximumPacketSize);
+
+        return marshalFd(
+                mService.getNative()
+                        .createSocketChannel(
+                                type,
+                                serviceName,
+                                Utils.uuidToByteArray(uuid),
+                                port,
+                                flag,
+                                Binder.getCallingUid(),
+                                dataPath,
+                                socketName,
+                                hubId,
+                                endpointId,
+                                maximumPacketSize));
     }
 
     @Override
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java
index 760b67d..8099c26 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java
@@ -17,6 +17,7 @@
 package com.android.bluetooth.pbapclient;
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
 
 import android.accounts.Account;
 import android.annotation.RequiresPermission;
@@ -468,7 +469,9 @@
 
     /* Utilize SDP, if available, to create a socket connection over L2CAP, RFCOMM specified
      * channel, or RFCOMM default channel. */
-    @RequiresPermission(BLUETOOTH_CONNECT)
+    @RequiresPermission(
+            allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+            conditional = true)
     private PbapClientSocket connectSocket(int transport, int channelOrPsm) {
         debug(
                 "Connect socket, transport="
diff --git a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java
index 4a17721..7438a02 100644
--- a/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java
+++ b/android/app/src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java
@@ -17,6 +17,7 @@
 package com.android.bluetooth.pbapclient;
 
 import static android.Manifest.permission.BLUETOOTH_CONNECT;
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
 
 import android.annotation.RequiresPermission;
 import android.bluetooth.BluetoothDevice;
@@ -101,7 +102,9 @@
     }
 
     /** Invokes the underlying BluetoothSocket#connect(), or does nothing if a socket is injected */
-    @RequiresPermission(BLUETOOTH_CONNECT)
+    @RequiresPermission(
+            allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+            conditional = true)
     public void connect() throws IOException {
         if (mSocket != null) {
             mSocket.connect();
diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
index b5ab602..31883c9 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/ActiveDeviceManagerTest.java
@@ -456,6 +456,43 @@
     }
 
     @Test
+    public void switchActiveDeviceFromLeToHfp_noFallbackToLe() {
+        // Turn off the dual mode audio flag
+        Utils.setDualModeAudioStateForTesting(false);
+
+        // Connect A2DP + HFP device, set it not active
+        a2dpConnected(mA2dpHeadsetDevice, true);
+        headsetConnected(mA2dpHeadsetDevice, true);
+        a2dpActiveDeviceChanged(null);
+        headsetActiveDeviceChanged(null);
+        mTestLooper.dispatchAll();
+
+        Mockito.clearInvocations(mHeadsetService);
+        Mockito.clearInvocations(mA2dpService);
+        Mockito.clearInvocations(mLeAudioService);
+
+        // Connect LE Audio device, set it to inactive
+        leAudioConnected(mLeAudioDevice);
+        leAudioActiveDeviceChanged(mLeAudioDevice);
+        mTestLooper.dispatchAll();
+        verify(mLeAudioService).setActiveDevice(mLeAudioDevice);
+        assertThat(mActiveDeviceManager.getLeAudioActiveDevice()).isEqualTo(mLeAudioDevice);
+
+        Mockito.clearInvocations(mHeadsetService);
+        Mockito.clearInvocations(mA2dpService);
+        Mockito.clearInvocations(mLeAudioService);
+
+        // Set LE Audio device to inactive
+        // Set A2DP + HFP device to active
+        leAudioActiveDeviceChanged(null);
+        headsetActiveDeviceChanged(mA2dpHeadsetDevice);
+        mTestLooper.dispatchAll();
+        // A2DP + HFP should now be active
+        verify(mLeAudioService, never()).setActiveDevice(mLeAudioDevice);
+        verify(mA2dpService).setActiveDevice(mA2dpHeadsetDevice);
+    }
+
+    @Test
     public void hfpActivatedAfterTimeout_shouldActivateA2dpAgain() {
         a2dpConnected(mA2dpHeadsetDevice, true);
         headsetConnected(mA2dpHeadsetDevice, true);
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 b00d809..bac576e 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
@@ -155,6 +155,7 @@
     private @Mock AdapterNativeInterface mNativeInterface;
     private @Mock BluetoothKeystoreNativeInterface mKeystoreNativeInterface;
     private @Mock BluetoothQualityReportNativeInterface mQualityNativeInterface;
+    private @Mock BluetoothHciVendorSpecificNativeInterface mHciVendorSpecificNativeInterface;
     private @Mock SdpManagerNativeInterface mSdpNativeInterface;
     private @Mock AdvertiseManagerNativeInterface mAdvertiseNativeInterface;
     private @Mock DistanceMeasurementNativeInterface mDistanceNativeInterface;
@@ -226,6 +227,7 @@
         AdapterNativeInterface.setInstance(mNativeInterface);
         BluetoothKeystoreNativeInterface.setInstance(mKeystoreNativeInterface);
         BluetoothQualityReportNativeInterface.setInstance(mQualityNativeInterface);
+        BluetoothHciVendorSpecificNativeInterface.setInstance(mHciVendorSpecificNativeInterface);
         SdpManagerNativeInterface.setInstance(mSdpNativeInterface);
         AdvertiseManagerNativeInterface.setInstance(mAdvertiseNativeInterface);
         DistanceMeasurementNativeInterface.setInstance(mDistanceNativeInterface);
@@ -354,6 +356,7 @@
         AdapterNativeInterface.setInstance(null);
         BluetoothKeystoreNativeInterface.setInstance(null);
         BluetoothQualityReportNativeInterface.setInstance(null);
+        BluetoothHciVendorSpecificNativeInterface.setInstance(null);
         SdpManagerNativeInterface.setInstance(null);
         AdvertiseManagerNativeInterface.setInstance(null);
         DistanceMeasurementNativeInterface.setInstance(null);
diff --git a/android/pandora/server/src/Pbap.kt b/android/pandora/server/src/Pbap.kt
index 5b7cef4..e8eab8b 100644
--- a/android/pandora/server/src/Pbap.kt
+++ b/android/pandora/server/src/Pbap.kt
@@ -159,8 +159,6 @@
             """
                 %d Lorem ipsum dolor sit amet, consectetur adipiscing elit.
                 Vivamus condimentum rhoncus est volutpat venenatis.
-                Fusce semper, sapien ut venenatis pellentesque,
-                lorem dui aliquam sapien, non pharetra diam neque id mi.
             """
     }
 }
diff --git a/flags/system_service.aconfig b/flags/system_service.aconfig
index 73f3061..ab54212 100644
--- a/flags/system_service.aconfig
+++ b/flags/system_service.aconfig
@@ -62,16 +62,6 @@
 }
 
 flag {
-    name: "ble_scan_setting_does_not_disconnect_if_bt_on"
-    namespace: "bluetooth"
-    description: "Stop calling unregAllGattClient when toggling the ble scan setting and bluetooth is ON"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-    bug: "379166793"
-}
-
-flag {
     name: "system_server_messenger"
     namespace: "bluetooth"
     description: "Replace binder call to the system server with a Messenger to enforce thread safety"
diff --git a/framework/api/current.txt b/framework/api/current.txt
index f77b9ec..8ee8e08 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -52,7 +52,7 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
     method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingL2capChannel() throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(String, java.util.UUID) throws java.io.IOException;
-    method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.bluetooth.BluetoothServerSocket listenUsingSocketSettings(@NonNull android.bluetooth.BluetoothSocketSettings) throws java.io.IOException;
+    method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @NonNull @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public android.bluetooth.BluetoothServerSocket listenUsingSocketSettings(@NonNull android.bluetooth.BluetoothSocketSettings) throws java.io.IOException;
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setName(String);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startDiscovery();
     method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback);
@@ -1094,7 +1094,7 @@
 
   public final class BluetoothSocket implements java.io.Closeable {
     method public void close() throws java.io.IOException;
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void connect() throws java.io.IOException;
+    method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public void connect() throws java.io.IOException;
     method public int getConnectionType();
     method public java.io.InputStream getInputStream() throws java.io.IOException;
     method public int getMaxReceivePacketSize();
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 06f611f..8368f67 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -127,7 +127,9 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAutoOnSupported();
     method public boolean isBleScanAlwaysAvailable();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int isDistanceMeasurementSupported();
+    method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isLeCocSocketOffloadSupported();
     method public boolean isLeEnabled();
+    method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isRfcommSocketOffloadSupported();
     method @NonNull public static String nameForState(int);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int notifyActiveDeviceChangeApplied(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean registerBluetoothConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.BluetoothConnectionCallback);
@@ -1152,6 +1154,25 @@
   public final class BluetoothSocket implements java.io.Closeable {
     method @FlaggedApi("com.android.bluetooth.flags.bt_socket_api_l2cap_cid") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getL2capLocalChannelId() throws java.io.IOException;
     method @FlaggedApi("com.android.bluetooth.flags.bt_socket_api_l2cap_cid") @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getL2capRemoteChannelId() throws java.io.IOException;
+    method @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public long getSocketId() throws java.io.IOException;
+  }
+
+  @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public final class BluetoothSocketSettings {
+    method public int getDataPath();
+    method public long getEndpointId();
+    method public long getHubId();
+    method public int getRequestedMaximumPacketSize();
+    method @NonNull public String getSocketName();
+    field public static final int DATA_PATH_HARDWARE_OFFLOAD = 1; // 0x1
+    field public static final int DATA_PATH_NO_OFFLOAD = 0; // 0x0
+  }
+
+  @FlaggedApi("com.android.bluetooth.flags.socket_settings_api") public static final class BluetoothSocketSettings.Builder {
+    method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setDataPath(int);
+    method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setEndpointId(long);
+    method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setHubId(long);
+    method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setRequestedMaximumPacketSize(int);
+    method @NonNull public android.bluetooth.BluetoothSocketSettings.Builder setSocketName(@NonNull String);
   }
 
   public final class BluetoothStatusCodes {
diff --git a/framework/jarjar-rules.txt b/framework/jarjar-rules.txt
index 93ec449..eb2bfc0 100644
--- a/framework/jarjar-rules.txt
+++ b/framework/jarjar-rules.txt
@@ -13,3 +13,6 @@
 rule com.android.bluetooth.flags.** com.android.bluetooth.jarjar.@0
 rule bluetooth.constants.** com.android.bluetooth.jarjar.@0
 rule android.media.audio.Flags com.android.bluetooth.jarjar.audio.Flags
+rule android.media.audio.CustomFeatureFlags com.android.bluetooth.jarjar.audio.CustomFeatureFlags
+rule android.media.audio.FakeFeatureFlagsImpl com.android.bluetooth.jarjar.audio.FakeFeatureFlagsImpl
+rule android.media.audio.FeatureFlags com.android.bluetooth.jarjar.audio.FeatureFlags
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index 2d26bf3..2ffc178 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -4564,6 +4564,14 @@
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
      * {@link BluetoothServerSocket}.
      *
+     * <p>Use {@link BluetoothDevice#createUsingSocketSettings(BluetoothSocketSettings)} to connect
+     * to this server socket from another Android device using the L2cap protocol/service
+     * multiplexer(PSM) value or the RFCOMM service UUID as input.
+     *
+     * <p>This API requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission
+     * only when {@code settings.getDataPath()} is different from {@link
+     * BluetoothSocketSettings#DATA_PATH_NO_OFFLOAD}.
+     *
      * <p>This API supports {@link BluetoothSocket#TYPE_RFCOMM} and {{@link BluetoothSocket#TYPE_LE}
      * only, which can be set using {@link BluetoothSocketSettings#setSocketType()}.
      * <li>For `BluetoothSocket.TYPE_RFCOMM`: The RFCOMM UUID must be provided using {@link
@@ -4574,10 +4582,6 @@
      *     application exits unexpectedly. The mechanism for disclosing the PSM value to the client
      *     is application-defined.
      *
-     *     <p>Use {@link BluetoothDevice#createUsingSocketSettings(BluetoothSocketSettings)} to
-     *     connect to this server socket from another Android device using the L2cap
-     *     protocol/service multiplexer(PSM) value or the RFCOMM service UUID as input.
-     *
      * @param settings Bluetooth socket settings {@link BluetoothSocketSettings}.
      * @return a {@link BluetoothServerSocket}
      * @throws IllegalArgumentException if BluetoothSocket#TYPE_RFCOMM socket is requested with no
@@ -4586,7 +4590,9 @@
      *     Connection-oriented Channel (CoC).
      */
     @RequiresBluetoothConnectPermission
-    @RequiresPermission(BLUETOOTH_CONNECT)
+    @RequiresPermission(
+            allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+            conditional = true)
     @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
     public @NonNull BluetoothServerSocket listenUsingSocketSettings(
             @NonNull BluetoothSocketSettings settings) throws IOException {
@@ -4597,24 +4603,64 @@
             if (settings.getRfcommUuid() == null) {
                 throw new IllegalArgumentException("RFCOMM server missing UUID");
             }
-            return createNewRfcommSocketAndRecord(
-                    settings.getRfcommServiceName(),
-                    settings.getRfcommUuid(),
-                    settings.isAuthenticationRequired(),
-                    settings.isEncryptionRequired());
+            if (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) {
+                socket =
+                        new BluetoothServerSocket(
+                                settings.getSocketType(),
+                                settings.isAuthenticationRequired(),
+                                settings.isEncryptionRequired(),
+                                new ParcelUuid(settings.getRfcommUuid()));
+            } else {
+                socket =
+                        new BluetoothServerSocket(
+                                settings.getSocketType(),
+                                settings.isAuthenticationRequired(),
+                                settings.isEncryptionRequired(),
+                                -1,
+                                new ParcelUuid(settings.getRfcommUuid()),
+                                false,
+                                false,
+                                settings.getDataPath(),
+                                settings.getSocketName(),
+                                settings.getHubId(),
+                                settings.getEndpointId(),
+                                settings.getRequestedMaximumPacketSize());
+            }
+            socket.setServiceName(settings.getRfcommServiceName());
         } else if (type == BluetoothSocket.TYPE_LE) {
-            socket =
-                    new BluetoothServerSocket(
-                            settings.getSocketType(),
-                            settings.isAuthenticationRequired(),
-                            settings.isEncryptionRequired(),
-                            SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
-                            false,
-                            false);
+            if (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) {
+                socket =
+                        new BluetoothServerSocket(
+                                settings.getSocketType(),
+                                settings.isAuthenticationRequired(),
+                                settings.isEncryptionRequired(),
+                                SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
+                                false,
+                                false);
+            } else {
+                socket =
+                        new BluetoothServerSocket(
+                                settings.getSocketType(),
+                                settings.isAuthenticationRequired(),
+                                settings.isEncryptionRequired(),
+                                SOCKET_CHANNEL_AUTO_STATIC_NO_SDP,
+                                null,
+                                false,
+                                false,
+                                settings.getDataPath(),
+                                settings.getSocketName(),
+                                settings.getHubId(),
+                                settings.getEndpointId(),
+                                settings.getRequestedMaximumPacketSize());
+            }
         } else {
-            throw new IOException("Error: Invalid socket type: " + type);
+            throw new IllegalArgumentException("Error: Invalid socket type: " + type);
         }
-        int errno = socket.mSocket.bindListen();
+        int errno;
+        errno =
+                (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD)
+                        ? socket.mSocket.bindListen()
+                        : socket.mSocket.bindListenWithOffload();
         if (errno != 0) {
             throw new IOException("Error: " + errno);
         }
@@ -5916,4 +5962,98 @@
             mServiceLock.readLock().unlock();
         }
     }
+
+    /**
+     * Returns whether LE CoC socket hardware offload is supported.
+     *
+     * <p>Bluetooth socket hardware offload allows the system to handle Bluetooth communication on a
+     * low-power processor, improving efficiency and reducing power consumption. This is achieved by
+     * providing channel information of an already connected {@link BluetoothSocket} to offload
+     * endpoints (e.g., offload stacks and applications). The offload stack can then decode received
+     * packets and pass them to the appropriate offload application without waking up the main
+     * application processor. This API allows offload endpoints to utilize Bluetooth sockets while
+     * the host stack retains control over the connection.
+     *
+     * <p>To configure a socket for hardware offload, use the following {@link
+     * BluetoothSocketSettings} methods:
+     *
+     * <ul>
+     *   <li>{@link BluetoothSocketSettings#setDataPath(int)} with {@link
+     *       BluetoothSocketSettings#DATA_PATH_HARDWARE_OFFLOAD}
+     *   <li>{@link BluetoothSocketSettings#setHubId(long)}
+     *   <li>{@link BluetoothSocketSettings#setEndpointId(long)}
+     * </ul>
+     *
+     * <p>This functionality is provided as a System API because only OEM specific system
+     * applications can be offloaded as endpoints in the low-power processor.
+     *
+     * @return {@code true} if LE CoC socket hardware offload is supported, {@code false} otherwise.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+    @RequiresPermission(BLUETOOTH_PRIVILEGED)
+    public boolean isLeCocSocketOffloadSupported() {
+        if (!isEnabled()) {
+            return false;
+        }
+        mServiceLock.readLock().lock();
+        try {
+            if (mService != null) {
+                return mService.isLeCocSocketOffloadSupported(mAttributionSource);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether RFCOMM socket hardware offload is supported.
+     *
+     * <p>Bluetooth socket hardware offload allows the system to handle Bluetooth communication on a
+     * low-power processor, improving efficiency and reducing power consumption. This is achieved by
+     * providing channel information of an already connected {@link BluetoothSocket} to offload
+     * endpoints (e.g., offload stacks and applications). The offload stack can then decode received
+     * packets and pass them to the appropriate offload application without waking up the main
+     * application processor. This API allows offload endpoints to utilize Bluetooth sockets while
+     * the host stack retains control over the connection.
+     *
+     * <p>To configure a socket for hardware offload, use the following {@link
+     * BluetoothSocketSettings} methods:
+     *
+     * <ul>
+     *   <li>{@link BluetoothSocketSettings#setDataPath(int)} with {@link
+     *       BluetoothSocketSettings#DATA_PATH_HARDWARE_OFFLOAD}
+     *   <li>{@link BluetoothSocketSettings#setHubId(long)}
+     *   <li>{@link BluetoothSocketSettings#setEndpointId(long)}
+     * </ul>
+     *
+     * <p>This functionality is provided as a System API because only OEM specific system
+     * applications can be offloaded as endpoints in the low-power processor.
+     *
+     * @return {@code true} if RFCOMM socket hardware offload is supported, {@code false} otherwise.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+    @RequiresPermission(BLUETOOTH_PRIVILEGED)
+    public boolean isRfcommSocketOffloadSupported() {
+        if (!isEnabled()) {
+            return false;
+        }
+        mServiceLock.readLock().lock();
+        try {
+            if (mService != null) {
+                return mService.isRfcommSocketOffloadSupported(mAttributionSource);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return false;
+    }
 }
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index d200e6a..795e911 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -3366,13 +3366,30 @@
                 throw new IllegalArgumentException("Invalid PSM/Channel value: " + psm);
             }
         }
-        return new BluetoothSocket(
-                this,
-                settings.getSocketType(),
-                settings.isAuthenticationRequired(),
-                settings.isEncryptionRequired(),
-                psm,
-                uuid);
+        if (settings.getDataPath() == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) {
+            return new BluetoothSocket(
+                    this,
+                    settings.getSocketType(),
+                    settings.isAuthenticationRequired(),
+                    settings.isEncryptionRequired(),
+                    psm,
+                    uuid);
+        } else {
+            return new BluetoothSocket(
+                    this,
+                    settings.getSocketType(),
+                    settings.isAuthenticationRequired(),
+                    settings.isEncryptionRequired(),
+                    psm,
+                    uuid,
+                    false,
+                    false,
+                    settings.getDataPath(),
+                    settings.getSocketName(),
+                    settings.getHubId(),
+                    settings.getEndpointId(),
+                    settings.getRequestedMaximumPacketSize());
+        }
     }
 
     /**
diff --git a/framework/java/android/bluetooth/BluetoothServerSocket.java b/framework/java/android/bluetooth/BluetoothServerSocket.java
index b1a01886..f20a03d 100644
--- a/framework/java/android/bluetooth/BluetoothServerSocket.java
+++ b/framework/java/android/bluetooth/BluetoothServerSocket.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Handler;
@@ -154,6 +155,60 @@
     }
 
     /**
+     * Construct a socket for incoming connections.
+     *
+     * @param type type of socket
+     * @param auth require the remote device to be authenticated
+     * @param encrypt require the connection to be encrypted
+     * @param port remote port
+     * @param uuid uuid
+     * @param pitm enforce person-in-the-middle protection for authentication.
+     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
+     * @param dataPath data path used for this socket
+     * @param socketName user-friendly name for this socket
+     * @param hubId ID of the hub to which the end point belongs
+     * @param endpointId ID of the endpoint within the hub that is associated with this socket
+     * @param maximumPacketSize The maximum size (in bytes) of a single data packet
+     * @throws IOException On error, for example Bluetooth not available, or insufficient privileges
+     */
+    /*package*/ BluetoothServerSocket(
+            int type,
+            boolean auth,
+            boolean encrypt,
+            int port,
+            ParcelUuid uuid,
+            boolean pitm,
+            boolean min16DigitPin,
+            int dataPath,
+            @NonNull String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize)
+            throws IOException {
+        mSocketCreationTimeMillis = System.currentTimeMillis();
+        mType = type;
+        mChannel = port;
+        mSocket =
+                new BluetoothSocket(
+                        type,
+                        auth,
+                        encrypt,
+                        port,
+                        uuid,
+                        pitm,
+                        min16DigitPin,
+                        dataPath,
+                        socketName,
+                        hubId,
+                        endpointId,
+                        maximumPacketSize);
+        if (port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            mSocket.setExcludeSdp(true);
+        }
+        mSocketCreationLatencyMillis = System.currentTimeMillis() - mSocketCreationTimeMillis;
+    }
+
+    /**
      * Block until a connection is established.
      *
      * <p>Returns a connected {@link BluetoothSocket} on successful connection.
diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java
index 901407c..97bdf59 100644
--- a/framework/java/android/bluetooth/BluetoothSocket.java
+++ b/framework/java/android/bluetooth/BluetoothSocket.java
@@ -157,6 +157,8 @@
     /*package*/ static final int SEC_FLAG_AUTH_PITM = 1 << 3;
     /*package*/ static final int SEC_FLAG_AUTH_16_DIGIT = 1 << 4;
 
+    /*package*/ static final String DEFAULT_SOCKET_NAME = "default_name";
+
     private final int mType; /* one of TYPE_RFCOMM etc */
     private BluetoothDevice mDevice; /* remote device */
     private String mAddress; /* remote address */
@@ -165,6 +167,11 @@
     private final BluetoothInputStream mInputStream;
     private final BluetoothOutputStream mOutputStream;
     private final ParcelUuid mUuid;
+    private final int mDataPath;
+    private final String mSocketName;
+    private final long mHubId;
+    private final long mEndpointId;
+    private final int mMaximumPacketSize;
 
     /** when true no SPP SDP record will be created */
     private boolean mExcludeSdp = false;
@@ -184,12 +191,15 @@
     @UnsupportedAppUsage private int mPort; /* RFCOMM channel or L2CAP psm */
     private String mServiceName;
 
-    private static final int SOCK_SIGNAL_SIZE = 36;
+    private static final int SOCK_CONNECTION_SIGNAL_SIZE = 44;
+    private static final long INVALID_SOCKET_ID = 0;
+    private static final int SOCK_ACCEPT_SIGNAL_SIZE = 4;
 
     private ByteBuffer mL2capBuffer = null;
     private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer.
     private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received.
     private ParcelUuid mConnectionUuid;
+    private long mSocketId; // Socket ID in connected state.
 
     private long mSocketCreationTimeNanos = 0;
     private long mSocketCreationLatencyNanos = 0;
@@ -245,6 +255,41 @@
             boolean pitm,
             boolean min16DigitPin)
             throws IOException {
+        this(type, auth, encrypt, port, uuid, pitm, min16DigitPin, 0, DEFAULT_SOCKET_NAME, 0, 0, 0);
+    }
+
+    /**
+     * Construct a BluetoothSocket.
+     *
+     * @param type type of socket
+     * @param auth require the remote device to be authenticated
+     * @param encrypt require the connection to be encrypted
+     * @param port remote port
+     * @param uuid SDP uuid
+     * @param pitm enforce person-in-the-middle protection.
+     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
+     * @param dataPath data path used for this socket
+     * @param socketName user-friendly name for this socket
+     * @param hubId ID of the hub to which the end point belongs
+     * @param endpointId ID of the endpoint within the hub that is associated with this socket
+     * @param maximumPacketSize The maximum size (in bytes) of a single data packet
+     * @throws IOException On error, for example Bluetooth not available, or insufficient privileges
+     */
+    @RequiresPermission(allOf = {BLUETOOTH_CONNECT, LOCAL_MAC_ADDRESS})
+    /*package*/ BluetoothSocket(
+            int type,
+            boolean auth,
+            boolean encrypt,
+            int port,
+            ParcelUuid uuid,
+            boolean pitm,
+            boolean min16DigitPin,
+            int dataPath,
+            @NonNull String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize)
+            throws IOException {
         if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
         mSocketCreationTimeNanos = System.nanoTime();
         if (type == BluetoothSocket.TYPE_RFCOMM
@@ -267,6 +312,11 @@
         mPort = port;
         // this constructor to be called only from BluetoothServerSocket
         mDevice = null;
+        mDataPath = dataPath;
+        mSocketName = socketName;
+        mHubId = hubId;
+        mEndpointId = endpointId;
+        mMaximumPacketSize = maximumPacketSize;
 
         mSocketState = SocketState.INIT;
 
@@ -322,6 +372,55 @@
             boolean pitm,
             boolean min16DigitPin)
             throws IOException {
+        this(
+                device,
+                type,
+                auth,
+                encrypt,
+                port,
+                uuid,
+                pitm,
+                min16DigitPin,
+                0,
+                DEFAULT_SOCKET_NAME,
+                0,
+                0,
+                0);
+    }
+
+    /**
+     * Construct a BluetoothSocket.
+     *
+     * @param device remote device that this socket can connect to
+     * @param type type of socket
+     * @param auth require the remote device to be authenticated
+     * @param encrypt require the connection to be encrypted
+     * @param port remote port
+     * @param uuid SDP uuid
+     * @param pitm enforce person-in-the-middle protection.
+     * @param min16DigitPin enforce a minimum length of 16 digits for a sec mode 2 connection
+     * @param dataPath data path used for this socket
+     * @param socketName user-friendly name for this socket
+     * @param hubId ID of the hub to which the end point belongs
+     * @param endpointId ID of the endpoint within the hub that is associated with this socket
+     * @param maximumPacketSize The maximum size (in bytes) of a single data packet
+     * @throws IOException On error, for example Bluetooth not available, or insufficient privileges
+     */
+    /*package*/ BluetoothSocket(
+            @NonNull BluetoothDevice device,
+            int type,
+            boolean auth,
+            boolean encrypt,
+            int port,
+            ParcelUuid uuid,
+            boolean pitm,
+            boolean min16DigitPin,
+            int dataPath,
+            @NonNull String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize)
+            throws IOException {
         if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
         mSocketCreationTimeNanos = System.nanoTime();
         if (type == BluetoothSocket.TYPE_RFCOMM
@@ -343,6 +442,11 @@
         mEncrypt = encrypt;
         mDevice = device;
         mPort = port;
+        mDataPath = dataPath;
+        mSocketName = socketName;
+        mHubId = hubId;
+        mEndpointId = endpointId;
+        mMaximumPacketSize = maximumPacketSize;
 
         mSocketState = SocketState.INIT;
 
@@ -393,11 +497,17 @@
         mMaxRxPacketSize = s.mMaxRxPacketSize;
         mMaxTxPacketSize = s.mMaxTxPacketSize;
         mConnectionUuid = s.mConnectionUuid;
+        mSocketId = s.mSocketId;
 
         mServiceName = s.mServiceName;
         mExcludeSdp = s.mExcludeSdp;
         mAuthPitm = s.mAuthPitm;
         mMin16DigitPin = s.mMin16DigitPin;
+        mDataPath = s.mDataPath;
+        mSocketName = s.mSocketName;
+        mHubId = s.mHubId;
+        mEndpointId = s.mEndpointId;
+        mMaximumPacketSize = s.mMaximumPacketSize;
         mSocketCreationTimeNanos = s.mSocketCreationTimeNanos;
         mSocketCreationLatencyNanos = s.mSocketCreationLatencyNanos;
     }
@@ -524,11 +634,17 @@
      *
      * <p>{@link #close} can be used to abort this call from another thread.
      *
+     * <p>Requires the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission only when
+     * {@code mDataPath} is different from {@link BluetoothSocketSettings#DATA_PATH_NO_OFFLOAD}.
+     *
      * @throws BluetoothSocketException in case of failure, with the corresponding error code.
      * @throws IOException for other errors (eg: InputStream read failures etc.).
      */
+    @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
     @RequiresBluetoothConnectPermission
-    @RequiresPermission(BLUETOOTH_CONNECT)
+    @RequiresPermission(
+            allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+            conditional = true)
     public void connect() throws IOException {
         IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService();
         long socketConnectionTimeNanos = System.nanoTime();
@@ -547,7 +663,30 @@
             if (socketManager == null) {
                 throw new BluetoothSocketException(BluetoothSocketException.SOCKET_MANAGER_FAILURE);
             }
-            mPfd = socketManager.connectSocket(mDevice, mType, mUuid, mPort, getSecurityFlags());
+            if (Flags.socketSettingsApi()) {
+                if (mDataPath == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) {
+                    mPfd =
+                            socketManager.connectSocket(
+                                    mDevice, mType, mUuid, mPort, getSecurityFlags());
+                } else {
+                    mPfd =
+                            socketManager.connectSocketwithOffload(
+                                    mDevice,
+                                    mType,
+                                    mUuid,
+                                    mPort,
+                                    getSecurityFlags(),
+                                    mDataPath,
+                                    mSocketName,
+                                    mHubId,
+                                    mEndpointId,
+                                    mMaximumPacketSize);
+                }
+            } else {
+                mPfd =
+                        socketManager.connectSocket(
+                                mDevice, mType, mUuid, mPort, getSecurityFlags());
+            }
             synchronized (this) {
                 Log.i(TAG, "connect(), SocketState: " + mSocketState + ", mPfd: " + mPfd);
                 if (mSocketState == SocketState.CLOSED) {
@@ -675,7 +814,7 @@
             if (DBG) Log.d(TAG, "bindListen(): channel=" + channel + ", mPort=" + mPort);
             if (mPort <= -1) {
                 mPort = channel;
-            } // else ASSERT(mPort == channel)
+            }
             ret = 0;
         } catch (IOException e) {
             if (mPfd != null) {
@@ -692,6 +831,98 @@
         return ret;
     }
 
+    /**
+     * Currently returns unix errno instead of throwing IOException, so that BluetoothAdapter can
+     * check the error code for EADDRINUSE
+     */
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(
+            allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED},
+            conditional = true)
+    /*package*/ int bindListenWithOffload() {
+        int ret;
+        if (mSocketState == SocketState.CLOSED) return EBADFD;
+        IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService();
+        if (bluetoothProxy == null) {
+            Log.e(TAG, "bindListenWithOffload() fail, reason: bluetooth is off");
+            return -1;
+        }
+        try {
+            if (DBG) Log.d(TAG, "bindListenWithOffload(): mPort=" + mPort + ", mType=" + mType);
+            IBluetoothSocketManager socketManager = bluetoothProxy.getSocketManager();
+            if (socketManager == null) {
+                Log.e(TAG, "bindListenWithOffload() bt get socket manager failed");
+                return -1;
+            }
+            mPfd =
+                    socketManager.createSocketChannelWithOffload(
+                            mType,
+                            mServiceName,
+                            mUuid,
+                            mPort,
+                            getSecurityFlags(),
+                            mDataPath,
+                            mSocketName,
+                            mHubId,
+                            mEndpointId,
+                            mMaximumPacketSize);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+            return -1;
+        }
+
+        // read out port number
+        try {
+            synchronized (this) {
+                if (DBG) {
+                    Log.d(
+                            TAG,
+                            "bindListenWithOffload(), SocketState: "
+                                    + mSocketState
+                                    + ", mPfd: "
+                                    + mPfd);
+                }
+                if (mSocketState != SocketState.INIT) return EBADFD;
+                if (mPfd == null) return -1;
+                FileDescriptor fd = mPfd.getFileDescriptor();
+                if (fd == null) {
+                    Log.e(TAG, "bindListenWithOffload(), null file descriptor");
+                    return -1;
+                }
+
+                if (DBG) Log.d(TAG, "bindListenWithOffload(), Create LocalSocket");
+                mSocket = new LocalSocket(fd);
+                if (DBG) Log.d(TAG, "bindListenWithOffload(), new LocalSocket.getInputStream()");
+                mSocketIS = mSocket.getInputStream();
+                mSocketOS = mSocket.getOutputStream();
+            }
+            if (DBG) Log.d(TAG, "bindListenWithOffload(), readInt mSocketIS: " + mSocketIS);
+            int channel = readInt(mSocketIS);
+            synchronized (this) {
+                if (mSocketState == SocketState.INIT) {
+                    mSocketState = SocketState.LISTENING;
+                }
+            }
+            if (DBG) Log.d(TAG, "bindListenWithOffload(): channel=" + channel + ", mPort=" + mPort);
+            if (mPort <= -1) {
+                mPort = channel;
+            }
+            ret = 0;
+        } catch (IOException e) {
+            if (mPfd != null) {
+                try {
+                    mPfd.close();
+                } catch (IOException e1) {
+                    Log.e(TAG, "bindListenWithOffload, close mPfd: " + e1);
+                }
+                mPfd = null;
+            }
+            Log.e(TAG, "bindListenWithOffload, fail to get port number, exception: " + e);
+            return -1;
+        }
+        return ret;
+    }
+
     /*package*/ BluetoothSocket accept(int timeout) throws IOException {
         BluetoothSocket acceptedSocket;
         if (mSocketState != SocketState.LISTENING) {
@@ -701,7 +932,13 @@
         if (timeout > 0) {
             mSocket.setSoTimeout(timeout);
         }
-        String RemoteAddr = waitSocketSignal(mSocketIS);
+        sendSocketAcceptSignal(mSocketOS, true);
+        String RemoteAddr;
+        try {
+            RemoteAddr = waitSocketSignal(mSocketIS);
+        } finally {
+            sendSocketAcceptSignal(mSocketOS, false);
+        }
         if (timeout > 0) {
             mSocket.setSoTimeout(0);
         }
@@ -836,6 +1073,7 @@
                     mPfd = null;
                 }
                 mConnectionUuid = null;
+                mSocketId = INVALID_SOCKET_ID;
             }
         }
     }
@@ -1016,6 +1254,25 @@
         return cid;
     }
 
+    /**
+     * Returns the socket ID assigned to the open connection on this BluetoothSocket. This socket ID
+     * is a unique identifier for the socket. It is valid only while the socket is connected.
+     *
+     * @return The socket ID in connected state.
+     * @throws BluetoothSocketException If the socket is not connected or an error occurs while
+     *     retrieving the socket ID.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_SOCKET_SETTINGS_API)
+    @RequiresNoPermission
+    public long getSocketId() throws IOException {
+        if (mSocketState != SocketState.CONNECTED || mSocketId == INVALID_SOCKET_ID) {
+            throw new BluetoothSocketException(BluetoothSocketException.SOCKET_CLOSED);
+        }
+        return mSocketId;
+    }
+
     /** @hide */
     @RequiresNoPermission
     public ParcelFileDescriptor getParcelFileDescriptor() {
@@ -1034,17 +1291,53 @@
                 addr[5]);
     }
 
+    /**
+     * Sends a socket accept signal to the host stack.
+     *
+     * <p>This method is used to notify the host stack whether the host application is actively
+     * accepting a new connection or not. It sends a signal containing the acceptance status to the
+     * output stream associated with the socket.
+     *
+     * <p>This method is only effective when the data path is not {@link
+     * BluetoothSocketSettings#DATA_PATH_NO_OFFLOAD}.
+     *
+     * @param os The output stream to write the signal to.
+     * @param isAccepting {@code true} if the socket connection is being accepted, {@code false}
+     *     otherwise.
+     * @throws IOException If an I/O error occurs while writing to the output stream.
+     * @hide
+     */
+    private void sendSocketAcceptSignal(OutputStream os, boolean isAccepting) throws IOException {
+        if (Flags.socketSettingsApi()) {
+            if (mDataPath == BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD) {
+                return;
+            }
+            Log.d(TAG, "sendSocketAcceptSignal" + " isAccepting " + isAccepting);
+            byte[] sig = new byte[SOCK_ACCEPT_SIGNAL_SIZE];
+            ByteBuffer bb = ByteBuffer.wrap(sig);
+            bb.order(ByteOrder.nativeOrder());
+            bb.putShort((short) SOCK_ACCEPT_SIGNAL_SIZE);
+            bb.putShort((short) (isAccepting ? 1 : 0));
+            os.write(sig, 0, SOCK_ACCEPT_SIGNAL_SIZE);
+        }
+    }
+
     private String waitSocketSignal(InputStream is) throws IOException {
-        byte[] sig = new byte[SOCK_SIGNAL_SIZE];
+        byte[] sig = new byte[SOCK_CONNECTION_SIGNAL_SIZE];
         int ret = readAll(is, sig);
         if (VDBG) {
-            Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE + " bytes signal ret: " + ret);
+            Log.d(
+                    TAG,
+                    "waitSocketSignal read "
+                            + SOCK_CONNECTION_SIGNAL_SIZE
+                            + " bytes signal ret: "
+                            + ret);
         }
         ByteBuffer bb = ByteBuffer.wrap(sig);
         /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
         bb.order(ByteOrder.nativeOrder());
         int size = bb.getShort();
-        if (size != SOCK_SIGNAL_SIZE) {
+        if (size != SOCK_CONNECTION_SIGNAL_SIZE) {
             throw new IOException("Connection failure, wrong signal size: " + size);
         }
         byte[] addr = new byte[6];
@@ -1056,6 +1349,7 @@
         long uuidLsb = bb.getLong();
         long uuidMsb = bb.getLong();
         mConnectionUuid = new ParcelUuid(new UUID(uuidMsb, uuidLsb));
+        mSocketId = bb.getLong();
         String RemoteAddr = convertAddr(addr);
         if (VDBG) {
             Log.d(
@@ -1073,7 +1367,9 @@
                             + " MaxTxPktSize: "
                             + mMaxTxPacketSize
                             + " mConnectionUuid: "
-                            + mConnectionUuid.toString());
+                            + mConnectionUuid.toString()
+                            + " mSocketId: "
+                            + mSocketId);
         }
         if (status != 0) {
             throw new IOException("Connection failure, status: " + status);
diff --git a/framework/java/android/bluetooth/BluetoothSocketException.java b/framework/java/android/bluetooth/BluetoothSocketException.java
index aba95a3..4bf84ad 100644
--- a/framework/java/android/bluetooth/BluetoothSocketException.java
+++ b/framework/java/android/bluetooth/BluetoothSocketException.java
@@ -31,8 +31,8 @@
 /**
  * Thrown when an error occurs during a Bluetooth Socket related exception.
  *
- * <p>This is currently only intended to be thrown for a failure during {@link
- * BluetoothSocket#connect()} operation.
+ * <p>This is currently intended to be thrown for a failure during {@link BluetoothSocket}
+ * operations.
  */
 public class BluetoothSocketException extends IOException {
 
@@ -70,8 +70,8 @@
     public static final int UNSPECIFIED = 0;
 
     /**
-     * Error code during connect when socket connection fails for unknown reason during L2CAP
-     * connection.
+     * Error code returned by {@link BluetoothSocket} during a L2CAP-related socket operation that
+     * failed for an unknown reason.
      */
     public static final int L2CAP_UNKNOWN = 1;
 
@@ -141,13 +141,22 @@
     /** Error code during connect when L2CAP connection timeout. */
     public static final int L2CAP_TIMEOUT = 14;
 
-    /** Error code during connect when Bluetooth is off and socket connection is triggered. */
+    /**
+     * Error code returned by {@link BluetoothSocket} during a socket operation that failed because
+     * Bluetooth is turned off.
+     */
     public static final int BLUETOOTH_OFF_FAILURE = 15;
 
-    /** Error code during connect when socket manager is not available. */
+    /**
+     * Error code returned by {@link BluetoothSocket} during a socket operation that failed because
+     * socket manager is not available.
+     */
     public static final int SOCKET_MANAGER_FAILURE = 16;
 
-    /** Error code during connect when socket is closed. */
+    /**
+     * Error code returned by {@link BluetoothSocket} during a socket operation that failed because
+     * the socket has been closed.
+     */
     public static final int SOCKET_CLOSED = 17;
 
     /** Error code during connect for generic socket connection failures. */
diff --git a/framework/java/android/bluetooth/BluetoothSocketSettings.java b/framework/java/android/bluetooth/BluetoothSocketSettings.java
index 6fa8b0d..b7706d5 100644
--- a/framework/java/android/bluetooth/BluetoothSocketSettings.java
+++ b/framework/java/android/bluetooth/BluetoothSocketSettings.java
@@ -18,14 +18,21 @@
 
 import static android.bluetooth.BluetoothSocket.SocketType;
 
+import static java.util.Objects.requireNonNull;
+
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresNoPermission;
+import android.annotation.SystemApi;
 
 import com.android.bluetooth.flags.Flags;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
 import java.util.UUID;
 
 /**
@@ -42,6 +49,64 @@
 
     private static final int L2CAP_PSM_UNSPECIFIED = -1;
 
+    /**
+     * Annotation to define the data path used for Bluetooth socket communication. This determines
+     * how data flows between the application and the Bluetooth controller.
+     *
+     * @hide
+     */
+    @IntDef(
+            prefix = {"DATA_PATH_"},
+            value = {DATA_PATH_NO_OFFLOAD, DATA_PATH_HARDWARE_OFFLOAD})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SocketDataPath {}
+
+    /**
+     * Non-offload data path where the application's socket data is processed by the main Bluetooth
+     * stack.
+     *
+     * @hide
+     */
+    @SystemApi public static final int DATA_PATH_NO_OFFLOAD = 0;
+
+    /**
+     * Hardware offload data path where the application's socket data is processed by a offloaded
+     * application running on the low-power processor.
+     *
+     * <p>Using this data path requires the {@code BLUETOOTH_PRIVILEGED} permission, which will be
+     * checked when a socket connection or channel is created.
+     *
+     * @hide
+     */
+    @SystemApi public static final int DATA_PATH_HARDWARE_OFFLOAD = 1;
+
+    /**
+     * Maximum size (in bytes) of a data packet that can be received from the endpoint when using
+     * {@link #DATA_PATH_HARDWARE_OFFLOAD}.
+     */
+    @SystemApi private static final int HARDWARE_OFFLOAD_PACKET_MAX_SIZE = 65535;
+
+    /**
+     * Maximum length (in bytes) of a socket name when using {@link #DATA_PATH_HARDWARE_OFFLOAD}.
+     */
+    @SystemApi private static final int HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH = 127;
+
+    /**
+     * Constant representing an invalid hub ID. This value indicates that a hub ID has not been
+     * assigned or is not valid.
+     *
+     * @hide
+     */
+    private static final long INVALID_HUB_ID = 0;
+
+    /**
+     * Constant representing an invalid hub endpoint ID. This value indicates that an endpoint ID
+     * has not been assigned or is not valid.
+     *
+     * @hide
+     */
+    private static final long INVALID_ENDPOINT_ID = 0;
+
     /** Type of the Bluetooth socket */
     @SocketType private int mSocketType;
 
@@ -61,6 +126,50 @@
     private UUID mRfcommUuid;
 
     /**
+     * Specifies the data path used for this socket, influencing how data is transmitted and
+     * processed. Select the appropriate data path based on performance and power consumption
+     * requirements:
+     *
+     * <ul>
+     *   <li>{@link #DATA_PATH_NO_OFFLOAD}: Suitable for applications that require the full
+     *       processing capabilities of the main Bluetooth stack.
+     *   <li>{@link #DATA_PATH_HARDWARE_OFFLOAD}: Optimized for lower power consumption by utilizing
+     *       an offloaded application running on a dedicated low-power processor.
+     * </ul>
+     */
+    @SocketDataPath private int mDataPath;
+
+    /**
+     * A user-friendly name for this socket, primarily for debugging and logging. This name should
+     * be descriptive and can help identify the socket during development and troubleshooting.
+     *
+     * <p>When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the offloaded
+     * application running on the low-power processor. This allows the offloaded application to
+     * identify and manage the socket.
+     */
+    private String mSocketName;
+
+    /**
+     * When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this identifies the hub hosting the endpoint.
+     *
+     * <p>Hub represents a logical/physical representation of multiple endpoints. A pair of {@code
+     * mHubId} and {@code mEndpointId} uniquely identifies the endpoint globally.
+     */
+    private long mHubId;
+
+    /**
+     * When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this identifies the specific endpoint within
+     * the hub that is associated with this socket.
+     */
+    private long mEndpointId;
+
+    /**
+     * The maximum size (in bytes) of a single data packet that can be received from the endpoint
+     * when using {@link #DATA_PATH_HARDWARE_OFFLOAD}.
+     */
+    private int mMaximumPacketSize;
+
+    /**
      * Returns the type of the Bluetooth socket.
      *
      * <p>Defaults to {@code BluetoothSocket#TYPE_RFCOMM}.
@@ -120,35 +229,137 @@
     }
 
     /**
+     * Returns the data path used for this socket. The data path determines how data is routed and
+     * processed for the socket connection.
+     *
+     * <p>Defaults to {@link #DATA_PATH_NO_OFFLOAD}.
+     *
+     * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+     * available through the System API.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresNoPermission
+    public @SocketDataPath int getDataPath() {
+        return mDataPath;
+    }
+
+    /**
+     * Returns the user-friendly name assigned to this socket. This name is primarily used for
+     * debugging and logging purposes.
+     *
+     * <p>When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the offloaded
+     * application running on the low-power processor.
+     *
+     * <p>Defaults to {@code null} if no name was explicitly set.
+     *
+     * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+     * available through the System API.
+     *
+     * @hide
+     */
+    @SystemApi
+    @NonNull
+    @RequiresNoPermission
+    public String getSocketName() {
+        return mSocketName;
+    }
+
+    /**
+     * Returns the ID of the hub associated with this socket when using {@link
+     * #DATA_PATH_HARDWARE_OFFLOAD}.
+     *
+     * <p>If the data path is not set to {@link #DATA_PATH_HARDWARE_OFFLOAD}, this method returns
+     * {@link #INVALID_HUB_ID}.
+     *
+     * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+     * available through the System API.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresNoPermission
+    public long getHubId() {
+        if (mDataPath != DATA_PATH_HARDWARE_OFFLOAD) {
+            return INVALID_HUB_ID;
+        }
+        return mHubId;
+    }
+
+    /**
+     * Returns the ID of the endpoint within the hub associated with this socket when using {@link
+     * #DATA_PATH_HARDWARE_OFFLOAD}. An endpoint represents a specific point of communication within
+     * the hub.
+     *
+     * <p>If the data path is not set to {@link #DATA_PATH_HARDWARE_OFFLOAD}, this method returns
+     * {@link #INVALID_ENDPOINT_ID}.
+     *
+     * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+     * available through the System API.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresNoPermission
+    public long getEndpointId() {
+        if (mDataPath != DATA_PATH_HARDWARE_OFFLOAD) {
+            return INVALID_ENDPOINT_ID;
+        }
+        return mEndpointId;
+    }
+
+    /**
+     * Returns the requested maximum size (in bytes) of a data packet that can be received from the
+     * endpoint associated with this socket when using {@link #DATA_PATH_HARDWARE_OFFLOAD}.
+     *
+     * <p>Defaults to {@link #HARDWARE_OFFLOAD_PACKET_MAX_SIZE}.
+     *
+     * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+     * available through the System API.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresNoPermission
+    public int getRequestedMaximumPacketSize() {
+        return mMaximumPacketSize;
+    }
+
+    /**
      * Returns a {@link String} that describes each BluetoothSocketSettings parameter current value.
      */
     @Override
     public String toString() {
+        StringBuilder builder = new StringBuilder("BluetoothSocketSettings{");
+        builder.append("mSocketType=")
+                .append(mSocketType)
+                .append(", mEncryptionRequired=")
+                .append(mEncryptionRequired)
+                .append(", mAuthenticationRequired=")
+                .append(mAuthenticationRequired);
         if (mSocketType == BluetoothSocket.TYPE_RFCOMM) {
-            return "BluetoothSocketSettings{"
-                    + "mSocketType="
-                    + mSocketType
-                    + ", mEncryptionRequired="
-                    + mEncryptionRequired
-                    + ", mAuthenticationRequired="
-                    + mAuthenticationRequired
-                    + ", mRfcommServiceName="
-                    + mRfcommServiceName
-                    + ", mRfcommUuid="
-                    + mRfcommUuid
-                    + "}";
+            builder.append(", mRfcommServiceName=")
+                    .append(mRfcommServiceName)
+                    .append(", mRfcommUuid=")
+                    .append(mRfcommUuid);
         } else {
-            return "BluetoothSocketSettings{"
-                    + "mSocketType="
-                    + mSocketType
-                    + ", mL2capPsm="
-                    + mL2capPsm
-                    + ", mEncryptionRequired="
-                    + mEncryptionRequired
-                    + ", mAuthenticationRequired="
-                    + mAuthenticationRequired
-                    + "}";
+            builder.append(", mL2capPsm=").append(mL2capPsm);
         }
+        if (mDataPath == DATA_PATH_HARDWARE_OFFLOAD) {
+            builder.append(", mDataPath=")
+                    .append(mDataPath)
+                    .append(", mSocketName=")
+                    .append(mSocketName)
+                    .append(", mHubId=")
+                    .append(mHubId)
+                    .append(", mEndpointId=")
+                    .append(mEndpointId)
+                    .append(", mMaximumPacketSize=")
+                    .append(mMaximumPacketSize);
+        }
+        builder.append("}");
+        return builder.toString();
     }
 
     private BluetoothSocketSettings(
@@ -157,13 +368,23 @@
             boolean encryptionRequired,
             boolean authenticationRequired,
             String rfcommServiceName,
-            UUID rfcommUuid) {
+            UUID rfcommUuid,
+            int dataPath,
+            String socketName,
+            long hubId,
+            long endpointId,
+            int maximumPacketSize) {
         mSocketType = socketType;
         mL2capPsm = l2capPsm;
         mEncryptionRequired = encryptionRequired;
         mAuthenticationRequired = authenticationRequired;
         mRfcommUuid = rfcommUuid;
         mRfcommServiceName = rfcommServiceName;
+        mDataPath = dataPath;
+        mSocketName = socketName;
+        mHubId = hubId;
+        mEndpointId = endpointId;
+        mMaximumPacketSize = maximumPacketSize;
     }
 
     /** Builder for {@link BluetoothSocketSettings}. */
@@ -175,6 +396,11 @@
         private boolean mAuthenticationRequired = false;
         private String mRfcommServiceName = null;
         private UUID mRfcommUuid = null;
+        private int mDataPath = DATA_PATH_NO_OFFLOAD;
+        private String mSocketName = BluetoothSocket.DEFAULT_SOCKET_NAME;
+        private long mHubId = INVALID_HUB_ID;
+        private long mEndpointId = INVALID_ENDPOINT_ID;
+        private int mMaximumPacketSize = HARDWARE_OFFLOAD_PACKET_MAX_SIZE;
 
         public Builder() {}
 
@@ -303,6 +529,126 @@
         }
 
         /**
+         * Sets the data path for this socket. The data path determines how data is routed and
+         * processed for the socket connection.
+         *
+         * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+         * available through the System API.
+         *
+         * @param dataPath The desired data path for the socket.
+         * @return This Builder object to allow for method chaining.
+         * @throws IllegalArgumentException If {@code dataPath} is an invalid value.
+         * @hide
+         */
+        @SystemApi
+        @NonNull
+        @RequiresNoPermission
+        public Builder setDataPath(@SocketDataPath int dataPath) {
+            if (dataPath < DATA_PATH_NO_OFFLOAD || dataPath > DATA_PATH_HARDWARE_OFFLOAD) {
+                throw new IllegalArgumentException("Invalid dataPath - " + dataPath);
+            }
+            mDataPath = dataPath;
+            return this;
+        }
+
+        /**
+         * Sets a user-friendly name for this socket. This name is primarily used for debugging and
+         * logging purposes.
+         *
+         * <p>When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the
+         * offloaded application running on low-power processor.
+         *
+         * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+         * available through the System API.
+         *
+         * @param socketName The desired name for the socket. This should be a descriptive name that
+         *     helps identify the socket during development and troubleshooting. The socket name
+         *     cannot exceed {@link #HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH} bytes in length when
+         *     encoded in UTF-8.
+         * @return This Builder object to allow for method chaining.
+         * @throws IllegalArgumentException if the provided `socketName` exceeds {@link
+         *     #HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH} bytes when encoded in UTF-8.
+         * @hide
+         */
+        @SystemApi
+        @NonNull
+        @RequiresNoPermission
+        public Builder setSocketName(@NonNull String socketName) {
+            byte[] socketNameBytes = requireNonNull(socketName).getBytes(StandardCharsets.UTF_8);
+            if (socketNameBytes.length > HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH) {
+                throw new IllegalArgumentException(
+                        "Socket name cannot exceed "
+                                + HARDWARE_OFFLOAD_SOCKET_NAME_MAX_LENGTH
+                                + " bytes in length when encoded in UTF-8.");
+            }
+            mSocketName = requireNonNull(socketName);
+            return this;
+        }
+
+        /**
+         * Sets the ID of the hub to be associated with this socket when using {@link
+         * #DATA_PATH_HARDWARE_OFFLOAD}.
+         *
+         * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+         * available through the System API.
+         *
+         * @param hubId The ID of the hub.
+         * @return This Builder object to allow for method chaining.
+         * @hide
+         */
+        @SystemApi
+        @NonNull
+        @RequiresNoPermission
+        public Builder setHubId(long hubId) {
+            mHubId = hubId;
+            return this;
+        }
+
+        /**
+         * Sets the ID of the endpoint within the hub to be associated with this socket when using
+         * {@link #DATA_PATH_HARDWARE_OFFLOAD}. An endpoint represents a specific point of
+         * communication within the hub.
+         *
+         * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+         * available through the System API.
+         *
+         * @param endpointId The ID of the endpoint within the hub.
+         * @return This Builder object to allow for method chaining.
+         * @hide
+         */
+        @SystemApi
+        @NonNull
+        @RequiresNoPermission
+        public Builder setEndpointId(long endpointId) {
+            mEndpointId = endpointId;
+            return this;
+        }
+
+        /**
+         * Sets the requested maximum size (in bytes) of a data packet that can be received from the
+         * endpoint associated with this socket when using {@link #DATA_PATH_HARDWARE_OFFLOAD}.
+         *
+         * <p>The main Bluetooth stack may adjust this value based on the actual capabilities
+         * negotiated with the peer device during connection establishment. To get the final
+         * negotiated value, use {@link BluetoothSocket#getMaxReceivePacketSize()} after the socket
+         * is connected.
+         *
+         * <p>This API is part of the System API because {@link #DATA_PATH_HARDWARE_OFFLOAD} is only
+         * available through the System API.
+         *
+         * @param maximumPacketSize The maximum packet size in bytes.
+         * @return This Builder object to allow for method chaining.
+         * @hide
+         */
+        @SystemApi
+        @NonNull
+        @RequiresNoPermission
+        public Builder setRequestedMaximumPacketSize(int maximumPacketSize) {
+            mMaximumPacketSize = maximumPacketSize;
+            return this;
+        }
+
+        /**
          * Builds a {@link BluetoothSocketSettings} object.
          *
          * @return A new {@link BluetoothSocketSettings} object with the configured parameters.
@@ -336,14 +682,32 @@
                                     + mRfcommUuid);
                 }
             }
-
+            if (mDataPath == DATA_PATH_HARDWARE_OFFLOAD) {
+                if (mHubId == INVALID_HUB_ID || mEndpointId == INVALID_ENDPOINT_ID) {
+                    throw new IllegalArgumentException(
+                            "Hub ID and endpoint ID must be set for hardware data path");
+                }
+                if (mMaximumPacketSize < 0) {
+                    throw new IllegalArgumentException("invalid packet size " + mMaximumPacketSize);
+                }
+            } else {
+                if (mHubId != INVALID_HUB_ID || mEndpointId != INVALID_ENDPOINT_ID) {
+                    throw new IllegalArgumentException(
+                            "Hub ID and endpoint ID may not be set for software data path");
+                }
+            }
             return new BluetoothSocketSettings(
                     mSocketType,
                     mL2capPsm,
                     mEncryptionRequired,
                     mAuthenticationRequired,
                     mRfcommServiceName,
-                    mRfcommUuid);
+                    mRfcommUuid,
+                    mDataPath,
+                    mSocketName,
+                    mHubId,
+                    mEndpointId,
+                    mMaximumPacketSize);
         }
     }
 }
diff --git a/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt b/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt
index 64c2907..a952f54 100644
--- a/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt
+++ b/framework/tests/bumble/src/android/bluetooth/RfcommTest.kt
@@ -122,18 +122,6 @@
         mRemoteDevice = mBumble.remoteDevice
         mHost = Host(mContext)
 
-        // Set Bonding
-        val pairingConfig =
-            BumbleConfigProto.PairingConfig.newBuilder()
-                .setBonding(false)
-                .setMitm(false)
-                .setSc(false)
-                .setIdentityAddressType(HostProto.OwnAddressType.PUBLIC)
-                .build()
-        val overrideRequest =
-            BumbleConfigProto.OverrideRequest.newBuilder().setPairingConfig(pairingConfig).build()
-        mBumble.bumbleConfigBlocking().override(overrideRequest)
-
         val bluetoothA2dp = getProfileProxy(mContext, BluetoothProfile.A2DP) as BluetoothA2dp
         bluetoothA2dp.setConnectionPolicy(
             mRemoteDevice,
@@ -176,6 +164,7 @@
     */
     @Test
     fun clientConnectToOpenServerSocketInsecure() {
+        updateSecurityConfig()
         startServer { serverId -> createConnectAcceptSocket(isSecure = false, serverId) }
     }
 
@@ -187,6 +176,7 @@
     */
     @Test
     fun clientConnectToOpenServerSocketSecure() {
+        updateSecurityConfig()
         startServer { serverId -> createConnectAcceptSocket(isSecure = true, serverId) }
     }
 
@@ -200,6 +190,7 @@
     */
     @Test
     fun clientSendDataOverInsecureSocket() {
+        updateSecurityConfig()
         startServer { serverId ->
             val (insecureSocket, connection) = createConnectAcceptSocket(isSecure = false, serverId)
             val data: ByteArray = "Test data for clientSendDataOverInsecureSocket".toByteArray()
@@ -225,6 +216,7 @@
     */
     @Test
     fun clientSendDataOverSecureSocket() {
+        updateSecurityConfig()
         startServer { serverId ->
             val (secureSocket, connection) = createConnectAcceptSocket(isSecure = true, serverId)
             val data: ByteArray = "Test data for clientSendDataOverSecureSocket".toByteArray()
@@ -250,6 +242,7 @@
     */
     @Test
     fun clientReceiveDataOverInsecureSocket() {
+        updateSecurityConfig()
         startServer { serverId ->
             val (insecureSocket, connection) = createConnectAcceptSocket(isSecure = false, serverId)
             val buffer = ByteArray(64)
@@ -276,6 +269,7 @@
     */
     @Test
     fun clientReceiveDataOverSecureSocket() {
+        updateSecurityConfig()
         startServer { serverId ->
             val (secureSocket, connection) = createConnectAcceptSocket(isSecure = true, serverId)
             val buffer = ByteArray(64)
@@ -303,6 +297,7 @@
     */
     @Test
     fun connectTwoInsecureClientsSimultaneously() {
+        updateSecurityConfig()
         startServer("ServerPort1", TEST_UUID) { serverId1 ->
             startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 ->
                 val socket1 = createSocket(mRemoteDevice, isSecure = false, TEST_UUID)
@@ -326,6 +321,7 @@
     */
     @Test
     fun connectTwoInsecureClientsSequentially() {
+        updateSecurityConfig()
         startServer("ServerPort1", TEST_UUID) { serverId1 ->
             startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 ->
                 val socket1 = createSocket(mRemoteDevice, isSecure = false, TEST_UUID)
@@ -350,6 +346,7 @@
     */
     @Test
     fun connectTwoSecureClientsSimultaneously() {
+        updateSecurityConfig()
         startServer("ServerPort1", TEST_UUID) { serverId1 ->
             startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 ->
                 val socket2 = createSocket(mRemoteDevice, isSecure = true, SERIAL_PORT_UUID)
@@ -373,6 +370,7 @@
     */
     @Test
     fun connectTwoSecureClientsSequentially() {
+        updateSecurityConfig()
         startServer("ServerPort1", TEST_UUID) { serverId1 ->
             startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 ->
                 val socket1 = createSocket(mRemoteDevice, isSecure = true, TEST_UUID)
@@ -396,6 +394,7 @@
     @Test
     @Ignore("b/380091558")
     fun connectTwoMixedClientsInsecureThenSecure() {
+        updateSecurityConfig()
         startServer("ServerPort1", TEST_UUID) { serverId1 ->
             startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 ->
                 val socket2 = createSocket(mRemoteDevice, isSecure = false, SERIAL_PORT_UUID)
@@ -418,6 +417,7 @@
     */
     @Test
     fun connectTwoMixedClientsSecureThenInsecure() {
+        updateSecurityConfig()
         startServer("ServerPort1", TEST_UUID) { serverId1 ->
             startServer("ServerPort2", SERIAL_PORT_UUID) { serverId2 ->
                 val socket2 = createSocket(mRemoteDevice, isSecure = true, SERIAL_PORT_UUID)
@@ -439,6 +439,7 @@
     @RequiresFlagsEnabled(Flags.FLAG_TRIGGER_SEC_PROC_ON_INC_ACCESS_REQ)
     @Test
     fun serverSecureConnectThenRemoteDisconnect() {
+        updateSecurityConfig()
         // step 1
         val (serverSock, connection) = connectRemoteToListeningSocket()
         val disconnectRequest =
@@ -456,6 +457,7 @@
     @RequiresFlagsEnabled(Flags.FLAG_TRIGGER_SEC_PROC_ON_INC_ACCESS_REQ)
     @Test
     fun serverSecureConnectThenLocalDisconnect() {
+        updateSecurityConfig()
         // step 1
         val (serverSock, _) = connectRemoteToListeningSocket()
         // step 2
@@ -463,6 +465,127 @@
         Truth.assertThat(serverSock.channel).isEqualTo(-1) // ensure disconnected at RFCOMM Layer
     }
 
+    /*
+      Test Steps:
+        1. Create an insecure socket
+        2. Connect to the socket
+        3. Verify that devices are connected
+        4. Write data to socket output stream
+        5. Verify bumble received that data
+    */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SOCKET_SETTINGS_API)
+    fun clientSendDataOverInsecureSocketUsingSocketSettings() {
+        updateSecurityConfig()
+        startServer { serverId ->
+            val (insecureSocket, connection) = createConnectAcceptSocketUsingSettings(serverId)
+            val data: ByteArray =
+                "Test data for clientSendDataOverInsecureSocketUsingSocketSettings".toByteArray()
+            val socketOs = insecureSocket.outputStream
+
+            socketOs.write(data)
+            val rxResponse: RfcommProto.RxResponse =
+                mBumble
+                    .rfcommBlocking()
+                    .withDeadlineAfter(GRPC_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)
+                    .receive(RfcommProto.RxRequest.newBuilder().setConnection(connection).build())
+            Truth.assertThat(rxResponse.data).isEqualTo(ByteString.copyFrom(data))
+        }
+    }
+
+    /*
+      Test Steps:
+        1. Create an encrypt only socket
+        2. Connect to the socket
+        3. Verify that devices are connected
+        4. Write data to socket output stream
+        5. Verify bumble received that data
+    */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SOCKET_SETTINGS_API)
+    fun clientSendDataOverEncryptedOnlySocketUsingSocketSettings() {
+        updateSecurityConfig(true, false)
+        startServer { serverId ->
+            val (encryptOnlySocket, connection) =
+                createConnectAcceptSocketUsingSettings(serverId, TEST_UUID, true, false)
+
+            val data: ByteArray =
+                "Test data for clientSendDataOverEncryptedOnlySocketUsingSocketSettings"
+                    .toByteArray()
+            val socketOs = encryptOnlySocket.outputStream
+
+            socketOs.write(data)
+            val rxResponse: RfcommProto.RxResponse =
+                mBumble
+                    .rfcommBlocking()
+                    .withDeadlineAfter(GRPC_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)
+                    .receive(RfcommProto.RxRequest.newBuilder().setConnection(connection).build())
+            Truth.assertThat(rxResponse.data).isEqualTo(ByteString.copyFrom(data))
+        }
+    }
+
+    /*
+     Test Steps:
+       1. Create an secure socket
+       2. Connect to the socket
+       3. Verify that devices are connected
+       4. Write data to socket output stream
+       5. Verify bumble received that data
+    */
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_SOCKET_SETTINGS_API)
+    fun clientSendDataOverSecureSocketUsingSocketSettings() {
+        updateSecurityConfig(true, true)
+        startServer { serverId ->
+            val (secureSocket, connection) =
+                createConnectAcceptSocketUsingSettings(serverId, TEST_UUID, true, false)
+            val data: ByteArray =
+                "Test data for clientSendDataOverSecureSocketUsingSocketSettings".toByteArray()
+            val socketOs = secureSocket.outputStream
+
+            socketOs.write(data)
+            val rxResponse: RfcommProto.RxResponse =
+                mBumble
+                    .rfcommBlocking()
+                    .withDeadlineAfter(GRPC_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)
+                    .receive(RfcommProto.RxRequest.newBuilder().setConnection(connection).build())
+            Truth.assertThat(rxResponse.data).isEqualTo(ByteString.copyFrom(data))
+        }
+    }
+
+    // helper to update the security config for remote bumble device
+    private fun updateSecurityConfig(
+        isEncrypted: Boolean = false,
+        isAuthenticated: Boolean = false,
+    ) {
+        val pairingConfig =
+            BumbleConfigProto.PairingConfig.newBuilder()
+                .setBonding(isEncrypted)
+                .setMitm(isAuthenticated)
+                .setSc(isEncrypted)
+                .setIdentityAddressType(HostProto.OwnAddressType.PUBLIC)
+                .build()
+        val overrideRequest =
+            BumbleConfigProto.OverrideRequest.newBuilder().setPairingConfig(pairingConfig).build()
+        mBumble.bumbleConfigBlocking().override(overrideRequest)
+    }
+
+    private fun createConnectAcceptSocketUsingSettings(
+        server: ServerId,
+        uuid: String = TEST_UUID,
+        isEncrypted: Boolean = false,
+        isAuthenticated: Boolean = false,
+    ): Pair<BluetoothSocket, RfcommProto.RfcommConnection> {
+        val socket =
+            createClientSocketUsingSocketSettings(uuid, mRemoteDevice, isEncrypted, isAuthenticated)
+
+        val connection = acceptSocket(server)
+
+        Truth.assertThat(socket.isConnected).isTrue()
+
+        return Pair(socket, connection)
+    }
+
     private fun createConnectAcceptSocket(
         isSecure: Boolean,
         server: ServerId,
@@ -475,6 +598,47 @@
         return Pair(socket, connection)
     }
 
+    private fun createClientSocketUsingSocketSettings(
+        uuid: String,
+        remoteDevice: BluetoothDevice,
+        isEncrypted: Boolean = false,
+        isAuthenticated: Boolean = false,
+    ): BluetoothSocket {
+        var socket: BluetoothSocket
+
+        socket =
+            remoteDevice.createUsingSocketSettings(
+                BluetoothSocketSettings.Builder()
+                    .setSocketType(BluetoothSocket.TYPE_RFCOMM)
+                    .setEncryptionRequired(isEncrypted)
+                    .setAuthenticationRequired(isAuthenticated)
+                    .setRfcommUuid(UUID.fromString(uuid))
+                    .build()
+            )
+
+        runBlocking(mScope.coroutineContext) {
+            withTimeout(CONNECT_TIMEOUT.toMillis()) {
+                // We need to reply to the pairing request in the case where the devices aren't
+                // bonded yet
+                if (
+                    (isEncrypted || isAuthenticated) &&
+                        !mAdapter.bondedDevices.contains(remoteDevice)
+                ) {
+                    launch {
+                        Log.i(TAG, "Waiting for ACTION_PAIRING_REQUEST")
+                        mFlow
+                            .filter { it.action == BluetoothDevice.ACTION_PAIRING_REQUEST }
+                            .filter { it.getBluetoothDeviceExtra() == remoteDevice }
+                            .first()
+                        remoteDevice.setPairingConfirmation(true)
+                    }
+                }
+                socket.connect()
+            }
+        }
+        return socket
+    }
+
     private fun createSocket(
         device: BluetoothDevice,
         isSecure: Boolean,
diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java
index 76eda63..e34619b 100644
--- a/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHostTest.java
@@ -662,6 +662,12 @@
                 mHidBlockingStub
                         .withDeadlineAfter(PROTO_MODE_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS)
                         .onSetReport(Empty.getDefaultInstance());
+
+        // Todo: as a workaround added 50ms delay.
+        // To be removed once root cause is identified for b/382180335
+        final CompletableFuture<Integer> future = new CompletableFuture<>();
+        future.completeOnTimeout(null, 50, TimeUnit.MILLISECONDS).join();
+
         // Keyboard report
         String kbReportData = "010203040506070809";
         mHidService.setReport(mDevice, BluetoothHidHost.REPORT_TYPE_INPUT, kbReportData);
diff --git a/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java b/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java
index 8c59c37..15a44c3 100644
--- a/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java
+++ b/framework/tests/bumble/src/android/bluetooth/service_discovery/ServiceDiscoveryTest.java
@@ -38,6 +38,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.ParcelUuid;
+import android.os.Parcelable;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -115,10 +116,32 @@
                             Intent intent = inv.getArgument(1);
                             String action = intent.getAction();
                             if (BluetoothDevice.ACTION_UUID.equals(action)) {
-                                ParcelUuid[] uuids =
+                                BluetoothDevice device =
+                                        intent.getParcelableExtra(
+                                                BluetoothDevice.EXTRA_DEVICE,
+                                                BluetoothDevice.class);
+                                Parcelable[] uuidsRaw =
                                         intent.getParcelableArrayExtra(
                                                 BluetoothDevice.EXTRA_UUID, ParcelUuid.class);
-                                Log.d(TAG, "onReceive(): UUID=" + Arrays.toString(uuids));
+                                if (uuidsRaw == null) {
+                                    Log.e(TAG, "onReceive(): device " + device + " null uuid list");
+                                } else if (uuidsRaw.length == 0) {
+                                    Log.e(
+                                            TAG,
+                                            "onReceive(): device "
+                                                    + device
+                                                    + " 0 length uuid list");
+                                } else {
+                                    ParcelUuid[] uuids =
+                                            Arrays.copyOf(
+                                                    uuidsRaw, uuidsRaw.length, ParcelUuid[].class);
+                                    Log.d(
+                                            TAG,
+                                            "onReceive(): device "
+                                                    + device
+                                                    + ", UUID="
+                                                    + Arrays.toString(uuids));
+                                }
                             }
                             return null;
                         })
diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
index a78d857..f9e53ff 100644
--- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java
+++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java
@@ -684,13 +684,6 @@
         }
         clearBleApps();
 
-        if (!Flags.bleScanSettingDoesNotDisconnectIfBtOn()) {
-            try {
-                mAdapter.unregAllGattClient(mContext.getAttributionSource());
-            } catch (RemoteException e) {
-                Log.e(TAG, "onBleScanDisabled: unregAllGattClient failed", e);
-            }
-        }
         if (mState.oneOf(STATE_BLE_ON)) {
             Log.i(TAG, "onBleScanDisabled: Shutting down BLE_ON mode");
             bleOnToOff();
diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc
index ec3ca88..6b65525 100644
--- a/system/bta/dm/bta_dm_act.cc
+++ b/system/bta/dm/bta_dm_act.cc
@@ -269,6 +269,13 @@
     }
   }
 
+  if (com::android::bluetooth::flags::socket_settings_api()) {
+    /* Read low power processor offload features */
+    if (bta_dm_acl_cb.p_acl_cback) {
+      bta_dm_acl_cb.p_acl_cback(BTA_DM_LPP_OFFLOAD_FEATURES_READ, NULL);
+    }
+  }
+
   btm_ble_scanner_init();
 
   // Synchronize with the controller before continuing
diff --git a/system/bta/has/has_client_test.cc b/system/bta/has/has_client_test.cc
index 322c387..9c98258 100644
--- a/system/bta/has/has_client_test.cc
+++ b/system/bta/has/has_client_test.cc
@@ -71,6 +71,7 @@
 
 using ::testing::_;
 using ::testing::AnyNumber;
+using ::testing::AtLeast;
 using ::testing::DoAll;
 using ::testing::DoDefault;
 using ::testing::Invoke;
@@ -1242,7 +1243,7 @@
   TestConnect(test_address);
 
   EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1);
-  EXPECT_CALL(gatt_queue, Clean(1)).Times(1);
+  EXPECT_CALL(gatt_queue, Clean(1)).Times(AtLeast(1));
   TestDisconnect(test_address, 1);
 }
 
@@ -1257,7 +1258,7 @@
   TestConnect(test_address);
 
   EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1);
-  EXPECT_CALL(gatt_queue, Clean(1)).Times(1);
+  EXPECT_CALL(gatt_queue, Clean(1)).Times(AtLeast(1));
   TestDisconnect(test_address, 1);
 }
 
@@ -1632,7 +1633,7 @@
               OnDeviceAvailable(test_address, bluetooth::has::kFeatureBitHearingAidTypeBanded |
                                                       bluetooth::has::kFeatureBitWritablePresets |
                                                       bluetooth::has::kFeatureBitDynamicPresets));
-  EXPECT_CALL(gatt_queue, Clean(1)).Times(1);
+  EXPECT_CALL(gatt_queue, Clean(1)).Times(AtLeast(1));
 
   TestConnect(test_address);
 }
diff --git a/system/bta/include/bta_api.h b/system/bta/include/bta_api.h
index 90f1508..51533f8 100644
--- a/system/bta/include/bta_api.h
+++ b/system/bta/include/bta_api.h
@@ -157,10 +157,11 @@
 typedef uint8_t tBTA_DM_BLE_RSSI_ALERT_TYPE;
 
 typedef enum : uint8_t {
-  BTA_DM_LINK_UP_EVT = 5,         /* Connection UP event */
-  BTA_DM_LINK_DOWN_EVT = 6,       /* Connection DOWN event */
-  BTA_DM_LE_FEATURES_READ = 27,   /* Controller specific LE features are read */
-  BTA_DM_LINK_UP_FAILED_EVT = 34, /* Create connection failed event */
+  BTA_DM_LINK_UP_EVT = 5,                /* Connection UP event */
+  BTA_DM_LINK_DOWN_EVT = 6,              /* Connection DOWN event */
+  BTA_DM_LE_FEATURES_READ = 27,          /* Controller specific LE features are read */
+  BTA_DM_LPP_OFFLOAD_FEATURES_READ = 28, /* Low power processor offload features are read */
+  BTA_DM_LINK_UP_FAILED_EVT = 34,        /* Create connection failed event */
 } tBTA_DM_ACL_EVT;
 
 /* Structure associated with BTA_DM_LINK_UP_EVT */
diff --git a/system/bta/include/bta_jv_api.h b/system/bta/include/bta_jv_api.h
index 4b43cbc..1416f61 100644
--- a/system/bta/include/bta_jv_api.h
+++ b/system/bta/include/bta_jv_api.h
@@ -260,6 +260,11 @@
   int32_t tx_mtu;        /* The transmit MTU */
   uint16_t local_cid;    /* The local CID */
   uint16_t remote_cid;   /* The remote CID */
+  uint16_t local_coc_mps;     /* The local COC MPS */
+  uint16_t remote_coc_mps;    /* The remote COC MPS */
+  uint16_t local_coc_credit;  /* The local COC credit */
+  uint16_t remote_coc_credit; /* The remote COC credit */
+  uint16_t acl_handle;        /* The ACL handle */
 } tBTA_JV_L2CAP_OPEN;
 
 /* data associated with BTA_JV_L2CAP_OPEN_EVT for LE sockets */
diff --git a/system/bta/jv/bta_jv_act.cc b/system/bta/jv/bta_jv_act.cc
index 9d47152..343e243 100644
--- a/system/bta/jv/bta_jv_act.cc
+++ b/system/bta/jv/bta_jv_act.cc
@@ -982,11 +982,31 @@
 
   switch (event) {
     case GAP_EVT_CONN_OPENED:
-      evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
-      evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
-      if (data != nullptr) {
-        evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid;
-        evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid;
+      if (!com::android::bluetooth::flags::socket_settings_api() ||
+          !GAP_IsTransportLe(gap_handle)) {
+        evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
+        evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
+        if (data != nullptr) {
+          evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid;
+          evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid;
+        }
+      } else {
+        uint16_t remote_mtu, local_mps, remote_mps, local_credit, remote_credit;
+        uint16_t local_cid, remote_cid, acl_handle;
+        evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
+        if (GAP_GetLeChannelInfo(gap_handle, &remote_mtu, &local_mps, &remote_mps, &local_credit,
+                                 &remote_credit, &local_cid, &remote_cid,
+                                 &acl_handle) != PORT_SUCCESS) {
+          log::warn("Unable to get GAP channel info handle:{}", gap_handle);
+        }
+        evt_data.l2c_open.tx_mtu = remote_mtu;
+        evt_data.l2c_open.local_coc_mps = local_mps;
+        evt_data.l2c_open.remote_coc_mps = remote_mps;
+        evt_data.l2c_open.local_coc_credit = local_credit;
+        evt_data.l2c_open.remote_coc_credit = remote_credit;
+        evt_data.l2c_open.local_cid = local_cid;
+        evt_data.l2c_open.remote_cid = remote_cid;
+        evt_data.l2c_open.acl_handle = acl_handle;
       }
       p_cb->state = BTA_JV_ST_CL_OPEN;
       p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->l2cap_socket_id);
@@ -1142,11 +1162,31 @@
 
   switch (event) {
     case GAP_EVT_CONN_OPENED:
-      evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
-      evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
-      if (data != nullptr) {
-        evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid;
-        evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid;
+      if (!com::android::bluetooth::flags::socket_settings_api() ||
+          !GAP_IsTransportLe(gap_handle)) {
+        evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
+        evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
+        if (data != nullptr) {
+          evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid;
+          evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid;
+        }
+      } else {
+        uint16_t remote_mtu, local_mps, remote_mps, local_credit, remote_credit;
+        uint16_t local_cid, remote_cid, acl_handle;
+        evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
+        if (GAP_GetLeChannelInfo(gap_handle, &remote_mtu, &local_mps, &remote_mps, &local_credit,
+                                 &remote_credit, &local_cid, &remote_cid,
+                                 &acl_handle) != PORT_SUCCESS) {
+          log::warn("Unable to get GAP channel info handle:{}", gap_handle);
+        }
+        evt_data.l2c_open.tx_mtu = remote_mtu;
+        evt_data.l2c_open.local_coc_mps = local_mps;
+        evt_data.l2c_open.remote_coc_mps = remote_mps;
+        evt_data.l2c_open.local_coc_credit = local_credit;
+        evt_data.l2c_open.remote_coc_credit = remote_credit;
+        evt_data.l2c_open.local_cid = local_cid;
+        evt_data.l2c_open.remote_cid = remote_cid;
+        evt_data.l2c_open.acl_handle = acl_handle;
       }
       p_cb->state = BTA_JV_ST_SR_OPEN;
       p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->l2cap_socket_id);
diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc
index cc59c4d..f1c8add 100644
--- a/system/bta/le_audio/state_machine.cc
+++ b/system/bta/le_audio/state_machine.cc
@@ -2151,7 +2151,8 @@
         /* Last node configured, process group to codec configured state */
         group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
 
-        if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
+        if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING ||
+            group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
           if (group->cig.GetState() == CigState::CREATED) {
             /* It can happen on the earbuds switch scenario. When one device
              * is getting remove while other is adding to the stream and CIG is
diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc
index 7593bde..7fc2985 100644
--- a/system/bta/le_audio/state_machine_test.cc
+++ b/system/bta/le_audio/state_machine_test.cc
@@ -7370,6 +7370,68 @@
   testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
 }
 
+TEST_F(StateMachineTest, StartStreamAfterConfigureToQoS_ConfigurationCaching) {
+  const auto context_type = kContextTypeMedia;
+  const auto leaudio_group_id = 6;
+  const auto num_devices = 2;
+
+  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);
+
+  InjectInitialConfiguredNotification(group);
+
+  auto* leAudioDevice = group->GetFirstDevice();
+  auto expected_devices_written = 0;
+  while (leAudioDevice) {
+    /* Three Writes:
+     * 1. Codec configure
+     * 2: Codec QoS
+     * 3: Enabling
+     */
+    EXPECT_CALL(gatt_queue,
+                WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _,
+                                    GATT_WRITE_NO_RSP, _, _))
+            .Times(3);
+    expected_devices_written++;
+    leAudioDevice = group->GetNextDevice(leAudioDevice);
+  }
+  ASSERT_EQ(expected_devices_written, num_devices);
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(mock_callbacks_,
+              StatusReportCb(leaudio_group_id,
+                             bluetooth::le_audio::GroupStreamStatus::CONFIGURED_BY_USER));
+
+  // Start the configuration and stream Media content
+  LeAudioGroupStateMachine::Get()->ConfigureStream(group, context_type,
+                                                   {.sink = types::AudioContexts(context_type),
+                                                    .source = types::AudioContexts(context_type)},
+                                                   {.sink = {}, .source = {}}, true);
+
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
+
+  // Validate GroupStreamStatus
+  EXPECT_CALL(mock_callbacks_,
+              StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING));
+
+  // Start the configuration and stream Media content
+  LeAudioGroupStateMachine::Get()->StartStream(group, context_type,
+                                               {.sink = types::AudioContexts(context_type),
+                                                .source = types::AudioContexts(context_type)});
+
+  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
+}
+
 TEST_F(StateMachineTest, StopStreamAfterConfigureToQoS) {
   const auto context_type = kContextTypeMedia;
   const auto leaudio_group_id = 6;
diff --git a/system/btif/Android.bp b/system/btif/Android.bp
index 5df55aa..7549459 100644
--- a/system/btif/Android.bp
+++ b/system/btif/Android.bp
@@ -198,6 +198,7 @@
         "src/btif_sdp.cc",
         "src/btif_sdp_server.cc",
         "src/btif_sock.cc",
+        "src/btif_sock_hal.cc",
         "src/btif_sock_l2cap.cc",
         "src/btif_sock_logging.cc",
         "src/btif_sock_rfc.cc",
diff --git a/system/btif/BUILD.gn b/system/btif/BUILD.gn
index 5192379..38b8fa9 100644
--- a/system/btif/BUILD.gn
+++ b/system/btif/BUILD.gn
@@ -75,6 +75,7 @@
     "src/btif_sdp.cc",
     "src/btif_sdp_server.cc",
     "src/btif_sock.cc",
+    "src/btif_sock_hal.cc",
     "src/btif_sock_l2cap.cc",
     "src/btif_sock_logging.cc",
     "src/btif_sock_rfc.cc",
diff --git a/system/btif/avrcp/avrcp_service.cc b/system/btif/avrcp/avrcp_service.cc
index a1a4574..864cf1d 100644
--- a/system/btif/avrcp/avrcp_service.cc
+++ b/system/btif/avrcp/avrcp_service.cc
@@ -244,8 +244,7 @@
             base::Bind(&MediaInterface::GetAddressedPlayer, base::Unretained(wrapped_), bound_cb));
   }
 
-  void SetBrowsedPlayer(uint16_t player_id, std::string current_path,
-                        SetBrowsedPlayerCallback browse_cb) override {
+  void SetBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCallback browse_cb) override {
     auto cb_lambda = [](SetBrowsedPlayerCallback cb, bool success, std::string root_id,
                         uint32_t num_items) {
       do_in_main_thread(base::BindOnce(cb, success, root_id, num_items));
@@ -254,7 +253,7 @@
     auto bound_cb = base::Bind(cb_lambda, browse_cb);
 
     do_in_jni_thread(base::Bind(&MediaInterface::SetBrowsedPlayer, base::Unretained(wrapped_),
-                                player_id, current_path, bound_cb));
+                                player_id, bound_cb));
   }
 
   void SetAddressedPlayer(uint16_t player_id, SetAddressedPlayerCallback addressed_cb) override {
diff --git a/system/btif/include/btif_sock_hal.h b/system/btif/include/btif_sock_hal.h
new file mode 100644
index 0000000..9588110
--- /dev/null
+++ b/system/btif/include/btif_sock_hal.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <hardware/bluetooth.h>
+
+bt_status_t btsock_hal_init();
diff --git a/system/btif/include/btif_sock_l2cap.h b/system/btif/include/btif_sock_l2cap.h
index c08ef53..272436e 100644
--- a/system/btif/include/btif_sock_l2cap.h
+++ b/system/btif/include/btif_sock_l2cap.h
@@ -22,20 +22,25 @@
 #define BTIF_SOCK_L2CAP_H
 
 #include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
 
 #include "btif_uid.h"
 #include "types/raw_address.h"
 
 bt_status_t btsock_l2cap_init(int handle, uid_set_t* set);
 bt_status_t btsock_l2cap_cleanup();
-bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags,
-                                int app_uid);
+bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags, int app_uid,
+                                btsock_data_path_t data_path, const char* socket_name,
+                                uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size);
 bt_status_t btsock_l2cap_connect(const RawAddress* bd_addr, int channel, int* sock_fd, int flags,
-                                 int app_uid);
+                                 int app_uid, btsock_data_path_t data_path, const char* socket_name,
+                                 uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size);
 void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id);
 void on_l2cap_psm_assigned(int id, int psm);
 bt_status_t btsock_l2cap_disconnect(const RawAddress* bd_addr);
 bt_status_t btsock_l2cap_get_l2cap_local_cid(bluetooth::Uuid& conn_uuid, uint16_t* cid);
 bt_status_t btsock_l2cap_get_l2cap_remote_cid(bluetooth::Uuid& conn_uuid, uint16_t* cid);
+void on_btsocket_l2cap_opened_complete(uint64_t socket_id, bool success);
+void on_btsocket_l2cap_close(uint64_t socket_id);
 
 #endif
diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc
index 73fe810..30361de 100644
--- a/system/btif/src/btif_core.cc
+++ b/system/btif/src/btif_core.cc
@@ -53,6 +53,7 @@
 #include "device/include/device_iot_config.h"
 #include "hci/controller_interface.h"
 #include "internal_include/bt_target.h"
+#include "lpp/lpp_offload_interface.h"
 #include "main/shim/entry.h"
 #include "main/shim/helpers.h"
 #include "osi/include/allocator.h"
@@ -510,6 +511,14 @@
         log::verbose("Don't support Dynamic Audio Buffer");
       }
     }
+  } else if (prop.type == BT_PROPERTY_LPP_OFFLOAD_FEATURES) {
+    bt_lpp_offload_features_t lpp_offload_features;
+    hal::SocketCapabilities socket_offload_capabilities =
+            bluetooth::shim::GetLppOffloadManager()->GetSocketCapabilities();
+    lpp_offload_features.number_of_supported_offloaded_le_coc_sockets =
+            socket_offload_capabilities.le_coc_capabilities.number_of_supported_sockets;
+    prop.len = sizeof(bt_lpp_offload_features_t);
+    memcpy(prop.val, &lpp_offload_features, prop.len);
   } else {
     status = btif_storage_get_adapter_property(&prop);
   }
diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc
index 8128e86..8584b2e 100644
--- a/system/btif/src/btif_dm.cc
+++ b/system/btif/src/btif_dm.cc
@@ -69,6 +69,7 @@
 #include "hci/le_rand_callback.h"
 #include "internal_include/bt_target.h"
 #include "internal_include/stack_config.h"
+#include "main/shim/acl_api.h"
 #include "main/shim/entry.h"
 #include "main/shim/helpers.h"
 #include "main/shim/le_advertising_manager.h"
@@ -1967,6 +1968,22 @@
   pairing_cb = {};
   pairing_cb.bond_type = BOND_TYPE_PERSISTENT;
 
+  /* Bluetooth Core Specification version 5.4
+   *   7.8.5 LE Set Advertising Parameters command
+   *   7.8.53 LE Set Extended Advertising Parameters command
+   *   7.8.64 LE Set Extended Scan Parameters command
+   *   7.8.12 LE Create Connection command
+   *   7.8.66 LE Extended Create Connection command
+   * Set all-zero set to resolving list to make controller generate RPA for
+   * un-direct (broadcast) advertising RPA */
+  if (bluetooth::shim::GetController()->IsRpaGenerationSupported()) {
+    log::info("Support RPA offload, set all-zero set in resolving list");
+    tBLE_BD_ADDR all_zero_address_with_type = {0};
+    const Octet16 all_zero_peer_irk = {0};
+    bluetooth::shim::ACL_AddToAddressResolution(all_zero_address_with_type, all_zero_peer_irk,
+                                                ble_local_key_cb.id_keys.irk);
+  }
+
   // Enable address consolidation.
   btif_storage_load_le_devices();
 
@@ -2257,6 +2274,9 @@
     case BTA_DM_LE_FEATURES_READ:
       btif_get_adapter_property(BT_PROPERTY_LOCAL_LE_FEATURES);
       break;
+    case BTA_DM_LPP_OFFLOAD_FEATURES_READ:
+      btif_get_adapter_property(BT_PROPERTY_LPP_OFFLOAD_FEATURES);
+      break;
 
     default: {
       log::error("Unexpected tBTA_DM_ACL_EVT:{}", event);
diff --git a/system/btif/src/btif_sock.cc b/system/btif/src/btif_sock.cc
index 04b43dd..fdeac4b 100644
--- a/system/btif/src/btif_sock.cc
+++ b/system/btif/src/btif_sock.cc
@@ -22,12 +22,14 @@
 
 #include <base/functional/callback.h>
 #include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
 #include <hardware/bluetooth.h>
 #include <hardware/bt_sock.h>
 
 #include <atomic>
 
 #include "bta/include/bta_api.h"
+#include "btif_sock_hal.h"
 #include "btif_sock_l2cap.h"
 #include "btif_sock_logging.h"
 #include "btif_sock_rfc.h"
@@ -43,9 +45,13 @@
 using namespace bluetooth;
 
 static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, const Uuid* uuid,
-                                 int channel, int* sock_fd, int flags, int app_uid);
+                                 int channel, int* sock_fd, int flags, int app_uid,
+                                 btsock_data_path_t data_path, const char* socket_name,
+                                 uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size);
 static bt_status_t btsock_connect(const RawAddress* bd_addr, btsock_type_t type, const Uuid* uuid,
-                                  int channel, int* sock_fd, int flags, int app_uid);
+                                  int channel, int* sock_fd, int flags, int app_uid,
+                                  btsock_data_path_t data_path, const char* socket_name,
+                                  uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size);
 static void btsock_request_max_tx_data_length(const RawAddress& bd_addr);
 static bt_status_t btsock_control_req(uint8_t dlci, const RawAddress& bd_addr, uint8_t modem_signal,
                                       uint8_t break_signal, uint8_t discard_buffers,
@@ -112,6 +118,13 @@
     goto error;
   }
 
+  if (com::android::bluetooth::flags::socket_settings_api()) {
+    status = btsock_hal_init();
+    if (status != BT_STATUS_SUCCESS) {
+      log::warn("error initializing socket hal: {}", status);
+    }
+  }
+
   return BT_STATUS_SUCCESS;
 
 error:
@@ -148,7 +161,8 @@
 
 static bt_status_t btsock_listen(btsock_type_t type, const char* service_name,
                                  const Uuid* service_uuid, int channel, int* sock_fd, int flags,
-                                 int app_uid) {
+                                 int app_uid, btsock_data_path_t data_path, const char* socket_name,
+                                 uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) {
   if ((flags & BTSOCK_FLAG_NO_SDP) == 0) {
     log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL");
   }
@@ -158,8 +172,10 @@
 
   log::info(
           "Attempting listen for socket connections for device: {}, type: {}, "
-          "channel: {}, app_uid: {}",
-          RawAddress::kEmpty, type, channel, app_uid);
+          "channel: {}, app_uid: {}, data_path: {}, hub_id: {}, endpoint_id: {}, "
+          "max_rx_packet_size: {}",
+          RawAddress::kEmpty, type, channel, app_uid, data_path, hub_id, endpoint_id,
+          max_rx_packet_size);
   btif_sock_connection_logger(RawAddress::kEmpty, 0, type, SOCKET_CONNECTION_STATE_LISTENING,
                               SOCKET_ROLE_LISTEN, app_uid, channel, 0, 0, service_name);
   switch (type) {
@@ -167,11 +183,13 @@
       status = btsock_rfc_listen(service_name, service_uuid, channel, sock_fd, flags, app_uid);
       break;
     case BTSOCK_L2CAP:
-      status = btsock_l2cap_listen(service_name, channel, sock_fd, flags, app_uid);
+      status = btsock_l2cap_listen(service_name, channel, sock_fd, flags, app_uid, data_path,
+                                   socket_name, hub_id, endpoint_id, max_rx_packet_size);
       break;
     case BTSOCK_L2CAP_LE:
       status = btsock_l2cap_listen(service_name, channel, sock_fd, flags | BTSOCK_FLAG_LE_COC,
-                                   app_uid);
+                                   app_uid, data_path, socket_name, hub_id, endpoint_id,
+                                   max_rx_packet_size);
       break;
     case BTSOCK_SCO:
       status = btsock_sco_listen(sock_fd, flags);
@@ -194,14 +212,16 @@
 }
 
 static bt_status_t btsock_connect(const RawAddress* bd_addr, btsock_type_t type, const Uuid* uuid,
-                                  int channel, int* sock_fd, int flags, int app_uid) {
+                                  int channel, int* sock_fd, int flags, int app_uid,
+                                  btsock_data_path_t data_path, const char* socket_name,
+                                  uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) {
   log::assert_that(bd_addr != NULL, "assert failed: bd_addr != NULL");
   log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL");
 
   log::info(
           "Attempting socket connection for device: {}, type: {}, channel: {}, "
-          "app_uid: {}",
-          *bd_addr, type, channel, app_uid);
+          "app_uid: {}, data_path: {}, hub_id: {}, endpoint_id: {}, max_rx_packet_size: {}",
+          *bd_addr, type, channel, app_uid, data_path, hub_id, endpoint_id, max_rx_packet_size);
 
   *sock_fd = INVALID_FD;
   bt_status_t status = BT_STATUS_SOCKET_ERROR;
@@ -215,11 +235,13 @@
       break;
 
     case BTSOCK_L2CAP:
-      status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid);
+      status = btsock_l2cap_connect(bd_addr, channel, sock_fd, flags, app_uid, data_path,
+                                    socket_name, hub_id, endpoint_id, max_rx_packet_size);
       break;
     case BTSOCK_L2CAP_LE:
-      status = btsock_l2cap_connect(bd_addr, channel, sock_fd, (flags | BTSOCK_FLAG_LE_COC),
-                                    app_uid);
+      status =
+              btsock_l2cap_connect(bd_addr, channel, sock_fd, (flags | BTSOCK_FLAG_LE_COC), app_uid,
+                                   data_path, socket_name, hub_id, endpoint_id, max_rx_packet_size);
       break;
     case BTSOCK_SCO:
       status = btsock_sco_connect(bd_addr, sock_fd, flags);
diff --git a/system/btif/src/btif_sock_hal.cc b/system/btif/src/btif_sock_hal.cc
new file mode 100644
index 0000000..48b40c5
--- /dev/null
+++ b/system/btif/src/btif_sock_hal.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#define LOG_TAG "bt_btif_sock_hal"
+
+#include "btif/include/btif_sock_hal.h"
+
+#include "btif/include/btif_sock_l2cap.h"
+#include "lpp/lpp_offload_interface.h"
+#include "main/shim/entry.h"
+#include "stack/include/main_thread.h"
+
+using namespace bluetooth;
+
+class BtifSocketHalCallback : public hal::SocketHalCallback {
+public:
+  void SocketOpenedComplete(uint64_t socket_id, hal::SocketStatus status) const override {
+    log::info("socket_id: {}, status: {}", socket_id, static_cast<int>(status));
+    do_in_main_thread(base::BindOnce(on_btsocket_l2cap_opened_complete, socket_id,
+                                     (status == hal::SocketStatus::SUCCESS)));
+  }
+
+  void SocketClose(uint64_t socket_id) const override {
+    log::info("socket_id: {}", socket_id);
+    do_in_main_thread(base::BindOnce(on_btsocket_l2cap_close, socket_id));
+  }
+};
+
+static BtifSocketHalCallback btif_socket_hal_cb;
+
+bt_status_t btsock_hal_init() {
+  log::info("");
+  auto lpp_offload_manager_interface = bluetooth::shim::GetLppOffloadManager();
+  if (lpp_offload_manager_interface == nullptr) {
+    log::warn("GetLppOffloadManager() returned nullptr!");
+    return BT_STATUS_FAIL;
+  }
+  if (!lpp_offload_manager_interface->RegisterSocketHalCallback(&btif_socket_hal_cb)) {
+    log::warn("RegisterSocketHalCallback() failed!");
+    return BT_STATUS_FAIL;
+  }
+  return BT_STATUS_SUCCESS;
+}
diff --git a/system/btif/src/btif_sock_l2cap.cc b/system/btif/src/btif_sock_l2cap.cc
index 3aa7162..030f2f4 100644
--- a/system/btif/src/btif_sock_l2cap.cc
+++ b/system/btif/src/btif_sock_l2cap.cc
@@ -16,11 +16,11 @@
  */
 
 #include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <com_android_bluetooth_flags.h>
 
 #include <cstdint>
 #include <cstring>
@@ -36,6 +36,8 @@
 #include "gd/os/rand.h"
 #include "include/hardware/bluetooth.h"
 #include "internal_include/bt_target.h"
+#include "lpp/lpp_offload_interface.h"
+#include "main/shim/entry.h"
 #include "osi/include/allocator.h"
 #include "osi/include/osi.h"
 #include "stack/include/bt_hdr.h"
@@ -65,6 +67,7 @@
   int channel;                // PSM
   int our_fd;                 // fd from our side
   int app_fd;                 // fd from app's side
+  int listen_fd;              // listen socket fd from our side
 
   unsigned bytes_buffered;
   struct packet* first_packet;  // fist packet to be delivered to app
@@ -84,9 +87,18 @@
   uint16_t local_cid;   // The local CID
   uint16_t remote_cid;  // The remote CID
   Uuid conn_uuid;       // The connection uuid
+  uint64_t socket_id;   // Socket ID in connected state
+  btsock_data_path_t data_path;  // socket data path
+  char socket_name[128];         // descriptive socket name
+  uint64_t hub_id;               // ID of the hub to which the end point belongs
+  uint64_t endpoint_id;          // ID of the hub end point
+  bool is_accepting;             // is app accepting on server socket?
 } l2cap_socket;
 
 static void btsock_l2cap_server_listen(l2cap_socket* sock);
+static uint64_t btif_l2cap_sock_generate_socket_id();
+static void on_cl_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock);
+static void on_srv_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock);
 
 static std::mutex state_lock;
 
@@ -246,13 +258,19 @@
 
   log::info(
           "Disconnected L2CAP connection for device: {}, channel: {}, app_uid: {}, "
-          "id: {}, is_le: {}",
-          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc);
+          "id: {}, is_le: {}, socket_id: {}",
+          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id);
   btif_sock_connection_logger(
           sock->addr, sock->id, sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP,
           SOCKET_CONNECTION_STATE_DISCONNECTED,
           sock->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION, sock->app_uid, sock->channel,
           sock->tx_bytes, sock->rx_bytes, sock->name);
+  if (com::android::bluetooth::flags::socket_settings_api()) {
+    if (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD && !sock->server &&
+        sock->socket_id != 0) {
+      bluetooth::shim::GetLppOffloadManager()->SocketClosed(sock->socket_id);
+    }
+  }
   if (sock->next) {
     sock->next->prev = sock->prev;
   }
@@ -334,12 +352,19 @@
 
   sock->our_fd = fds[0];
   sock->app_fd = fds[1];
+  sock->listen_fd = -1;
   sock->security = security;
   sock->server = is_server;
   sock->connected = false;
   sock->handle = 0;
   sock->server_psm_sent = false;
   sock->app_uid = -1;
+  sock->conn_uuid = Uuid::kEmpty;
+  sock->socket_id = 0;
+  sock->data_path = BTSOCK_DATA_PATH_NO_OFFLOAD;
+  sock->hub_id = 0;
+  sock->endpoint_id = 0;
+  sock->is_accepting = false;
 
   if (name) {
     strncpy(sock->name, name, sizeof(sock->name) - 1);
@@ -446,7 +471,7 @@
 
 static bool send_app_connect_signal(int fd, const RawAddress* addr, int channel, int status,
                                     int send_fd, uint16_t rx_mtu, uint16_t tx_mtu,
-                                    const Uuid& conn_uuid) {
+                                    const Uuid& conn_uuid, uint64_t socket_id) {
   sock_connect_signal_t cs;
   cs.size = sizeof(cs);
   cs.bd_addr = *addr;
@@ -456,6 +481,7 @@
   cs.max_tx_packet_size = tx_mtu;
   cs.conn_uuid_lsb = uuid_lsb(conn_uuid);
   cs.conn_uuid_msb = uuid_msb(conn_uuid);
+  cs.socket_id = socket_id;
   if (send_fd != -1) {
     if (sock_send_fd(fd, (const uint8_t*)&cs, sizeof(cs), send_fd) == sizeof(cs)) {
       return true;
@@ -531,7 +557,7 @@
  * will be a clone of the sock representing the BluetoothServerSocket.
  * */
 static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) {
-  // std::mutex locked by caller
+  // state_lock taken by caller
   l2cap_socket* accept_rs = btsock_l2cap_alloc_l(sock->name, &p_open->rem_bda, false, 0);
   accept_rs->connected = true;
   accept_rs->security = sock->security;
@@ -541,10 +567,22 @@
   sock->handle = -1; /* We should no longer associate this handle with the server socket */
   accept_rs->is_le_coc = sock->is_le_coc;
   accept_rs->tx_mtu = sock->tx_mtu = p_open->tx_mtu;
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349374
+    accept_rs->rx_mtu = sock->rx_mtu;
+  }
   accept_rs->local_cid = p_open->local_cid;
   accept_rs->remote_cid = p_open->remote_cid;
+  // TODO(b/342012881) Remove connection uuid when offload socket API is landed.
   Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>());
   accept_rs->conn_uuid = uuid;
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349374
+    accept_rs->socket_id = btif_l2cap_sock_generate_socket_id();
+    accept_rs->data_path = sock->data_path;
+    strncpy(accept_rs->socket_name, sock->socket_name, sizeof(accept_rs->socket_name) - 1);
+    accept_rs->socket_name[sizeof(accept_rs->socket_name) - 1] = '\0';
+    accept_rs->hub_id = sock->hub_id;
+    accept_rs->endpoint_id = sock->endpoint_id;
+  }
 
   /* Swap IDs to hand over the GAP connection to the accepted socket, and start
      a new server on the newly create socket ID. */
@@ -554,8 +592,9 @@
 
   log::info(
           "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, "
-          "id: {}, is_le: {}",
-          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc);
+          "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}",
+          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, accept_rs->socket_id,
+          accept_rs->rx_mtu);
   btif_sock_connection_logger(accept_rs->addr, accept_rs->id,
                               accept_rs->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP,
                               SOCKET_CONNECTION_STATE_CONNECTED,
@@ -566,11 +605,15 @@
   btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_EXCEPTION, sock->id);
   btsock_thread_add_fd(pth, accept_rs->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, accept_rs->id);
   send_app_connect_signal(sock->our_fd, &accept_rs->addr, sock->channel, 0, accept_rs->app_fd,
-                          sock->rx_mtu, p_open->tx_mtu, accept_rs->conn_uuid);
+                          sock->rx_mtu, p_open->tx_mtu, accept_rs->conn_uuid, accept_rs->socket_id);
   accept_rs->app_fd = -1;  // The fd is closed after sent to app in send_app_connect_signal()
   // But for some reason we still leak a FD - either the server socket
   // one or the accept socket one.
   btsock_l2cap_server_listen(sock);
+  // start monitoring the socketpair to get call back when app is accepting on server socket
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349375
+    btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+  }
 }
 
 static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) {
@@ -578,8 +621,12 @@
   sock->tx_mtu = p_open->tx_mtu;
   sock->local_cid = p_open->local_cid;
   sock->remote_cid = p_open->remote_cid;
+  // TODO(b/342012881) Remove connection uuid when offload socket API is landed.
   Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>());
   sock->conn_uuid = uuid;
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349374
+    sock->socket_id = btif_l2cap_sock_generate_socket_id();
+  }
 
   if (!send_app_psm_or_chan_l(sock)) {
     log::error("Unable to send l2cap socket to application socket_id:{}", sock->id);
@@ -587,15 +634,16 @@
   }
 
   if (!send_app_connect_signal(sock->our_fd, &sock->addr, sock->channel, 0, -1, sock->rx_mtu,
-                               p_open->tx_mtu, sock->conn_uuid)) {
+                               p_open->tx_mtu, sock->conn_uuid, sock->socket_id)) {
     log::error("Unable to connect l2cap socket to application socket_id:{}", sock->id);
     return;
   }
 
   log::info(
           "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, "
-          "id: {}, is_le: {}",
-          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc);
+          "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}",
+          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id,
+          sock->rx_mtu);
   btif_sock_connection_logger(sock->addr, sock->id,
                               sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP,
                               SOCKET_CONNECTION_STATE_CONNECTED,
@@ -621,10 +669,19 @@
 
   sock->tx_mtu = le_open->tx_mtu;
   if (psm_open->status == tBTA_JV_STATUS::SUCCESS) {
-    if (!sock->server) {
-      on_cl_l2cap_psm_connect_l(psm_open, sock);
+    if (!com::android::bluetooth::flags::socket_settings_api() ||  // Added with aosp/3349378
+        sock->data_path == BTSOCK_DATA_PATH_NO_OFFLOAD) {
+      if (!sock->server) {
+        on_cl_l2cap_psm_connect_l(psm_open, sock);
+      } else {
+        on_srv_l2cap_psm_connect_l(psm_open, sock);
+      }
     } else {
-      on_srv_l2cap_psm_connect_l(psm_open, sock);
+      if (!sock->server) {
+        on_cl_l2cap_psm_connect_offload_l(psm_open, sock);
+      } else {
+        on_srv_l2cap_psm_connect_offload_l(psm_open, sock);
+      }
     }
   } else {
     log::error("Unable to open socket after receiving connection socket_id:{}", sock->id);
@@ -825,6 +882,15 @@
   /* Setup ETM settings: mtu will be set below */
   std::unique_ptr<tL2CAP_CFG_INFO> cfg = std::make_unique<tL2CAP_CFG_INFO>(
           tL2CAP_CFG_INFO{.fcr_present = true, .fcr = kDefaultErtmOptions});
+  /* For hardware offload data path, host stack sets the initial credits to 0. The offload stack
+   * should send initial credits to peer device through L2CAP signaling command when the data path
+   * is switched successfully. */
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349376
+    if (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) {
+      cfg->init_credit_present = true;
+      cfg->init_credit = 0;
+    }
+  }
 
   std::unique_ptr<tL2CAP_ERTM_INFO> ertm_info;
   if (!sock->is_le_coc) {
@@ -835,9 +901,38 @@
                          sock->rx_mtu, std::move(cfg), btsock_l2cap_cbk, sock->id);
 }
 
+/*
+ * Determine the local MTU for the offloaded L2CAP connection.
+ *
+ * The local MTU is selected as the minimum of:
+ *   - The socket hal's offload capabilities (socket_cap.leCocCapabilities.mtu)
+ *   - The application's requested maximum RX packet size (app_max_rx_packet_size)
+ *
+ * However, the MTU must be at least the minimum required by the L2CAP LE
+ * specification (L2CAP_SDU_LENGTH_LE_MIN).
+ */
+
+static bool btsock_l2cap_get_offload_mtu(uint16_t* rx_mtu, uint16_t app_max_rx_packet_size) {
+  hal::SocketCapabilities socket_cap =
+          bluetooth::shim::GetLppOffloadManager()->GetSocketCapabilities();
+  if (!socket_cap.le_coc_capabilities.number_of_supported_sockets) {
+    return false;
+  }
+  /* Socket HAL client has already verified that the MTU is in a valid range. */
+  uint16_t mtu = static_cast<uint16_t>(socket_cap.le_coc_capabilities.mtu);
+  mtu = std::min(mtu, app_max_rx_packet_size);
+  if (mtu < L2CAP_SDU_LENGTH_LE_MIN) {
+    mtu = L2CAP_SDU_LENGTH_LE_MIN;
+  }
+  *rx_mtu = mtu;
+  return true;
+}
+
 static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAddress* addr,
                                                   int channel, int* sock_fd, int flags, char listen,
-                                                  int app_uid) {
+                                                  int app_uid, btsock_data_path_t data_path,
+                                                  const char* socket_name, uint64_t hub_id,
+                                                  uint64_t endpoint_id, int max_rx_packet_size) {
   if (!is_inited()) {
     return BT_STATUS_NOT_READY;
   }
@@ -875,11 +970,31 @@
   sock->channel = channel;
   sock->app_uid = app_uid;
   sock->is_le_coc = is_le_coc;
-  sock->rx_mtu = is_le_coc ? L2CAP_SDU_LENGTH_LE_MAX : L2CAP_SDU_LENGTH_MAX;
+  if (com::android::bluetooth::flags::socket_settings_api() &&  // Added with aosp/3349377
+      data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) {
+    if (!btsock_l2cap_get_offload_mtu(&sock->rx_mtu, static_cast<uint16_t>(max_rx_packet_size))) {
+      return BT_STATUS_UNSUPPORTED;
+    }
+  } else {
+    sock->rx_mtu = is_le_coc ? L2CAP_SDU_LENGTH_LE_MAX : L2CAP_SDU_LENGTH_MAX;
+  }
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349374
+    sock->data_path = data_path;
+    if (socket_name) {
+      strncpy(sock->socket_name, socket_name, sizeof(sock->socket_name) - 1);
+      sock->socket_name[sizeof(sock->socket_name) - 1] = '\0';
+    }
+    sock->hub_id = hub_id;
+    sock->endpoint_id = endpoint_id;
+  }
 
   /* "role" is never initialized in rfcomm code */
   if (listen) {
     btsock_l2cap_server_listen(sock);
+    // start monitoring the socketpair to get call back when app is accepting on server socket
+    if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349375
+      btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+    }
   } else {
     tBTA_JV_CONN_TYPE connection_type =
             sock->is_le_coc ? tBTA_JV_CONN_TYPE::L2CAP_LE : tBTA_JV_CONN_TYPE::L2CAP;
@@ -887,6 +1002,15 @@
     /* Setup ETM settings: mtu will be set below */
     std::unique_ptr<tL2CAP_CFG_INFO> cfg = std::make_unique<tL2CAP_CFG_INFO>(
             tL2CAP_CFG_INFO{.fcr_present = true, .fcr = kDefaultErtmOptions});
+    /* For hardware offload data path, host stack sets the initial credits to 0. The offload stack
+     * should send initial credits to peer device through L2CAP signaling command when the data path
+     * is switched successfully. */
+    if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349376
+      if (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) {
+        cfg->init_credit_present = true;
+        cfg->init_credit = 0;
+      }
+    }
 
     std::unique_ptr<tL2CAP_ERTM_INFO> ertm_info;
     if (!sock->is_le_coc) {
@@ -909,14 +1033,19 @@
   return BT_STATUS_SUCCESS;
 }
 
-bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags,
-                                int app_uid) {
-  return btsock_l2cap_listen_or_connect(name, NULL, channel, sock_fd, flags, 1, app_uid);
+bt_status_t btsock_l2cap_listen(const char* name, int channel, int* sock_fd, int flags, int app_uid,
+                                btsock_data_path_t data_path, const char* socket_name,
+                                uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) {
+  return btsock_l2cap_listen_or_connect(name, NULL, channel, sock_fd, flags, 1, app_uid, data_path,
+                                        socket_name, hub_id, endpoint_id, max_rx_packet_size);
 }
 
 bt_status_t btsock_l2cap_connect(const RawAddress* bd_addr, int channel, int* sock_fd, int flags,
-                                 int app_uid) {
-  return btsock_l2cap_listen_or_connect(NULL, bd_addr, channel, sock_fd, flags, 0, app_uid);
+                                 int app_uid, btsock_data_path_t data_path, const char* socket_name,
+                                 uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size) {
+  return btsock_l2cap_listen_or_connect(NULL, bd_addr, channel, sock_fd, flags, 0, app_uid,
+                                        data_path, socket_name, hub_id, endpoint_id,
+                                        max_rx_packet_size);
 }
 
 /* return true if we have more to send and should wait for user readiness, false
@@ -962,7 +1091,111 @@
   return (uint8_t*)(msg) + BT_HDR_SIZE + msg->offset;
 }
 
+// state_lock taken by caller
+bool btsock_l2cap_read_signaled_on_connected_socket(int fd, int flags, uint32_t user_id,
+                                                    l2cap_socket* sock) {
+  if (!sock->connected) {
+    return false;
+  }
+  int size = 0;
+  bool ioctl_success = ioctl(sock->our_fd, FIONREAD, &size) == 0;
+  if (!(flags & SOCK_THREAD_FD_EXCEPTION) || (ioctl_success && size)) {
+    /* FIONREAD return number of bytes that are immediately available for
+      reading, might be bigger than awaiting packet.
+
+      BluetoothSocket.write(...) guarantees that any packet send to this
+      socket is broken into pieces no bigger than MTU bytes (as requested
+      by BT spec). */
+    size = std::min(size, (int)sock->tx_mtu);
+
+    BT_HDR* buffer = malloc_l2cap_buf(size);
+    /* The socket is created with SOCK_SEQPACKET, hence we read one message
+     * at the time. */
+    ssize_t count;
+    OSI_NO_INTR(count = recv(fd, get_l2cap_sdu_start_ptr(buffer), size,
+                             MSG_NOSIGNAL | MSG_DONTWAIT | MSG_TRUNC));
+    if (count > sock->tx_mtu) {
+      /* This can't happen thanks to check in BluetoothSocket.java but leave
+       * this in case this socket is ever used anywhere else*/
+      log::error("recv more than MTU. Data will be lost: {}", count);
+      count = sock->tx_mtu;
+    }
+
+    /* When multiple packets smaller than MTU are flushed to the socket, the
+      size of the single packet read could be smaller than the ioctl
+      reported total size of awaiting packets. Hence, we adjust the buffer
+      length. */
+    buffer->len = count;
+
+    // will take care of freeing buffer
+    BTA_JvL2capWrite(sock->handle, PTR_TO_UINT(buffer), buffer, user_id);
+  }
+  return true;
+}
+
+// state_lock taken by caller
+bool btsock_l2cap_read_signaled_on_listen_socket(int fd, int /* flags */, uint32_t /* user_id */,
+                                                 l2cap_socket* sock) {
+  int size = 0;
+  bool ioctl_success = ioctl(sock->our_fd, FIONREAD, &size) == 0;
+  if (ioctl_success && size) {
+    sock_accept_signal_t accept_signal = {};
+    ssize_t count;
+    OSI_NO_INTR(count = recv(fd, reinterpret_cast<uint8_t*>(&accept_signal), sizeof(accept_signal),
+                             MSG_NOSIGNAL | MSG_DONTWAIT | MSG_TRUNC));
+    if (count != sizeof(accept_signal) || count != accept_signal.size) {
+      log::error("Unexpected count {} sizeof(accept_signal) {} accept_signal.size {}", count,
+                 sizeof(accept_signal), accept_signal.size);
+      return false;
+    }
+    sock->is_accepting = accept_signal.is_accepting;
+    log::info("Server socket {} is_accepting {}", sock->id, sock->is_accepting);
+  }
+  return true;
+}
+
+void btsock_l2cap_signaled_flagged(int fd, int flags, uint32_t user_id) {
+  char drop_it = false;
+
+  /* We use MSG_DONTWAIT when sending data to JAVA, hence it can be accepted to
+   * hold the lock. */
+  std::unique_lock<std::mutex> lock(state_lock);
+  l2cap_socket* sock = btsock_l2cap_find_by_id_l(user_id);
+  if (!sock) {
+    return;
+  }
+  if (flags & SOCK_THREAD_FD_RD) {
+    if (!sock->server) {
+      // app sending data on connection socket
+      if (!btsock_l2cap_read_signaled_on_connected_socket(fd, flags, user_id, sock)) {
+        drop_it = true;
+      }
+    } else {
+      // app sending signal on listen socket
+      if (!btsock_l2cap_read_signaled_on_listen_socket(fd, flags, user_id, sock)) {
+        drop_it = true;
+      }
+    }
+  }
+  if (flags & SOCK_THREAD_FD_WR) {
+    // app is ready to receive more data, tell stack to enable the data flow
+    if (flush_incoming_que_on_wr_signal_l(sock) && sock->connected) {
+      btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_WR, sock->id);
+    }
+  }
+  if (drop_it || (flags & SOCK_THREAD_FD_EXCEPTION)) {
+    int size = 0;
+    if (drop_it || ioctl(sock->our_fd, FIONREAD, &size) != 0 || size == 0) {
+      btsock_l2cap_free_l(sock);
+    }
+  }
+}
+
 void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id) {
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349375
+    btsock_l2cap_signaled_flagged(fd, flags, user_id);
+    return;
+  }
   char drop_it = false;
 
   /* We use MSG_DONTWAIT when sending data to JAVA, hence it can be accepted to
@@ -1074,3 +1307,198 @@
   *cid = sock->remote_cid;
   return BT_STATUS_SUCCESS;
 }
+
+// TODO(b/380189525): Replace the randomized socket ID with static counter when we don't have
+// security concerns about using static counter.
+static uint64_t btif_l2cap_sock_generate_socket_id() {
+  uint64_t socket_id;
+  do {
+    socket_id = bluetooth::os::GenerateRandomUint64();
+  } while (!socket_id);
+  return socket_id;
+}
+
+/* only call with state_lock taken */
+static l2cap_socket* btsock_l2cap_find_by_socket_id_l(uint64_t socket_id) {
+  l2cap_socket* sock = socks;
+
+  while (sock) {
+    if (sock->socket_id == socket_id) {
+      return sock;
+    }
+    sock = sock->next;
+  }
+
+  return nullptr;
+}
+
+void on_btsocket_l2cap_opened_complete(uint64_t socket_id, bool success) {
+  l2cap_socket* sock;
+
+  std::unique_lock<std::mutex> lock(state_lock);
+  sock = btsock_l2cap_find_by_socket_id_l(socket_id);
+  if (!sock) {
+    log::error("Unable to find l2cap socket with socket_id:{}", socket_id);
+    return;
+  }
+  if (!success) {
+    log::error("L2CAP opened complete failed with socket_id:{}", socket_id);
+    btsock_l2cap_free_l(sock);
+    return;
+  }
+  // If the socket was accepted from listen socket, use listen_fd.
+  if (sock->listen_fd != -1) {
+    send_app_connect_signal(sock->listen_fd, &sock->addr, sock->channel, 0, sock->app_fd,
+                            sock->rx_mtu, sock->tx_mtu, sock->conn_uuid, sock->socket_id);
+    // The fd is closed after sent to app in send_app_connect_signal()
+    sock->app_fd = -1;
+  } else {
+    if (!send_app_psm_or_chan_l(sock)) {
+      log::error("Unable to send l2cap socket to application socket_id:{}", sock->id);
+      return;
+    }
+    if (!send_app_connect_signal(sock->our_fd, &sock->addr, sock->channel, 0, -1, sock->rx_mtu,
+                                 sock->tx_mtu, sock->conn_uuid, sock->socket_id)) {
+      log::error("Unable to connect l2cap socket to application socket_id:{}", sock->id);
+      return;
+    }
+
+    log::info(
+            "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, id: {}, "
+            "is_le: {}, socket_id: {}, rx_mtu: {}",
+            sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id,
+            sock->rx_mtu);
+    btif_sock_connection_logger(sock->addr, sock->id,
+                                sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP,
+                                SOCKET_CONNECTION_STATE_CONNECTED,
+                                sock->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION,
+                                sock->app_uid, sock->channel, 0, 0, sock->name);
+
+    log::info("Connected l2cap socket socket_id:{}", sock->id);
+    sock->connected = true;
+  }
+}
+
+void on_btsocket_l2cap_close(uint64_t socket_id) {
+  l2cap_socket* sock;
+
+  std::unique_lock<std::mutex> lock(state_lock);
+  sock = btsock_l2cap_find_by_socket_id_l(socket_id);
+  if (!sock) {
+    log::error("Unable to find l2cap socket with socket_id:{}", socket_id);
+    return;
+  }
+  log::info("L2CAP close request for socket_id:{}", socket_id);
+  btsock_l2cap_free_l(sock);
+}
+
+static void on_cl_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) {
+  sock->addr = p_open->rem_bda;
+  sock->tx_mtu = p_open->tx_mtu;
+  sock->local_cid = p_open->local_cid;
+  sock->remote_cid = p_open->remote_cid;
+  // TODO(b/342012881) Remove connection uuid when offload socket API is landed.
+  Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>());
+  sock->conn_uuid = uuid;
+  sock->socket_id = btif_l2cap_sock_generate_socket_id();
+
+  log::info(
+          "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, "
+          "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}",
+          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, sock->socket_id,
+          sock->rx_mtu);
+  btif_sock_connection_logger(sock->addr, sock->id,
+                              sock->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP,
+                              SOCKET_CONNECTION_STATE_CONNECTED,
+                              sock->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION,
+                              sock->app_uid, sock->channel, 0, 0, sock->name);
+
+  bluetooth::hal::SocketContext socket_context = {
+          .socket_id = sock->socket_id,
+          .name = sock->socket_name,
+          .acl_connection_handle = p_open->acl_handle,
+          .channel_info = bluetooth::hal::LeCocChannelInfo(
+                  p_open->local_cid, p_open->remote_cid, static_cast<uint16_t>(sock->channel),
+                  sock->rx_mtu, sock->tx_mtu, p_open->local_coc_mps, p_open->remote_coc_mps,
+                  p_open->local_coc_credit, p_open->remote_coc_credit),
+          .endpoint_info.hub_id = sock->hub_id,
+          .endpoint_info.endpoint_id = sock->endpoint_id,
+  };
+  if (!bluetooth::shim::GetLppOffloadManager()->SocketOpened(socket_context)) {
+    log::warn("L2CAP socket opened failed. Disconnect the incoming connection.");
+    btsock_l2cap_free_l(sock);
+  } else {
+    log::info(
+            "L2CAP socket opened successful. Will send connect signal in "
+            "on_btsocket_l2cap_opened_complete() asynchronously.");
+  }
+}
+
+static void on_srv_l2cap_psm_connect_offload_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock) {
+  // std::mutex locked by caller
+  l2cap_socket* accept_rs = btsock_l2cap_alloc_l(sock->name, &p_open->rem_bda, false, 0);
+  accept_rs->connected = true;
+  accept_rs->security = sock->security;
+  accept_rs->channel = sock->channel;
+  accept_rs->handle = sock->handle;
+  accept_rs->app_uid = sock->app_uid;
+  sock->handle = -1; /* We should no longer associate this handle with the server socket */
+  accept_rs->is_le_coc = sock->is_le_coc;
+  accept_rs->tx_mtu = sock->tx_mtu = p_open->tx_mtu;
+  accept_rs->rx_mtu = sock->rx_mtu;
+  accept_rs->local_cid = p_open->local_cid;
+  accept_rs->remote_cid = p_open->remote_cid;
+  // TODO(b/342012881) Remove connection uuid when offload socket API is landed.
+  Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom<Uuid::kNumBytes128>());
+  accept_rs->conn_uuid = uuid;
+  accept_rs->socket_id = btif_l2cap_sock_generate_socket_id();
+  accept_rs->data_path = sock->data_path;
+  strncpy(accept_rs->socket_name, sock->socket_name, sizeof(accept_rs->socket_name) - 1);
+  accept_rs->socket_name[sizeof(accept_rs->socket_name) - 1] = '\0';
+  accept_rs->hub_id = sock->hub_id;
+  accept_rs->endpoint_id = sock->endpoint_id;
+  accept_rs->listen_fd = sock->our_fd;
+
+  /* Swap IDs to hand over the GAP connection to the accepted socket, and start
+     a new server on the newly create socket ID. */
+  uint32_t new_listen_id = accept_rs->id;
+  accept_rs->id = sock->id;
+  sock->id = new_listen_id;
+
+  log::info(
+          "Connected to L2CAP connection for device: {}, channel: {}, app_uid: {}, "
+          "id: {}, is_le: {}, socket_id: {}, rx_mtu: {}",
+          sock->addr, sock->channel, sock->app_uid, sock->id, sock->is_le_coc, accept_rs->socket_id,
+          accept_rs->rx_mtu);
+  btif_sock_connection_logger(accept_rs->addr, accept_rs->id,
+                              accept_rs->is_le_coc ? BTSOCK_L2CAP_LE : BTSOCK_L2CAP,
+                              SOCKET_CONNECTION_STATE_CONNECTED,
+                              accept_rs->server ? SOCKET_ROLE_LISTEN : SOCKET_ROLE_CONNECTION,
+                              accept_rs->app_uid, accept_rs->channel, 0, 0, accept_rs->name);
+
+  bluetooth::hal::SocketContext socket_context = {
+          .socket_id = accept_rs->socket_id,
+          .name = accept_rs->socket_name,
+          .acl_connection_handle = p_open->acl_handle,
+          .channel_info = bluetooth::hal::LeCocChannelInfo(
+                  p_open->local_cid, p_open->remote_cid, static_cast<uint16_t>(accept_rs->channel),
+                  accept_rs->rx_mtu, accept_rs->tx_mtu, p_open->local_coc_mps,
+                  p_open->remote_coc_mps, p_open->local_coc_credit, p_open->remote_coc_credit),
+          .endpoint_info.hub_id = accept_rs->hub_id,
+          .endpoint_info.endpoint_id = accept_rs->endpoint_id,
+  };
+  if (!sock->is_accepting) {
+    log::warn("Server socket is not accepting. Disconnect the incoming connection.");
+    btsock_l2cap_free_l(accept_rs);
+  } else if (!bluetooth::shim::GetLppOffloadManager()->SocketOpened(socket_context)) {
+    log::warn("L2CAP socket opened failed. Disconnect the incoming connection.");
+    btsock_l2cap_free_l(accept_rs);
+  } else {
+    log::info("L2CAP socket opened successful. Will send connect signal in async callback.");
+  }
+  // start monitor the socket
+  btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_EXCEPTION, sock->id);
+  btsock_l2cap_server_listen(sock);
+  // start monitoring the socketpair to get call back when app is accepting on server socket
+  btsock_thread_add_fd(pth, sock->our_fd, BTSOCK_L2CAP, SOCK_THREAD_FD_RD, sock->id);
+}
diff --git a/system/btif/src/btif_sock_rfc.cc b/system/btif/src/btif_sock_rfc.cc
index e8bf6b4..0f9daa8 100644
--- a/system/btif/src/btif_sock_rfc.cc
+++ b/system/btif/src/btif_sock_rfc.cc
@@ -510,6 +510,7 @@
   cs.max_tx_packet_size = 0;  // not used for RFCOMM
   cs.conn_uuid_lsb = 0;       // not used for RFCOMM
   cs.conn_uuid_msb = 0;       // not used for RFCOMM
+  cs.socket_id = 0;           // not used for RFCOMM
   if (send_fd == INVALID_FD) {
     return sock_send_all(fd, (const uint8_t*)&cs, sizeof(cs)) == sizeof(cs);
   }
diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc
index 8d78ce6..66881a9 100644
--- a/system/btif/test/btif_core_test.cc
+++ b/system/btif/test/btif_core_test.cc
@@ -1022,9 +1022,14 @@
   static constexpr int kAppUid = 3;
   const Uuid server_uuid = Uuid::From16Bit(UUID_SERVCLASS_SERIAL_PORT);
   int socket_number = 0;
+  btsock_data_path_t data_path = BTSOCK_DATA_PATH_NO_OFFLOAD;
+  uint64_t hub_id = 0;
+  uint64_t endpoint_id = 0;
+  int max_rx_packet_size = 0;
   ASSERT_EQ(BT_STATUS_SUCCESS,
-            btif_sock_get_interface()->listen(BTSOCK_RFCOMM, "TestService", &server_uuid,
-                                              kChannelOne, &socket_number, kFlags, kAppUid));
+            btif_sock_get_interface()->listen(
+                    BTSOCK_RFCOMM, "TestService", &server_uuid, kChannelOne, &socket_number, kFlags,
+                    kAppUid, data_path, "TestSocket", hub_id, endpoint_id, max_rx_packet_size));
 }
 
 TEST_F(BtifCoreSocketTest, CreateTwoRfcommServerSockets) {
@@ -1033,9 +1038,14 @@
   static constexpr int kAppUid = 3;
   const Uuid server_uuid = Uuid::From16Bit(UUID_SERVCLASS_SERIAL_PORT);
   int socket_number = 0;
+  btsock_data_path_t data_path = BTSOCK_DATA_PATH_NO_OFFLOAD;
+  uint64_t hub_id = 0;
+  uint64_t endpoint_id = 0;
+  int max_rx_packet_size = 0;
   ASSERT_EQ(BT_STATUS_SUCCESS,
-            btif_sock_get_interface()->listen(BTSOCK_RFCOMM, "TestService", &server_uuid,
-                                              kChannelOne, &socket_number, kFlags, kAppUid));
+            btif_sock_get_interface()->listen(
+                    BTSOCK_RFCOMM, "TestService", &server_uuid, kChannelOne, &socket_number, kFlags,
+                    kAppUid, data_path, "TestSocket", hub_id, endpoint_id, max_rx_packet_size));
   static constexpr int kChannelTwo = 2;
   static constexpr int kFlagsTwo = 4;
   static constexpr int kAppUidTwo = 6;
@@ -1043,7 +1053,8 @@
   int socket_number_two = 1;
   ASSERT_EQ(BT_STATUS_SUCCESS, btif_sock_get_interface()->listen(
                                        BTSOCK_RFCOMM, "ServiceTwo", &server_uuid_two, kChannelTwo,
-                                       &socket_number_two, kFlagsTwo, kAppUidTwo));
+                                       &socket_number_two, kFlagsTwo, kAppUidTwo, data_path,
+                                       "TestSocket", hub_id, endpoint_id, max_rx_packet_size));
 }
 
 TEST_F(BtifCoreSocketTest, CreateManyRfcommServerSockets) {
@@ -1059,9 +1070,14 @@
     server_uuid_str[1] = (i / 100) % 10 + '0';
     server_uuid_str[0] = (i / 1000) % 10 + '0';
     Uuid server_uuid = Uuid::FromString(server_uuid_str);
+    btsock_data_path_t data_path = BTSOCK_DATA_PATH_NO_OFFLOAD;
+    uint64_t hub_id = 0;
+    uint64_t endpoint_id = 0;
+    int max_rx_packet_size = 0;
     ASSERT_EQ(BT_STATUS_SUCCESS,
-              btif_sock_get_interface()->listen(BTSOCK_RFCOMM, "TestService", &server_uuid, channel,
-                                                &socket_number, flags, app_uuid));
+              btif_sock_get_interface()->listen(
+                      BTSOCK_RFCOMM, "TestService", &server_uuid, channel, &socket_number, flags,
+                      app_uuid, data_path, "TestSocket", hub_id, endpoint_id, max_rx_packet_size));
     ASSERT_EQ(0, close(socket_number));
   }
 }
diff --git a/system/gd/Android.bp b/system/gd/Android.bp
index a8dda59..b928ee2 100644
--- a/system/gd/Android.bp
+++ b/system/gd/Android.bp
@@ -87,6 +87,7 @@
             srcs: [
                 ":BluetoothHalSources_hci_host",
                 ":BluetoothHalSources_ranging_host",
+                ":BluetoothHalSources_socket_host",
                 ":BluetoothOsSources_host",
                 ":BluetoothSyspropsSources",
             ],
@@ -95,6 +96,7 @@
             srcs: [
                 ":BluetoothHalSources_hci_android_hidl",
                 ":BluetoothHalSources_ranging_android",
+                ":BluetoothHalSources_socket_android",
                 ":BluetoothOsSources_android",
             ],
             shared_libs: [
@@ -117,6 +119,8 @@
             whole_static_libs: [
                 "android.hardware.bluetooth-V1-ndk",
                 "android.hardware.bluetooth.ranging-V2-ndk",
+                "android.hardware.bluetooth.socket-V1-ndk",
+                "android.hardware.contexthub-V4-ndk",
             ],
         },
     },
@@ -124,6 +128,7 @@
         ":BluetoothCommonSources",
         ":BluetoothHalSources",
         ":BluetoothHciSources",
+        ":BluetoothLppOffloadSources",
         ":BluetoothMetricsSources",
         ":BluetoothNeighborSources",
         ":BluetoothOsSources",
diff --git a/system/gd/BUILD.gn b/system/gd/BUILD.gn
index fc4b808..fc51ba8 100644
--- a/system/gd/BUILD.gn
+++ b/system/gd/BUILD.gn
@@ -64,6 +64,7 @@
     "//bt/system/gd/hal:BluetoothHalSources",
     "//bt/system/gd/hal:BluetoothHalSources_hci_host",
     "//bt/system/gd/hal:BluetoothHalSources_ranging_host",
+    "//bt/system/gd/hal:BluetoothHalSources_socket_host",
     "//bt/system/gd/metrics:BluetoothMetricsSources",
     "//bt/system/gd/neighbor:BluetoothNeighborSources",
     "//bt/system/gd/shim:BluetoothShimSources",
diff --git a/system/gd/hal/Android.bp b/system/gd/hal/Android.bp
index 91e1573..49683af 100644
--- a/system/gd/hal/Android.bp
+++ b/system/gd/hal/Android.bp
@@ -43,6 +43,20 @@
 }
 
 filegroup {
+    name: "BluetoothHalSources_socket_android",
+    srcs: [
+        "socket_hal_android.cc",
+    ],
+}
+
+filegroup {
+    name: "BluetoothHalSources_socket_host",
+    srcs: [
+        "socket_hal_host.cc",
+    ],
+}
+
+filegroup {
     name: "BluetoothHalSources_hci_android_hidl",
     srcs: [
         "hci_backend_aidl.cc",
diff --git a/system/gd/hal/BUILD.gn b/system/gd/hal/BUILD.gn
index 46be6e9..ebf1525 100644
--- a/system/gd/hal/BUILD.gn
+++ b/system/gd/hal/BUILD.gn
@@ -53,3 +53,12 @@
   configs += [ "//bt/system/gd:gd_defaults" ]
   deps = [ "//bt/system/gd:gd_default_deps" ]
 }
+
+source_set("BluetoothHalSources_socket_host") {
+  sources = [
+    "socket_hal_host.cc",
+  ]
+
+  configs += [ "//bt/system/gd:gd_defaults" ]
+  deps = [ "//bt/system/gd:gd_default_deps" ]
+}
diff --git a/system/gd/hal/socket_hal.h b/system/gd/hal/socket_hal.h
new file mode 100644
index 0000000..2f3d583
--- /dev/null
+++ b/system/gd/hal/socket_hal.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include "module.h"
+
+namespace bluetooth::hal {
+
+enum SocketStatus {
+  SUCCESS = 0,
+  FAILURE,
+};
+
+struct EndpointInfo {
+  // The ID of the Hub to which the end point belongs for hardware offload data path.
+  uint64_t hub_id;
+
+  //  The ID of the Hub endpoint for hardware offload data path.
+  uint64_t endpoint_id;
+};
+
+struct LeCocCapabilities {
+  // Maximum number of LE COC sockets supported. If not supported, the value must be zero.
+  int number_of_supported_sockets;
+
+  // Local Maximum Transmission Unit size in octets.
+  uint16_t mtu;
+};
+
+struct SocketCapabilities {
+  LeCocCapabilities le_coc_capabilities;
+};
+
+struct LeCocChannelInfo {
+  // L2cap local channel ID.
+  uint16_t local_cid;
+
+  // L2cap remote channel ID.
+  uint16_t remote_cid;
+
+  // PSM for L2CAP LE CoC.
+  uint16_t psm;
+
+  // Local Maximum Transmission Unit for LE COC specifying the maximum SDU size in bytes that the
+  // local L2CAP layer can receive.
+  uint16_t local_mtu;
+
+  // Remote Maximum Transmission Unit for LE COC specifying the maximum SDU size in bytes that the
+  // remote L2CAP layer can receive.
+  uint16_t remote_mtu;
+
+  // Local Maximum PDU payload Size in bytes that the local L2CAP layer can receive.
+  uint16_t local_mps;
+
+  // Remote Maximum PDU payload Size in bytes that the remote L2CAP layer can receive.
+  uint16_t remote_mps;
+
+  // Protocol initial credits at Rx path.
+  uint16_t initial_rx_credits;
+
+  // Protocol initial credits at Tx path.
+  uint16_t initial_tx_credits;
+};
+
+struct SocketContext {
+  // Identifier assigned to the socket by the host stack when the socket is connected.
+  uint64_t socket_id;
+
+  // Descriptive socket name provided by the host app when it created this socket.
+  std::string name;
+
+  // ACL connection handle for the socket.
+  uint16_t acl_connection_handle;
+
+  // Channel information of different protocol used for the socket.
+  std::variant<LeCocChannelInfo> channel_info;
+
+  // Endpoint information.
+  EndpointInfo endpoint_info;
+};
+
+/**
+ * SocketHalCallback provides an interface for receiving asynchronous events from socket HAL.
+ * Implementations of this class can be registered with the stack to receive these callbacks.
+ *
+ * Callback methods in this interface are invoked from the binder thread. This means that
+ * implementations must be thread-safe and handle any necessary synchronization to avoid race
+ * conditions or other concurrency issues. The callee is solely responsible for ensuring thread
+ * safety within the callback methods.
+ */
+class SocketHalCallback {
+public:
+  virtual ~SocketHalCallback() = default;
+
+  /**
+   * Invoked when IBluetoothSocket.opened() has been completed.
+   *
+   * @param socket_id Identifier assigned to the socket by the host stack
+   * @param status Status indicating success or failure
+   */
+  virtual void SocketOpenedComplete(uint64_t socket_id, SocketStatus status) const = 0;
+
+  /**
+   * Invoked when offload app or stack requests host stack to close the socket.
+   *
+   * @param socket_id Identifier assigned to the socket by the host stack
+   */
+  virtual void SocketClose(uint64_t socket_id) const = 0;
+};
+
+/**
+ * SocketHal provides an interface to low-power processors, enabling Bluetooth Offload Socket
+ * functionality.
+ *
+ * Bluetooth Offload Socket allows the transfer of channel information from an established
+ * BluetoothSocket to a low-power processor. This enables the offload stack on the low-power
+ * processor to handle packet reception, processing, and transmission independently. This offloading
+ * process prevents the need to wake the main application processor, improving power efficiency.
+ */
+class SocketHal : public ::bluetooth::Module {
+public:
+  static const ModuleFactory Factory;
+
+  virtual ~SocketHal() = default;
+
+  /**
+   * Registers a socket hal callback function to receive asynchronous events from socket HAL.
+   *
+   * @param callback A pointer to the callback function. Must not be nullptr and must have static
+   * lifetime.
+   * @return True if the callback was successfully registered, false otherwise.
+   */
+  virtual bool RegisterCallback(hal::SocketHalCallback const* callback) = 0;
+
+  /**
+   * Retrieves the supported offloaded socket capabilities.
+   *
+   * @return Supported socket capabilities
+   */
+  virtual hal::SocketCapabilities GetSocketCapabilities() const = 0;
+
+  /**
+   * Notifies the socket HAL that the socket has been opened.
+   *
+   * @param context Socket context including socket ID, channel, hub, and endpoint info
+   * @return Result of calling this method
+   */
+  virtual bool Opened(const hal::SocketContext& context) const = 0;
+
+  /**
+   * Notifies the socket HAL that the socket has been closed.
+   *
+   * @param socket_id Identifier assigned to the socket by the host stack
+   */
+  virtual void Closed(uint64_t socket_id) const = 0;
+};
+
+}  // namespace bluetooth::hal
diff --git a/system/gd/hal/socket_hal_android.cc b/system/gd/hal/socket_hal_android.cc
new file mode 100644
index 0000000..707fd42
--- /dev/null
+++ b/system/gd/hal/socket_hal_android.cc
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/android/hardware/bluetooth/socket/BnBluetoothSocketCallback.h>
+#include <aidl/android/hardware/bluetooth/socket/IBluetoothSocket.h>
+#include <aidl/android/hardware/bluetooth/socket/IBluetoothSocketCallback.h>
+#include <android/binder_manager.h>
+#include <bluetooth/log.h>
+
+// syslog.h conflicts with libchrome/base/logging.h
+#undef LOG_DEBUG
+#undef LOG_INFO
+#undef LOG_WARNING
+
+#include "hal/socket_hal.h"
+
+using ::aidl::android::hardware::bluetooth::socket::BnBluetoothSocketCallback;
+using ::aidl::android::hardware::bluetooth::socket::IBluetoothSocket;
+
+namespace bluetooth::hal {
+
+constexpr uint16_t kLeCocMtuMin = 23;
+constexpr uint16_t kLeCocMtuMax = 65535;
+
+class SocketAidlCallback : public BnBluetoothSocketCallback {
+  class : public hal::SocketHalCallback {
+  public:
+    void SocketOpenedComplete(uint64_t /* socket_id */,
+                              hal::SocketStatus /* status */) const override {
+      log::warn("Dropping SocketOpenedComplete event, since callback is not set");
+    }
+
+    void SocketClose(uint64_t /* socket_id */) const override {
+      log::warn("Dropping SocketClose event, since callback is not set");
+    }
+  } kNullCallbacks;
+
+public:
+  SocketAidlCallback() = default;
+
+  void SetCallback(hal::SocketHalCallback const* callback) {
+    log::assert_that(callback != nullptr, "callback != nullptr");
+    socket_hal_cb_ = callback;
+  }
+
+  ::ndk::ScopedAStatus openedComplete(int64_t socket_id,
+                                      ::aidl::android::hardware::bluetooth::socket::Status status,
+                                      const std::string& reason) override {
+    log::info("socket_id: {} status: {} reason: {}", socket_id, static_cast<int>(status), reason);
+    socket_hal_cb_->SocketOpenedComplete(
+            socket_id, status == ::aidl::android::hardware::bluetooth::socket::Status::SUCCESS
+                               ? hal::SocketStatus::SUCCESS
+                               : hal::SocketStatus::FAILURE);
+    return ::ndk::ScopedAStatus::ok();
+  }
+
+  ::ndk::ScopedAStatus close(int64_t socket_id, const std::string& reason) override {
+    log::info("socket_id: {} reason: {}", socket_id, reason);
+    socket_hal_cb_->SocketClose(socket_id);
+    return ::ndk::ScopedAStatus::ok();
+  }
+
+private:
+  hal::SocketHalCallback const* socket_hal_cb_ = &kNullCallbacks;
+};
+
+class SocketHalAndroid : public SocketHal {
+public:
+  bool IsBound() const { return socket_hal_instance_ != nullptr; }
+
+protected:
+  void ListDependencies(ModuleList* /*list*/) const {}
+
+  void Start() override {
+    std::string instance = std::string() + IBluetoothSocket::descriptor + "/default";
+    if (!AServiceManager_isDeclared(instance.c_str())) {
+      log::error("The service {} is not declared", instance);
+      return;
+    }
+
+    ::ndk::SpAIBinder binder(AServiceManager_waitForService(instance.c_str()));
+    socket_hal_instance_ = IBluetoothSocket::fromBinder(binder);
+
+    if (socket_hal_instance_ == nullptr) {
+      log::error("Failed to bind to the service {}", instance);
+      return;
+    }
+
+    socket_aidl_cb_ = ndk::SharedRefBase::make<SocketAidlCallback>();
+    ::ndk::ScopedAStatus status = socket_hal_instance_->registerCallback(socket_aidl_cb_);
+    if (!status.isOk()) {
+      log::error("registerCallback failure: {}", status.getDescription());
+      socket_hal_instance_ = nullptr;
+      return;
+    }
+
+    death_recipient_ =
+            ::ndk::ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new([](void* /* cookie*/) {
+              log::error("The Socket HAL service died.");
+              // At shutdown, sometimes the HAL service gets killed before Bluetooth.
+              std::this_thread::sleep_for(std::chrono::seconds(1));
+              log::fatal("Restarting Bluetooth after the socket HAL has died.");
+            }));
+
+    auto death_link = AIBinder_linkToDeath(socket_hal_instance_->asBinder().get(),
+                                           death_recipient_.get(), this);
+    log::assert_that(death_link == STATUS_OK,
+                     "Unable to set the death recipient for the Socket HAL");
+  }
+
+  void Stop() override {
+    if (IsBound()) {
+      auto death_unlink = AIBinder_unlinkToDeath(socket_hal_instance_->asBinder().get(),
+                                                 death_recipient_.get(), this);
+      if (death_unlink != STATUS_OK) {
+        log::error("Error unlinking death recipient from the Socket HAL");
+      }
+      socket_hal_instance_ = nullptr;
+    }
+  }
+
+  std::string ToString() const override { return std::string("SocketHalAndroid"); }
+
+  hal::SocketCapabilities GetSocketCapabilities() const override {
+    if (!IsBound()) {
+      return {};
+    }
+    ::aidl::android::hardware::bluetooth::socket::SocketCapabilities socket_capabilities;
+    ::ndk::ScopedAStatus status = socket_hal_instance_->getSocketCapabilities(&socket_capabilities);
+    if (!status.isOk()) {
+      log::info("Failed to get socket capabilities");
+      return {};
+    }
+    if (socket_capabilities.leCocCapabilities.numberOfSupportedSockets < 0) {
+      log::error("Invalid leCocCapabilities.numberOfSupportedSockets: {}",
+                 socket_capabilities.leCocCapabilities.numberOfSupportedSockets);
+      return {};
+    }
+    if (socket_capabilities.leCocCapabilities.numberOfSupportedSockets) {
+      if (socket_capabilities.leCocCapabilities.mtu < kLeCocMtuMin ||
+          socket_capabilities.leCocCapabilities.mtu > kLeCocMtuMax) {
+        log::error("Invalid leCocCapabilities.mtu: {}", socket_capabilities.leCocCapabilities.mtu);
+        return {};
+      }
+    }
+    log::info("le_coc_capabilities number_of_supported_sockets: {}, mtu: {}",
+              socket_capabilities.leCocCapabilities.numberOfSupportedSockets,
+              socket_capabilities.leCocCapabilities.mtu);
+    return hal::SocketCapabilities{
+            .le_coc_capabilities.number_of_supported_sockets =
+                    socket_capabilities.leCocCapabilities.numberOfSupportedSockets,
+            .le_coc_capabilities.mtu =
+                    static_cast<uint16_t>(socket_capabilities.leCocCapabilities.mtu)};
+  }
+
+  bool RegisterCallback(hal::SocketHalCallback const* callback) override {
+    if (!IsBound()) {
+      return false;
+    }
+    socket_aidl_cb_->SetCallback(callback);
+    return true;
+  }
+
+  bool Opened(const hal::SocketContext& context) const override {
+    if (!IsBound()) {
+      return false;
+    }
+    log::info("socket_id: {}, name: {}, acl_connection_handle: {}, hub_id: {}, endpoint_id: {}",
+              context.socket_id, context.name, context.acl_connection_handle,
+              context.endpoint_info.hub_id, context.endpoint_info.endpoint_id);
+    ::aidl::android::hardware::bluetooth::socket::SocketContext hal_context = {
+            .socketId = static_cast<int64_t>(context.socket_id),
+            .name = context.name,
+            .aclConnectionHandle = context.acl_connection_handle,
+            .endpointId.id = static_cast<int64_t>(context.endpoint_info.endpoint_id),
+            .endpointId.hubId = static_cast<int64_t>(context.endpoint_info.hub_id),
+    };
+    if (std::holds_alternative<hal::LeCocChannelInfo>(context.channel_info)) {
+      auto& le_coc_context = std::get<hal::LeCocChannelInfo>(context.channel_info);
+      hal_context.channelInfo = ::aidl::android::hardware::bluetooth::socket::LeCocChannelInfo(
+              le_coc_context.local_cid, le_coc_context.remote_cid, le_coc_context.psm,
+              le_coc_context.local_mtu, le_coc_context.remote_mtu, le_coc_context.local_mps,
+              le_coc_context.remote_mps, le_coc_context.initial_rx_credits,
+              le_coc_context.initial_tx_credits);
+      log::info(
+              "le_coc local_cid: {}, remote_cid: {}, psm: {}, local_mtu: {}, remote_mtu: {}, "
+              "local_mps: {}, remote_mps: {}, initial_rx_credits: {}, initial_tx_credits: {}",
+              le_coc_context.local_cid, le_coc_context.remote_cid, le_coc_context.psm,
+              le_coc_context.local_mtu, le_coc_context.remote_mtu, le_coc_context.local_mps,
+              le_coc_context.remote_mps, le_coc_context.initial_rx_credits,
+              le_coc_context.initial_tx_credits);
+    } else {
+      log::error("Unsupported protocol");
+      return false;
+    }
+    ::ndk::ScopedAStatus status = socket_hal_instance_->opened(hal_context);
+    if (!status.isOk()) {
+      log::error("Opened failure: {}", status.getDescription());
+      return false;
+    }
+    return true;
+  }
+
+  void Closed(uint64_t socket_id) const override {
+    if (!IsBound()) {
+      return;
+    }
+    log::info("socket_id: {}", socket_id);
+    ::ndk::ScopedAStatus status = socket_hal_instance_->closed(socket_id);
+    if (!status.isOk()) {
+      log::info("Closed failure: {}", status.getDescription());
+    }
+  }
+
+private:
+  std::shared_ptr<IBluetoothSocket> socket_hal_instance_;
+  std::shared_ptr<SocketAidlCallback> socket_aidl_cb_;
+  ::ndk::ScopedAIBinder_DeathRecipient death_recipient_;
+};
+
+const ModuleFactory SocketHal::Factory = ModuleFactory([]() { return new SocketHalAndroid(); });
+
+}  // namespace bluetooth::hal
diff --git a/system/gd/hal/socket_hal_host.cc b/system/gd/hal/socket_hal_host.cc
new file mode 100644
index 0000000..292428a
--- /dev/null
+++ b/system/gd/hal/socket_hal_host.cc
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "hal/socket_hal.h"
+
+namespace bluetooth::hal {
+
+class SocketHalHost : public SocketHal {
+protected:
+  void ListDependencies(ModuleList* /*list*/) const {}
+
+  void Start() override {}
+
+  void Stop() override { socket_hal_cb_ = nullptr; }
+
+  std::string ToString() const override { return std::string("SocketHalHost"); }
+
+  hal::SocketCapabilities GetSocketCapabilities() const override { return {}; }
+
+  bool RegisterCallback(hal::SocketHalCallback const* /*callback*/) override { return false; }
+
+  bool Opened(const hal::SocketContext& /*context*/) const override { return false; }
+
+  void Closed(uint64_t /*socket_id*/) const override {}
+
+private:
+  hal::SocketHalCallback* socket_hal_cb_;
+};
+
+const ModuleFactory SocketHal::Factory = ModuleFactory([]() { return new SocketHalHost(); });
+
+}  // namespace bluetooth::hal
diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h
index cfb42db..00af9b7 100644
--- a/system/gd/hci/acl_manager/le_impl.h
+++ b/system/gd/hci/acl_manager/le_impl.h
@@ -863,8 +863,7 @@
       address_with_type = AddressWithType();
     }
 
-    if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() &&
-        controller_->IsSupported(OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2) &&
+    if (controller_->IsRpaGenerationSupported() &&
         own_address_type != OwnAddressType::PUBLIC_DEVICE_ADDRESS) {
       log::info("Support RPA offload, set own address type RESOLVABLE_OR_RANDOM_ADDRESS");
       own_address_type = OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS;
diff --git a/system/gd/hci/controller.cc b/system/gd/hci/controller.cc
index 18743a1..eaba7da 100644
--- a/system/gd/hci/controller.cc
+++ b/system/gd/hci/controller.cc
@@ -43,8 +43,11 @@
 constexpr int kMaxEncryptionKeySize = 16;
 
 constexpr bool kDefaultVendorCapabilitiesEnabled = true;
+constexpr bool kDefaultRpaOffload = false;
+
 static const std::string kPropertyVendorCapabilitiesEnabled =
         "bluetooth.core.le.vendor_capabilities.enabled";
+static const std::string kPropertyRpaOffload = "bluetooth.core.le.rpa_offload";
 
 using os::Handler;
 
@@ -1543,6 +1546,15 @@
   }
 }
 
+bool Controller::IsRpaGenerationSupported(void) const {
+  static const bool rpa_supported =
+          com::android::bluetooth::flags::rpa_offload_to_bt_controller() &&
+          os::GetSystemPropertyBool(kPropertyRpaOffload, kDefaultRpaOffload) &&
+          IsSupported(OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2);
+
+  return rpa_supported;
+}
+
 const ModuleFactory Controller::Factory = ModuleFactory([]() { return new Controller(); });
 
 void Controller::ListDependencies(ModuleList* list) const {
diff --git a/system/gd/hci/controller.h b/system/gd/hci/controller.h
index dcf0b10e..7060995 100644
--- a/system/gd/hci/controller.h
+++ b/system/gd/hci/controller.h
@@ -217,6 +217,8 @@
 
   static uint64_t MaskLeEventMask(HciVersion version, uint64_t mask);
 
+  virtual bool IsRpaGenerationSupported(void) const override;
+
 protected:
   void ListDependencies(ModuleList* list) const override;
 
diff --git a/system/gd/hci/controller_interface.h b/system/gd/hci/controller_interface.h
index 3dd3cd0..a9ebecd 100644
--- a/system/gd/hci/controller_interface.h
+++ b/system/gd/hci/controller_interface.h
@@ -215,6 +215,8 @@
   virtual VendorCapabilities GetVendorCapabilities() const = 0;
 
   virtual bool IsSupported(OpCode op_code) const = 0;
+
+  virtual bool IsRpaGenerationSupported(void) const = 0;
 };
 
 }  // namespace hci
diff --git a/system/gd/hci/controller_interface_mock.h b/system/gd/hci/controller_interface_mock.h
index 6c8a281..36bc50a 100644
--- a/system/gd/hci/controller_interface_mock.h
+++ b/system/gd/hci/controller_interface_mock.h
@@ -149,6 +149,7 @@
   MOCK_METHOD(std::vector<uint8_t>, GetLocalSupportedBrEdrCodecIds, (), (const));
   MOCK_METHOD(VendorCapabilities, GetVendorCapabilities, (), (const));
   MOCK_METHOD(bool, IsSupported, (OpCode op_code), (const));
+  MOCK_METHOD(bool, IsRpaGenerationSupported, (), (const));
 
   MOCK_METHOD(uint32_t, GetDabSupportedCodecs, (), (const));
   MOCK_METHOD((const std::array<DynamicAudioBufferCodecCapability, 32>&), GetDabCodecCapabilities,
diff --git a/system/gd/hci/controller_mock.h b/system/gd/hci/controller_mock.h
index 1c05ad9..fd3a705 100644
--- a/system/gd/hci/controller_mock.h
+++ b/system/gd/hci/controller_mock.h
@@ -149,6 +149,7 @@
   MOCK_METHOD(std::vector<uint8_t>, GetLocalSupportedBrEdrCodecIds, (), (const));
   MOCK_METHOD(VendorCapabilities, GetVendorCapabilities, (), (const));
   MOCK_METHOD(bool, IsSupported, (OpCode op_code), (const));
+  MOCK_METHOD(bool, IsRpaGenerationSupported, (), (const));
 
   MOCK_METHOD(uint32_t, GetDabSupportedCodecs, (), (const));
   MOCK_METHOD((const std::array<DynamicAudioBufferCodecCapability, 32>&), GetDabCodecCapabilities,
diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc
index 256276e..bddbcaf 100644
--- a/system/gd/hci/le_address_manager.cc
+++ b/system/gd/hci/le_address_manager.cc
@@ -157,13 +157,11 @@
         log::info("minimum_rotation_time_={}ms, maximum_rotation_time_={}ms",
                   minimum_rotation_time_.count(), maximum_rotation_time_.count());
       }
-      if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() &&
-          controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) {
+      if (controller_->IsRpaGenerationSupported()) {
         auto min_seconds = std::chrono::duration_cast<std::chrono::seconds>(minimum_rotation_time_);
         auto max_seconds = std::chrono::duration_cast<std::chrono::seconds>(maximum_rotation_time_);
-        log::info(
-                "Support RPA offload, set min_seconds={}s, max_seconds={}s",
-                min_seconds.count(), max_seconds.count());
+        log::info("Support RPA offload, set min_seconds={}s, max_seconds={}s", min_seconds.count(),
+                  max_seconds.count());
         /* Default to 7 minutes minimum, 15 minutes maximum for random address refreshing;
          * device can override. */
         auto packet = hci::LeSetResolvablePrivateAddressTimeoutV2Builder::Create(
@@ -223,13 +221,11 @@
       maximum_rotation_time_ = maximum_rotation_time;
       log::info("minimum_rotation_time_={}ms, maximum_rotation_time_={}ms",
                 minimum_rotation_time_.count(), maximum_rotation_time_.count());
-      if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() &&
-          controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) {
+      if (controller_->IsRpaGenerationSupported()) {
         auto min_seconds = std::chrono::duration_cast<std::chrono::seconds>(minimum_rotation_time_);
         auto max_seconds = std::chrono::duration_cast<std::chrono::seconds>(maximum_rotation_time_);
-        log::info(
-                "Support RPA offload, set min_seconds={}s, max_seconds={}s",
-                min_seconds.count(), max_seconds.count());
+        log::info("Support RPA offload, set min_seconds={}s, max_seconds={}s", min_seconds.count(),
+                  max_seconds.count());
         /* Default to 7 minutes minimum, 15 minutes maximum for random address refreshing;
          * device can override. */
         auto packet = hci::LeSetResolvablePrivateAddressTimeoutV2Builder::Create(
@@ -269,8 +265,7 @@
   } else if (address_policy_ == AddressPolicy::USE_RESOLVABLE_ADDRESS ||
              address_policy_ == AddressPolicy::USE_NON_RESOLVABLE_ADDRESS) {
     if (registered_clients_.size() == 1) {
-      if (!com::android::bluetooth::flags::rpa_offload_to_bt_controller() ||
-          !controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) {
+      if (!controller_->IsRpaGenerationSupported()) {
         schedule_rotate_random_address();
         log::info("Scheduled address rotation for first client registered");
       }
diff --git a/system/gd/hci/le_address_manager_test.cc b/system/gd/hci/le_address_manager_test.cc
index 7e5c679..33efcb5 100644
--- a/system/gd/hci/le_address_manager_test.cc
+++ b/system/gd/hci/le_address_manager_test.cc
@@ -18,9 +18,9 @@
 
 #include <gtest/gtest.h>
 
+#include "hci/controller.h"
 #include "hci/hci_layer_fake.h"
 #include "hci/octets.h"
-#include "hci/controller.h"
 #include "packet/raw_builder.h"
 
 using ::bluetooth::hci::Octet16;
diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc
index e40b253..240f8f5 100644
--- a/system/gd/hci/le_advertising_manager.cc
+++ b/system/gd/hci/le_advertising_manager.cc
@@ -658,9 +658,7 @@
       // but we only rotate if the AdvertiserAddressType is non-public
       // or non-rpa requested by leaudio(since static random addresses don't rotate)
       if (advertising_sets_[id].address_type != AdvertiserAddressType::PUBLIC &&
-          !leaudio_requested_nrpa &&
-          (!com::android::bluetooth::flags::rpa_offload_to_bt_controller() ||
-           !controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2))) {
+          !leaudio_requested_nrpa && (!controller_->IsRpaGenerationSupported())) {
         // start timer for random address
         log::info("Scheduling address rotation for advertiser_id={}", id);
         if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) {
@@ -906,8 +904,7 @@
     auto own_address_type = static_cast<OwnAddressType>(
             advertising_sets_[advertiser_id].current_address.GetAddressType());
 
-    if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() &&
-        controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2) &&
+    if (controller_->IsRpaGenerationSupported() &&
         own_address_type != OwnAddressType::PUBLIC_DEVICE_ADDRESS) {
       log::info("Support RPA offload, set own address type RESOLVABLE_OR_RANDOM_ADDRESS");
       own_address_type = OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS;
diff --git a/system/gd/hci/le_scanning_manager.cc b/system/gd/hci/le_scanning_manager.cc
index 5934cf7..e5600d2 100644
--- a/system/gd/hci/le_scanning_manager.cc
+++ b/system/gd/hci/le_scanning_manager.cc
@@ -466,8 +466,7 @@
     stop_scan();
 
     if (le_address_manager_->GetAddressPolicy() != LeAddressManager::USE_PUBLIC_ADDRESS) {
-      if (com::android::bluetooth::flags::rpa_offload_to_bt_controller() &&
-          controller_->IsSupported(hci::OpCode::LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT_V2)) {
+      if (controller_->IsRpaGenerationSupported()) {
         log::info("Support RPA offload, set own address type RESOLVABLE_OR_RANDOM_ADDRESS");
         own_address_type_ = OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS;
       } else {
diff --git a/system/gd/lpp/Android.bp b/system/gd/lpp/Android.bp
new file mode 100644
index 0000000..2be7b85
--- /dev/null
+++ b/system/gd/lpp/Android.bp
@@ -0,0 +1,15 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_bt_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_bt_license"],
+}
+
+filegroup {
+    name: "BluetoothLppOffloadSources",
+    srcs: [
+        "lpp_offload_manager.cc",
+    ],
+}
diff --git a/system/gd/lpp/BUILD.gn b/system/gd/lpp/BUILD.gn
new file mode 100644
index 0000000..b773001
--- /dev/null
+++ b/system/gd/lpp/BUILD.gn
@@ -0,0 +1,31 @@
+#
+#  Copyright 2024 Google, Inc.
+#
+#  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.
+
+source_set("BluetoothLppOffloadSources") {
+  sources = [
+    "lpp_offload_manager.cc",
+  ]
+
+  include_dirs = [ "//bt/system/gd" ]
+
+  deps = [
+    "//bt/system/gd:gd_default_deps",
+  ]
+
+  configs += [
+    "//bt/system:target_defaults",
+    "//bt/system/log:log_defaults",
+  ]
+}
diff --git a/system/gd/lpp/lpp_offload_interface.h b/system/gd/lpp/lpp_offload_interface.h
new file mode 100644
index 0000000..388466f
--- /dev/null
+++ b/system/gd/lpp/lpp_offload_interface.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "hal/socket_hal.h"
+
+namespace bluetooth::lpp {
+
+/**
+ * Interface to low-power processors (LPPs) for supporting LPP offload features.
+ *
+ * This interface allows inheritance from multiple offload HAL interfaces, enabling a unified
+ * offload function management approach through a single interface accessible from the upper layer.
+ */
+class LppOffloadInterface {
+public:
+  LppOffloadInterface() = default;
+
+  virtual ~LppOffloadInterface() = default;
+
+  LppOffloadInterface(const LppOffloadInterface&) = delete;
+
+  LppOffloadInterface& operator=(const LppOffloadInterface&) = delete;
+
+  /**
+   * Registers a socket hal callback function to receive asynchronous events from socket HAL.
+   *
+   * The provided callback function must be executed on the main thread.
+   *
+   * @param callback A pointer to the callback function. Must not be nullptr and must have static
+   * lifetime.
+   * @return True if the callback was successfully registered, false otherwise.
+   */
+  virtual bool RegisterSocketHalCallback(hal::SocketHalCallback* callbacks) = 0;
+
+  /**
+   * Retrieves the supported offload socket capabilities.
+   *
+   * @return Supported socket capabilities
+   */
+  virtual hal::SocketCapabilities GetSocketCapabilities() const = 0;
+
+  /**
+   * Notifies the socket HAL that the socket has been opened.
+   *
+   * If this method returns true, SocketHalCallback.SocketOpenedComplete() shall be called to
+   * indicate the result of this operation.
+   *
+   * @param context Socket context including socket ID, channel, hub, and endpoint info
+   * @return True if calling this method was successful, false otherwise
+   */
+  virtual bool SocketOpened(const hal::SocketContext& context) = 0;
+
+  /**
+   * Notifies the socket HAL that the socket has been closed.
+   *
+   * @param socket_id Identifier assigned to the socket by the host stack
+   */
+  virtual void SocketClosed(uint64_t socket_id) = 0;
+};
+
+}  // namespace bluetooth::lpp
diff --git a/system/gd/lpp/lpp_offload_interface_mock.h b/system/gd/lpp/lpp_offload_interface_mock.h
new file mode 100644
index 0000000..03cd5f4
--- /dev/null
+++ b/system/gd/lpp/lpp_offload_interface_mock.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "lpp/lpp_offload_interface.h"
+
+// Unit test interfaces
+namespace bluetooth::lpp::testing {
+
+class MockLppOffloadInterface : public LppOffloadInterface {
+public:
+  MOCK_METHOD(bool, RegisterSocketHalCallback, (hal::SocketHalCallback*), (override));
+  MOCK_METHOD(hal::SocketCapabilities, GetSocketCapabilities, (), (const override));
+  MOCK_METHOD(bool, SocketOpened, (const hal::SocketContext&), (override));
+  MOCK_METHOD(void, SocketClosed, (uint64_t), (override));
+};
+
+}  // namespace bluetooth::lpp::testing
diff --git a/system/gd/lpp/lpp_offload_manager.cc b/system/gd/lpp/lpp_offload_manager.cc
new file mode 100644
index 0000000..c5c1fb2
--- /dev/null
+++ b/system/gd/lpp/lpp_offload_manager.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "lpp_offload_manager.h"
+
+#include <bluetooth/log.h>
+
+#include <string>
+
+#include "hal/socket_hal.h"
+#include "module.h"
+#include "os/handler.h"
+#include "os/system_properties.h"
+
+namespace bluetooth::lpp {
+
+const ModuleFactory LppOffloadManager::Factory =
+        ModuleFactory([]() { return new LppOffloadManager(); });
+
+struct LppOffloadManager::impl {
+  ~impl() {}
+
+  void start(os::Handler* handler, hal::SocketHal* socket_hal) {
+    log::info("");
+    handler_ = handler;
+    socket_hal_ = socket_hal;
+    socket_capabilities_ = socket_hal_->GetSocketCapabilities();
+  }
+
+  void stop() {
+    log::info("");
+    socket_capabilities_ = {};
+  }
+
+  bool register_socket_hal_callbacks(hal::SocketHalCallback* callbacks) {
+    log::info("");
+    return socket_hal_->RegisterCallback(callbacks);
+  }
+
+  hal::SocketCapabilities get_socket_capabilities() const {
+    log::info("");
+    return socket_capabilities_;
+  }
+
+  bool socket_opened(const hal::SocketContext& context) {
+    log::info("socket_id: {}", context.socket_id);
+    return socket_hal_->Opened(context);
+  }
+
+  void socket_closed(uint64_t socket_id) {
+    log::info("socket_id: {}", socket_id);
+    return socket_hal_->Closed(socket_id);
+  }
+
+  os::Handler* handler_;
+  hal::SocketHal* socket_hal_;
+  hal::SocketCapabilities socket_capabilities_;
+};
+
+LppOffloadManager::LppOffloadManager() { pimpl_ = std::make_unique<impl>(); }
+
+LppOffloadManager::~LppOffloadManager() = default;
+
+void LppOffloadManager::ListDependencies(ModuleList* list) const { list->add<hal::SocketHal>(); }
+
+void LppOffloadManager::Start() { pimpl_->start(GetHandler(), GetDependency<hal::SocketHal>()); }
+
+void LppOffloadManager::Stop() { pimpl_->stop(); }
+
+std::string LppOffloadManager::ToString() const { return "Low Power Processor Offload Manager"; }
+
+bool LppOffloadManager::RegisterSocketHalCallback(hal::SocketHalCallback* callbacks) {
+  return pimpl_->register_socket_hal_callbacks(callbacks);
+}
+
+hal::SocketCapabilities LppOffloadManager::GetSocketCapabilities() const {
+  return pimpl_->get_socket_capabilities();
+}
+
+bool LppOffloadManager::SocketOpened(const hal::SocketContext& context) {
+  return pimpl_->socket_opened(context);
+}
+
+void LppOffloadManager::SocketClosed(uint64_t socket_id) {
+  CallOn(pimpl_.get(), &impl::socket_closed, socket_id);
+}
+
+}  // namespace bluetooth::lpp
diff --git a/system/gd/lpp/lpp_offload_manager.h b/system/gd/lpp/lpp_offload_manager.h
new file mode 100644
index 0000000..90097f7
--- /dev/null
+++ b/system/gd/lpp/lpp_offload_manager.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <bluetooth/log.h>
+
+#include <memory>
+#include <string>
+
+#include "lpp_offload_interface.h"
+#include "module.h"
+
+namespace bluetooth::lpp {
+
+class LppOffloadManager : public bluetooth::Module, public LppOffloadInterface {
+public:
+  LppOffloadManager();
+
+  LppOffloadManager(const LppOffloadManager&) = delete;
+
+  LppOffloadManager& operator=(const LppOffloadManager&) = delete;
+
+  ~LppOffloadManager();
+
+  bool RegisterSocketHalCallback(hal::SocketHalCallback* callbacks) override;
+
+  hal::SocketCapabilities GetSocketCapabilities() const override;
+
+  bool SocketOpened(const hal::SocketContext& context) override;
+
+  void SocketClosed(uint64_t socket_id) override;
+
+  static const ModuleFactory Factory;
+
+protected:
+  void ListDependencies(ModuleList* list) const override;
+
+  void Start() override;
+
+  void Stop() override;
+
+  std::string ToString() const override;
+
+private:
+  struct impl;
+  std::unique_ptr<impl> pimpl_;
+};
+
+}  // namespace bluetooth::lpp
diff --git a/system/gd/os/rand.h b/system/gd/os/rand.h
index 04e29d3..03f29bc 100644
--- a/system/gd/os/rand.h
+++ b/system/gd/os/rand.h
@@ -40,5 +40,12 @@
   return ret;
 }
 
+inline uint64_t GenerateRandomUint64() {
+  uint64_t ret{};
+  log::assert_that(RAND_bytes((uint8_t*)(&ret), sizeof(uint64_t)) == 1,
+                   "assert failed: RAND_bytes((uint8_t*)(&ret), sizeof(uint64_t)) == 1");
+  return ret;
+}
+
 }  // namespace os
 }  // namespace bluetooth
diff --git a/system/gd/rust/topshim/btav/btav_shim.cc b/system/gd/rust/topshim/btav/btav_shim.cc
index eed0d54..ac173ac 100644
--- a/system/gd/rust/topshim/btav/btav_shim.cc
+++ b/system/gd/rust/topshim/btav/btav_shim.cc
@@ -54,7 +54,6 @@
   void GetAddressedPlayer(GetAddressedPlayerCallback cb) override { cb.Run(currentPlayer_); }
 
   void SetBrowsedPlayer([[maybe_unused]] uint16_t player_id,
-                        [[maybe_unused]] std::string current_path,
                         [[maybe_unused]] SetBrowsedPlayerCallback browse_cb) override {}
 
   void SetAddressedPlayer([[maybe_unused]] uint16_t player_id,
diff --git a/system/gd/rust/topshim/macros/src/lib.rs b/system/gd/rust/topshim/macros/src/lib.rs
index 65aa24d..78fc677 100644
--- a/system/gd/rust/topshim/macros/src/lib.rs
+++ b/system/gd/rust/topshim/macros/src/lib.rs
@@ -103,17 +103,13 @@
         let ident = format_ident!("_{}", i);
         params.extend(quote! { #ident: #start, });
 
-        match end {
-            Some(v) => {
-                // Argument needs an into translation if it doesn't match the start
-                if start != v {
-                    args.extend(quote! { #end::from(#ident), });
-                } else {
-                    args.extend(quote! {#ident,});
-                }
+        if let Some(v) = end {
+            // Argument needs an into translation if it doesn't match the start
+            if start != v {
+                args.extend(quote! { #end::from(#ident), });
+            } else {
+                args.extend(quote! {#ident,});
             }
-            // If there's no end type, just consume it instead.
-            None => (),
         }
     }
 
diff --git a/system/gd/rust/topshim/src/profiles/socket.rs b/system/gd/rust/topshim/src/profiles/socket.rs
index 49a0f15..d8cfd45 100644
--- a/system/gd/rust/topshim/src/profiles/socket.rs
+++ b/system/gd/rust/topshim/src/profiles/socket.rs
@@ -164,6 +164,12 @@
         let name = CString::new(service_name).expect("Service name has null in it.");
         let name_ptr = LTCheckedPtr::from(&name);
 
+        let data_path: u32 = 0;
+        let sock_name = CString::new("test").expect("Socket name has null in it");
+        let hub_id: u64 = 0;
+        let endpoint_id: u64 = 0;
+        let max_rx_packet_size: i32 = 0;
+
         let status: BtStatus = ccall!(
             self,
             listen,
@@ -173,7 +179,12 @@
             channel,
             sockfd_ptr.into(),
             flags,
-            calling_uid
+            calling_uid,
+            data_path,
+            sock_name.as_ptr(),
+            hub_id,
+            endpoint_id,
+            max_rx_packet_size
         )
         .into();
 
@@ -194,6 +205,12 @@
         let uuid_ptr = LTCheckedPtr::from(&service_uuid);
         let addr_ptr = LTCheckedPtr::from_ref(&addr);
 
+        let data_path: u32 = 0;
+        let sock_name = CString::new("test").expect("Socket name has null in it");
+        let hub_id: u64 = 0;
+        let endpoint_id: u64 = 0;
+        let max_rx_packet_size: i32 = 0;
+
         let status: BtStatus = ccall!(
             self,
             connect,
@@ -203,7 +220,12 @@
             channel,
             sockfd_ptr.into(),
             flags,
-            calling_uid
+            calling_uid,
+            data_path,
+            sock_name.as_ptr(),
+            hub_id,
+            endpoint_id,
+            max_rx_packet_size
         )
         .into();
 
@@ -274,6 +296,7 @@
             max_rx_packet_size: 17_u16,
             conn_uuid_lsb: 0x0000113500001135_u64,
             conn_uuid_msb: 0x1135000011350000_u64,
+            socket_id: 0x1135113511351135_u64,
         };
         // SAFETY: The sock_connect_signal_t type has size CONNECT_COMPLETE_SIZE,
         // and has no padding, so it's safe to convert it to a byte array.
diff --git a/system/include/hardware/avrcp/avrcp.h b/system/include/hardware/avrcp/avrcp.h
index 4a5d1a7..a1a44c1 100644
--- a/system/include/hardware/avrcp/avrcp.h
+++ b/system/include/hardware/avrcp/avrcp.h
@@ -131,9 +131,8 @@
   virtual void GetAddressedPlayer(GetAddressedPlayerCallback addressed_player) = 0;
 
   using SetBrowsedPlayerCallback =
-          base::Callback<void(bool success, std::string current_path, uint32_t num_items)>;
-  virtual void SetBrowsedPlayer(uint16_t player_id, std::string current_path,
-                                SetBrowsedPlayerCallback browse_cb) = 0;
+          base::Callback<void(bool success, std::string root_id, uint32_t num_items)>;
+  virtual void SetBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCallback browse_cb) = 0;
 
   using SetAddressedPlayerCallback = base::Callback<void(uint16_t)>;
   virtual void SetAddressedPlayer(uint16_t player_id, SetAddressedPlayerCallback new_player) = 0;
diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h
index 02a8f51..1be80e1 100644
--- a/system/include/hardware/bluetooth.h
+++ b/system/include/hardware/bluetooth.h
@@ -238,6 +238,10 @@
   bool le_channel_sounding_supported;
 } bt_local_le_features_t;
 
+typedef struct {
+  uint8_t number_of_supported_offloaded_le_coc_sockets;
+} bt_lpp_offload_features_t;
+
 /** Bluetooth Vendor and Product ID info */
 typedef struct {
   uint8_t vendor_id_src;
@@ -415,6 +419,13 @@
    */
   BT_PROPERTY_REMOTE_MAX_SESSION_KEY_SIZE,
 
+  /**
+   * Description - Low power processor offload features
+   * Access mode - GET.
+   * Data Type   - bt_lpp_offload_features_t.
+   */
+  BT_PROPERTY_LPP_OFFLOAD_FEATURES,
+
   BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF,
 } bt_property_type_t;
 
diff --git a/system/include/hardware/bt_sock.h b/system/include/hardware/bt_sock.h
index c7fff44..0436fad 100644
--- a/system/include/hardware/bt_sock.h
+++ b/system/include/hardware/bt_sock.h
@@ -38,6 +38,18 @@
   BTSOCK_L2CAP_LE = 4
 } btsock_type_t;
 
+/**
+ * Data path used for Bluetooth socket communication.
+ *
+ * NOTE: The values must be same as:
+ *    - BluetoothSocketSettings.DATA_PATH_NO_OFFLOAD = 0
+ *    - BluetoothSocketSettings.DATA_PATH_HARDWARE_OFFLOAD = 1
+ */
+typedef enum {
+  BTSOCK_DATA_PATH_NO_OFFLOAD = 0,
+  BTSOCK_DATA_PATH_HARDWARE_OFFLOAD = 1,
+} btsock_data_path_t;
+
 /** Represents the standard BT SOCKET interface. */
 typedef struct {
   int16_t size;
@@ -56,9 +68,17 @@
   // The connection uuid. (L2CAP only)
   uint64_t conn_uuid_lsb;
   uint64_t conn_uuid_msb;
+
+  // Socket ID in connected state
+  uint64_t socket_id;
 } __attribute__((packed)) sock_connect_signal_t;
 
 typedef struct {
+  uint16_t size;
+  uint16_t is_accepting;
+} __attribute__((packed)) sock_accept_signal_t;
+
+typedef struct {
   /** set to size of this struct*/
   size_t size;
 
@@ -73,7 +93,8 @@
    */
   bt_status_t (*listen)(btsock_type_t type, const char* service_name,
                         const bluetooth::Uuid* service_uuid, int channel, int* sock_fd, int flags,
-                        int callingUid);
+                        int callingUid, btsock_data_path_t data_path, const char* socket_name,
+                        uint64_t hub_id, uint64_t endpoint_id, int max_rx_packet_size);
 
   /**
    * Connect to a RFCOMM UUID channel of remote device, It returns the socket fd
@@ -83,7 +104,9 @@
    * purposes.
    */
   bt_status_t (*connect)(const RawAddress* bd_addr, btsock_type_t type, const bluetooth::Uuid* uuid,
-                         int channel, int* sock_fd, int flags, int callingUid);
+                         int channel, int* sock_fd, int flags, int callingUid,
+                         btsock_data_path_t data_path, const char* socket_name, uint64_t hub_id,
+                         uint64_t endpoint_id, int max_rx_packet_size);
 
   /**
    * Set the LE Data Length value to this connected peer to the
@@ -128,6 +151,9 @@
 namespace std {
 template <>
 struct formatter<btsock_type_t> : enum_formatter<btsock_type_t> {};
+
+template <>
+struct formatter<btsock_data_path_t> : enum_formatter<btsock_data_path_t> {};
 }  // namespace std
 
 #endif  // __has_include(<bluetooth/log.h>)
diff --git a/system/main/shim/BUILD.gn b/system/main/shim/BUILD.gn
index 5e73e0f..ff7bdae 100644
--- a/system/main/shim/BUILD.gn
+++ b/system/main/shim/BUILD.gn
@@ -31,6 +31,7 @@
   deps = [
     "//bt/system/gd/common:BluetoothCommonSources",
     "//bt/system/gd/hci:BluetoothHciSources",
+    "//bt/system/gd/lpp:BluetoothLppOffloadSources",
     "//bt/system/gd/os:BluetoothOsSources_linux_generic",
     "//bt/system/gd/packet:BluetoothPacketSources",
     "//bt/system/gd/rust/topshim:libbluetooth_topshim",
@@ -75,6 +76,7 @@
     "//bt/sysprop:libcom.android.sysprop.bluetooth",
     "//bt/system/gd/common:BluetoothCommonSources",
     "//bt/system/gd/hci:BluetoothHciSources",
+    "//bt/system/gd/lpp:BluetoothLppOffloadSources",
     "//bt/system/gd/os:BluetoothOsSources_linux_generic",
     "//bt/system/gd/packet:BluetoothPacketSources",
     "//bt/system/gd/rust/topshim:libbluetooth_topshim",
diff --git a/system/main/shim/entry.cc b/system/main/shim/entry.cc
index a1c61b7..8f0c6c6 100644
--- a/system/main/shim/entry.cc
+++ b/system/main/shim/entry.cc
@@ -26,6 +26,7 @@
 #include "hci/le_scanning_manager.h"
 #include "hci/msft.h"
 #include "hci/remote_name_request.h"
+#include "lpp/lpp_offload_manager.h"
 #include "main/shim/stack.h"
 #include "metrics/counter_metrics.h"
 #include "os/handler.h"
@@ -67,6 +68,10 @@
   return Stack::GetInstance()->GetStackManager()->GetInstance<hal::SnoopLogger>();
 }
 
+lpp::LppOffloadInterface* GetLppOffloadManager() {
+  return Stack::GetInstance()->GetStackManager()->GetInstance<lpp::LppOffloadManager>();
+}
+
 storage::StorageModule* GetStorage() {
   return Stack::GetInstance()->GetStackManager()->GetInstance<storage::StorageModule>();
 }
diff --git a/system/main/shim/entry.h b/system/main/shim/entry.h
index e50cbc4..7e97c87 100644
--- a/system/main/shim/entry.h
+++ b/system/main/shim/entry.h
@@ -48,6 +48,10 @@
 class MsftExtensionManager;
 }  // namespace hci
 
+namespace lpp {
+class LppOffloadInterface;
+}
+
 namespace metrics {
 class CounterMetrics;
 }
@@ -69,6 +73,7 @@
 hci::RemoteNameRequestModule* GetRemoteNameRequest();
 hci::DistanceMeasurementManager* GetDistanceMeasurementManager();
 hci::LeScanningManager* GetScanning();
+lpp::LppOffloadInterface* GetLppOffloadManager();
 hal::SnoopLogger* GetSnoopLogger();
 storage::StorageModule* GetStorage();
 hci::AclManager* GetAclManager();
diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc
index 73526bc..82879e2 100644
--- a/system/main/shim/stack.cc
+++ b/system/main/shim/stack.cc
@@ -19,6 +19,7 @@
 #include "main/shim/stack.h"
 
 #include <bluetooth/log.h>
+#include <com_android_bluetooth_flags.h>
 #include <fcntl.h>
 #include <unistd.h>
 
@@ -36,6 +37,7 @@
 #include "hci/le_scanning_manager.h"
 #include "hci/msft.h"
 #include "hci/remote_name_request.h"
+#include "lpp/lpp_offload_manager.h"
 #include "main/shim/acl.h"
 #include "main/shim/acl_interface.h"
 #include "main/shim/distance_measurement_manager.h"
@@ -74,6 +76,10 @@
 
 #if TARGET_FLOSS
   modules.add<sysprops::SyspropsModule>();
+#else
+  if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3286716
+    modules.add<lpp::LppOffloadManager>();
+  }
 #endif
   modules.add<metrics::CounterMetrics>();
   modules.add<hal::HciHal>();
diff --git a/system/profile/avrcp/device.cc b/system/profile/avrcp/device.cc
index 1d0b325..b7bd001 100644
--- a/system/profile/avrcp/device.cc
+++ b/system/profile/avrcp/device.cc
@@ -1518,14 +1518,14 @@
   }
 
   log::verbose("player_id={}", pkt->GetPlayerId());
-  media_interface_->SetBrowsedPlayer(pkt->GetPlayerId(), CurrentFolder(),
+  media_interface_->SetBrowsedPlayer(pkt->GetPlayerId(),
                                      base::Bind(&Device::SetBrowsedPlayerResponse,
                                                 weak_ptr_factory_.GetWeakPtr(), label, pkt));
 }
 
 void Device::SetBrowsedPlayerResponse(uint8_t label, std::shared_ptr<SetBrowsedPlayerRequest> pkt,
-                                      bool success, std::string current_path, uint32_t num_items) {
-  log::verbose("success={} current_path=\"{}\" num_items={}", success, current_path, num_items);
+                                      bool success, std::string root_id, uint32_t num_items) {
+  log::verbose("success={} root_id=\"{}\" num_items={}", success, root_id, num_items);
 
   if (!success) {
     auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(Status::INVALID_PLAYER_ID, 0x0000,
@@ -1542,18 +1542,14 @@
     return;
   }
 
-  // SetBrowsedPlayer can be called to retrieve the current path
-  // and to verify that the player is still present, so we need to
-  // keep current_path_ as is if the player is already the one browsed.
-  // Otherwise, the current_path in the callback will contain the root id.
-  if (pkt->GetPlayerId() != curr_browsed_player_id_) {
-    curr_browsed_player_id_ = pkt->GetPlayerId();
-    current_path_ = std::stack<std::string>();
-    current_path_.push(current_path);
-  }
+  curr_browsed_player_id_ = pkt->GetPlayerId();
+
+  // Clear the path and push the new root.
+  current_path_ = std::stack<std::string>();
+  current_path_.push(root_id);
 
   auto response = SetBrowsedPlayerResponseBuilder::MakeBuilder(Status::NO_ERROR, 0x0000, num_items,
-                                                               0, current_path);
+                                                               0, root_id);
   send_message(label, true, std::move(response));
 }
 
diff --git a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
index 3bfce3f..5f69a6a 100644
--- a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
+++ b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
@@ -144,9 +144,8 @@
     addressed_player.Run(currentPlayer);
   }
   using SetBrowsedPlayerCallback =
-          base::Callback<void(bool success, std::string current_path, uint32_t num_items)>;
-  void SetBrowsedPlayer(uint16_t player_id, std::string /* current_path */,
-                        SetBrowsedPlayerCallback browse_cb) {
+          base::Callback<void(bool success, std::string root_id, uint32_t num_items)>;
+  void SetBrowsedPlayer(uint16_t player_id, SetBrowsedPlayerCallback browse_cb) {
     std::string rootId = mFdp->ConsumeRandomLengthString(kMaxLen);
     uint32_t numItems = mFdp->ConsumeIntegral<uint32_t>();
     browse_cb.Run(player_id, rootId, numItems);
diff --git a/system/profile/avrcp/tests/avrcp_device_test.cc b/system/profile/avrcp/tests/avrcp_device_test.cc
index 204a1e7..a33046f 100644
--- a/system/profile/avrcp/tests/avrcp_device_test.cc
+++ b/system/profile/avrcp/tests/avrcp_device_test.cc
@@ -1130,11 +1130,11 @@
 
   test_device->RegisterInterfaces(&interface, &a2dp_interface, nullptr, nullptr);
 
-  EXPECT_CALL(interface, SetBrowsedPlayer(_, "", _))
+  EXPECT_CALL(interface, SetBrowsedPlayer(_, _))
           .Times(3)
-          .WillOnce(InvokeCb<2>(true, "", 0))
-          .WillOnce(InvokeCb<2>(false, "", 0))
-          .WillOnce(InvokeCb<2>(true, "", 2));
+          .WillOnce(InvokeCb<1>(true, "", 0))
+          .WillOnce(InvokeCb<1>(false, "", 0))
+          .WillOnce(InvokeCb<1>(true, "", 2));
 
   auto not_browsable_rsp = SetBrowsedPlayerResponseBuilder::MakeBuilder(
           Status::PLAYER_NOT_BROWSABLE, 0x0000, 0, 0, "");
diff --git a/system/profile/avrcp/tests/avrcp_test_helper.h b/system/profile/avrcp/tests/avrcp_test_helper.h
index 0c5199a..ea6ed4d 100644
--- a/system/profile/avrcp/tests/avrcp_test_helper.h
+++ b/system/profile/avrcp/tests/avrcp_test_helper.h
@@ -45,8 +45,7 @@
   MOCK_METHOD1(GetMediaPlayerList, void(MediaInterface::MediaListCallback));
   MOCK_METHOD1(GetAddressedPlayer, void(MediaInterface::GetAddressedPlayerCallback));
   MOCK_METHOD3(GetFolderItems, void(uint16_t, std::string, MediaInterface::FolderItemsCallback));
-  MOCK_METHOD3(SetBrowsedPlayer,
-               void(uint16_t, std::string, MediaInterface::SetBrowsedPlayerCallback));
+  MOCK_METHOD2(SetBrowsedPlayer, void(uint16_t, MediaInterface::SetBrowsedPlayerCallback));
   MOCK_METHOD2(SetAddressedPlayer, void(uint16_t, MediaInterface::SetAddressedPlayerCallback));
   MOCK_METHOD3(PlayItem, void(uint16_t, bool, std::string));
   MOCK_METHOD1(SetActiveDevice, void(const RawAddress&));
diff --git a/system/stack/fuzzers/rfcomm_fuzzer.cc b/system/stack/fuzzers/rfcomm_fuzzer.cc
index 3f3afbf..c39c644 100644
--- a/system/stack/fuzzers/rfcomm_fuzzer.cc
+++ b/system/stack/fuzzers/rfcomm_fuzzer.cc
@@ -36,12 +36,18 @@
 #include "test/mock/mock_stack_btm_dev.h"
 #include "test/mock/mock_stack_l2cap_api.h"
 #include "test/mock/mock_stack_l2cap_ble.h"
+#include "test/mock/mock_stack_l2cap_interface.h"
 #include "test/rfcomm/stack_rfcomm_test_utils.h"
 
 // TODO(b/369381361) Enfore -Wmissing-prototypes
 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
 #pragma GCC diagnostic ignored "-Wunused-parameter"
 
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Unused;
+
 namespace bluetooth {
 namespace hal {
 class SnoopLogger;
@@ -75,30 +81,34 @@
 }
 
 class FakeBtStack {
+  NiceMock<bluetooth::testing::stack::l2cap::Mock> mock_l2cap_interface;
 public:
-  FakeBtStack() {
-    test::mock::stack_l2cap_api::L2CA_DataWrite.body = [](uint16_t lcid, BT_HDR* hdr) {
-      osi_free(hdr);
-      return tL2CAP_DW_RESULT::SUCCESS;
-    };
-    test::mock::stack_l2cap_api::L2CA_ConnectReq.body =
-            [](uint16_t psm, const RawAddress& raw_address) { return kDummyCID; };
+  NiceMock<bluetooth::rfcomm::MockRfcommCallback> mock_rfcomm_callback;
 
-    test::mock::stack_l2cap_api::L2CA_DisconnectReq.body = [](uint16_t) { return true; };
-    test::mock::stack_l2cap_api::L2CA_Register.body =
-            [](uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info, bool enable_snoop,
-               tL2CAP_ERTM_INFO* p_ertm_info, uint16_t my_mtu, uint16_t required_remote_mtu,
-               uint16_t sec_level) {
-              appl_info = p_cb_info;
-              return psm;
-            };
+  FakeBtStack() {
+    ON_CALL(mock_l2cap_interface, L2CA_DataWrite)
+        .WillByDefault([](Unused, BT_HDR* hdr) {
+          osi_free(hdr);
+          return tL2CAP_DW_RESULT::SUCCESS;
+        });
+    ON_CALL(mock_l2cap_interface, L2CA_ConnectReq)
+        .WillByDefault([](Unused, Unused) { return kDummyCID; });
+    ON_CALL(mock_l2cap_interface, L2CA_DisconnectReq)
+        .WillByDefault([](Unused) { return true; });
+    ON_CALL(mock_l2cap_interface, L2CA_Register)
+      .WillByDefault([](uint16_t psm, const tL2CAP_APPL_INFO& p_cb_info, Unused, Unused,
+          Unused, Unused, Unused) {
+        appl_info = p_cb_info;
+        return psm;
+      });
+    bluetooth::testing::stack::l2cap::set_interface(&mock_l2cap_interface);
+
+    rfcomm_callback = &mock_rfcomm_callback;
   }
 
   ~FakeBtStack() {
-    test::mock::stack_l2cap_api::L2CA_DataWrite = {};
-    test::mock::stack_l2cap_api::L2CA_ConnectReq = {};
-    test::mock::stack_l2cap_api::L2CA_DisconnectReq = {};
-    test::mock::stack_l2cap_api::L2CA_Register = {};
+    rfcomm_callback = nullptr;
+    bluetooth::testing::stack::l2cap::reset_interface();
   }
 };
 
@@ -108,15 +118,6 @@
   FakeBtStack fake_stack;
 };
 
-class Mocks {
-public:
-  ::testing::NiceMock<bluetooth::rfcomm::MockRfcommCallback> mock_rfcomm_callback;
-
-  Mocks() { rfcomm_callback = &mock_rfcomm_callback; }
-
-  ~Mocks() { rfcomm_callback = nullptr; }
-};
-
 }  // namespace
 
 static int Cleanup(uint16_t* server_handle) { return RFCOMM_RemoveServer(*server_handle); }
@@ -212,7 +213,6 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   auto fakes = std::make_unique<Fakes>();
-  auto mocks = std::make_unique<Mocks>();
 
   FuzzedDataProvider fdp(data, size);
 
diff --git a/system/stack/gap/gap_conn.cc b/system/stack/gap/gap_conn.cc
index fd988c8..0f8ff9b 100644
--- a/system/stack/gap/gap_conn.cc
+++ b/system/stack/gap/gap_conn.cc
@@ -55,7 +55,9 @@
 
   uint8_t service_id;     /* Used by BTM */
   uint16_t gap_handle;    /* GAP handle */
-  uint16_t connection_id; /* L2CAP CID */
+  uint16_t local_cid;     /* Local L2CAP CID */
+  uint16_t remote_cid;    /* Remote L2CAP CID */
+  uint16_t acl_handle;    /* ACL handle */
   bool rem_addr_specified;
   uint8_t chan_mode_mask; /* Supported channel modes (FCR) */
   RawAddress rem_dev_address;
@@ -211,7 +213,12 @@
 
   /* Configure L2CAP COC, if transport is LE */
   if (transport == BT_TRANSPORT_LE) {
-    p_ccb->local_coc_cfg.credits = L2CA_LeCreditDefault();
+    if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349376
+      p_ccb->local_coc_cfg.credits =
+              (p_ccb->cfg.init_credit_present) ? p_ccb->cfg.init_credit : L2CA_LeCreditDefault();
+    } else {
+      p_ccb->local_coc_cfg.credits = L2CA_LeCreditDefault();
+    }
     p_ccb->local_coc_cfg.mtu = p_cfg->mtu;
 
     uint16_t max_mps = bluetooth::shim::GetController()->GetLeBufferSize().le_data_packet_length_;
@@ -284,7 +291,7 @@
       cid = stack::l2cap::get_interface().L2CA_ConnectReqWithSecurity(p_ccb->psm, *p_rem_bda,
                                                                       security);
       if (cid != 0) {
-        p_ccb->connection_id = cid;
+        p_ccb->local_cid = cid;
         return p_ccb->gap_handle;
       }
       log::warn("Unable to initiate connection peer:{} psm:{} transport:{}", *p_rem_bda, p_ccb->psm,
@@ -295,7 +302,7 @@
       cid = stack::l2cap::get_interface().L2CA_ConnectLECocReq(p_ccb->psm, *p_rem_bda,
                                                                &p_ccb->local_coc_cfg, security);
       if (cid != 0) {
-        p_ccb->connection_id = cid;
+        p_ccb->local_cid = cid;
         return p_ccb->gap_handle;
       }
       log::warn("Unable to initiate connection peer:{} psm:{} transport:{}", *p_rem_bda, p_ccb->psm,
@@ -329,14 +336,14 @@
   /* Check if we have a connection ID */
   if (p_ccb->con_state != GAP_CCB_STATE_LISTENING) {
     if (p_ccb->transport == BT_TRANSPORT_LE) {
-      if (!stack::l2cap::get_interface().L2CA_DisconnectLECocReq(p_ccb->connection_id)) {
+      if (!stack::l2cap::get_interface().L2CA_DisconnectLECocReq(p_ccb->local_cid)) {
         log::warn("Unable to request L2CAP disconnect le_coc peer:{} cid:{}",
-                  p_ccb->rem_dev_address, p_ccb->connection_id);
+                  p_ccb->rem_dev_address, p_ccb->local_cid);
       }
     } else {
-      if (!stack::l2cap::get_interface().L2CA_DisconnectReq(p_ccb->connection_id)) {
+      if (!stack::l2cap::get_interface().L2CA_DisconnectReq(p_ccb->local_cid)) {
         log::warn("Unable to request L2CAP disconnect peer:{} cid:{}", p_ccb->rem_dev_address,
-                  p_ccb->connection_id);
+                  p_ccb->local_cid);
       }
     }
   }
@@ -451,9 +458,9 @@
   while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->tx_queue)) != NULL) {
     tL2CAP_DW_RESULT status;
     if (p_ccb->transport == BT_TRANSPORT_LE) {
-      status = stack::l2cap::get_interface().L2CA_LECocDataWrite(p_ccb->connection_id, p_buf);
+      status = stack::l2cap::get_interface().L2CA_LECocDataWrite(p_ccb->local_cid, p_buf);
     } else {
-      status = stack::l2cap::get_interface().L2CA_DataWrite(p_ccb->connection_id, p_buf);
+      status = stack::l2cap::get_interface().L2CA_DataWrite(p_ccb->local_cid, p_buf);
     }
 
     if (status == tL2CAP_DW_RESULT::CONGESTED) {
@@ -576,7 +583,67 @@
     return 0;
   }
 
-  return p_ccb->connection_id;
+  return p_ccb->local_cid;
+}
+
+/*******************************************************************************
+ *
+ * Function         GAP_GetLeChannelInfo
+ *
+ * Description      This function is called to get LE L2CAP channel information
+ *                  by the gap handle. All OUT parameters must NOT be nullptr.
+ *
+ * Parameters:      handle        - Handle of the port returned in the Open
+ *                  remote_mtu    - OUT remote L2CAP MTU
+ *                  local_mps     - OUT local L2CAP COC MPS
+ *                  remote_mps    - OUT remote L2CAP COC MPS
+ *                  local_credit  - OUT local L2CAP COC credit
+ *                  remote_credit - OUT remote L2CAP COC credit
+ *                  local_cid     - OUT local L2CAP CID
+ *                  remote_cid    - OUT remote L2CAP CID
+ *                  acl_handle    - OUT ACL handle
+ *
+ * Returns          true if request accepted
+ *
+ ******************************************************************************/
+bool GAP_GetLeChannelInfo(uint16_t gap_handle, uint16_t* remote_mtu, uint16_t* local_mps,
+                          uint16_t* remote_mps, uint16_t* local_credit, uint16_t* remote_credit,
+                          uint16_t* local_cid, uint16_t* remote_cid, uint16_t* acl_handle) {
+  tGAP_CCB* p_ccb = gap_find_ccb_by_handle(gap_handle);
+  if (p_ccb == NULL || p_ccb->transport != BT_TRANSPORT_LE ||
+      p_ccb->con_state != GAP_CCB_STATE_CONNECTED) {
+    return false;
+  }
+
+  *remote_mtu = p_ccb->peer_coc_cfg.mtu;
+  *local_mps = p_ccb->local_coc_cfg.mps;
+  *remote_mps = p_ccb->peer_coc_cfg.mps;
+  *local_credit = p_ccb->local_coc_cfg.credits;
+  *remote_credit = p_ccb->peer_coc_cfg.credits;
+  *local_cid = p_ccb->local_cid;
+  *remote_cid = p_ccb->remote_cid;
+  *acl_handle = p_ccb->acl_handle;
+  return true;
+}
+
+/*******************************************************************************
+ *
+ * Function         GAP_IsTransportLe
+ *
+ * Description      This function returns if the transport is LE by the gap handle.
+ *
+ * Parameters:      handle        - Handle of the port returned in the Open
+ *
+ * Returns          true if transport is LE, else false
+ *
+ ******************************************************************************/
+bool GAP_IsTransportLe(uint16_t gap_handle) {
+  tGAP_CCB* p_ccb = gap_find_ccb_by_handle(gap_handle);
+  if (p_ccb == NULL || p_ccb->transport != BT_TRANSPORT_LE ||
+      p_ccb->con_state != GAP_CCB_STATE_CONNECTED) {
+    return false;
+  }
+  return true;
 }
 
 /*******************************************************************************
@@ -649,7 +716,7 @@
 
   /* Save the BD Address and Channel ID. */
   p_ccb->rem_dev_address = bd_addr;
-  p_ccb->connection_id = l2cap_cid;
+  p_ccb->local_cid = l2cap_cid;
 
   if (p_ccb->transport == BT_TRANSPORT_LE) {
     /* get the remote coc configuration */
@@ -683,12 +750,16 @@
     tGAP_CB_DATA cb_data;
     uint16_t l2cap_remote_cid;
     if (com::android::bluetooth::flags::bt_socket_api_l2cap_cid() &&
-        stack::l2cap::get_interface().L2CA_GetRemoteChannelId(p_ccb->connection_id,
+        stack::l2cap::get_interface().L2CA_GetRemoteChannelId(p_ccb->local_cid,
                                                               &l2cap_remote_cid)) {
-      cb_data.l2cap_cids.local_cid = p_ccb->connection_id;
+      cb_data.l2cap_cids.local_cid = p_ccb->local_cid;
       cb_data.l2cap_cids.remote_cid = l2cap_remote_cid;
       cb_data_ptr = &cb_data;
     }
+    if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3367197
+      stack::l2cap::get_interface().L2CA_GetRemoteChannelId(p_ccb->local_cid, &p_ccb->remote_cid);
+      stack::l2cap::get_interface().L2CA_GetAclHandle(p_ccb->local_cid, &p_ccb->acl_handle);
+    }
     p_ccb->con_state = GAP_CCB_STATE_CONNECTED;
 
     p_ccb->p_callback(p_ccb->gap_handle, GAP_EVT_CONN_OPENED, cb_data_ptr);
@@ -926,7 +997,7 @@
  * Function         gap_find_ccb_by_cid
  *
  * Description      This function searches the CCB table for an entry with the
- *                  passed CID.
+ *                  passed local CID.
  *
  * Returns          the CCB address, or NULL if not found.
  *
@@ -937,7 +1008,7 @@
 
   /* Look through each connection control block */
   for (xx = 0, p_ccb = conn.ccb_pool; xx < GAP_MAX_CONNECTIONS; xx++, p_ccb++) {
-    if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->connection_id == cid)) {
+    if ((p_ccb->con_state != GAP_CCB_STATE_IDLE) && (p_ccb->local_cid == cid)) {
       return p_ccb;
     }
   }
diff --git a/system/stack/include/gap_api.h b/system/stack/include/gap_api.h
index e4a185e..f216b12 100644
--- a/system/stack/include/gap_api.h
+++ b/system/stack/include/gap_api.h
@@ -226,6 +226,43 @@
 
 /*******************************************************************************
  *
+ * Function         GAP_GetLeChannelInfo
+ *
+ * Description      This function is called to get LE L2CAP channel information
+ *                  by the gap handle. All OUT parameters must NOT be nullptr.
+ *
+ * Parameters:      handle        - Handle of the port returned in the Open
+ *                  remote_mtu    - OUT remote L2CAP MTU
+ *                  local_mps     - OUT local L2CAP COC MPS
+ *                  remote_mps    - OUT remote L2CAP COC MPS
+ *                  local_credit  - OUT local L2CAP COC credit
+ *                  remote_credit - OUT remote L2CAP COC credit
+ *                  local_cid     - OUT local L2CAP CID
+ *                  remote_cid    - OUT remote L2CAP CID
+ *                  acl_handle    - OUT ACL handle
+ *
+ * Returns          true if request accepted
+ *
+ ******************************************************************************/
+bool GAP_GetLeChannelInfo(uint16_t gap_handle, uint16_t* remote_mtu, uint16_t* local_mps,
+                          uint16_t* remote_mps, uint16_t* local_credit, uint16_t* remote_credit,
+                          uint16_t* local_cid, uint16_t* remote_cid, uint16_t* acl_handle);
+
+/*******************************************************************************
+ *
+ * Function         GAP_IsTransportLe
+ *
+ * Description      This function returns if the transport is LE by the gap handle.
+ *
+ * Parameters:      handle        - Handle of the port returned in the Open
+ *
+ * Returns          true if transport is LE, else false
+ *
+ ******************************************************************************/
+bool GAP_IsTransportLe(uint16_t gap_handle);
+
+/*******************************************************************************
+ *
  * Function         GAP_Init
  *
  * Description      Initializes the control blocks used by GAP.
diff --git a/system/stack/include/l2cap_interface.h b/system/stack/include/l2cap_interface.h
index cfd8815..c3e32dc 100644
--- a/system/stack/include/l2cap_interface.h
+++ b/system/stack/include/l2cap_interface.h
@@ -916,6 +916,23 @@
    **
    *******************************************************************************/
   virtual bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id, bool is_local_cid) = 0;
+
+  /*******************************************************************************
+   **
+   ** Function         L2CA_GetAclHandle
+   **
+   ** Description      Given a local channel identifier, |lcid|, this function
+   **                  returns the handle of the corresponding ACL connection, |acl_handle|. If
+   **                  |lcid| is not known or is invalid, this function returns false and does not
+   **                  modify the value pointed at by |acl_handle|.
+   **
+   ** Parameters:      lcid: Local CID
+   **                  acl_handle: Pointer to ACL handle must NOT be nullptr
+   **
+   ** Returns          true if acl_handle lookup was successful
+   **
+   ******************************************************************************/
+  virtual bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) = 0;
 };
 
 Interface& get_interface();
diff --git a/system/stack/include/l2cap_types.h b/system/stack/include/l2cap_types.h
index 311dab7..d2dd74b 100644
--- a/system/stack/include/l2cap_types.h
+++ b/system/stack/include/l2cap_types.h
@@ -129,6 +129,8 @@
   uint8_t fcs;      /* '0' if desire is to bypass FCS, otherwise '1' */
   bool ext_flow_spec_present;
   tHCI_EXT_FLOW_SPEC ext_flow_spec;
+  bool init_credit_present;
+  uint16_t init_credit;
   uint16_t flags; /* bit 0: 0-no continuation, 1-continuation */
 } tL2CAP_CFG_INFO;
 
diff --git a/system/stack/include/l2cdefs.h b/system/stack/include/l2cdefs.h
index 88ed7a7..be10171 100644
--- a/system/stack/include/l2cdefs.h
+++ b/system/stack/include/l2cdefs.h
@@ -487,6 +487,7 @@
  */
 #define L2CAP_SDU_LENGTH_MAX (8080 + 26 - (L2CAP_MIN_OFFSET + 6))
 constexpr uint16_t L2CAP_SDU_LENGTH_LE_MAX = 0xffff;
+constexpr uint16_t L2CAP_SDU_LENGTH_LE_MIN = 23;
 
 /* SAR bits in the control word
  */
diff --git a/system/stack/l2cap/internal/l2c_api.h b/system/stack/l2cap/internal/l2c_api.h
index 5ed3403..99c1f57 100644
--- a/system/stack/l2cap/internal/l2c_api.h
+++ b/system/stack/l2cap/internal/l2c_api.h
@@ -777,3 +777,20 @@
 **
 *******************************************************************************/
 [[nodiscard]] bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id, bool is_local_cid);
+
+/*******************************************************************************
+**
+** Function         L2CA_GetAclHandle
+**
+** Description      Given a local channel identifier, |lcid|, this function
+**                  returns the handle of the corresponding ACL connection, |acl_handle|. If
+**                  |lcid| is not known or is invalid, this function returns false and does not
+**                  modify the value pointed at by |acl_handle|.
+**
+** Parameters:      lcid: Local CID
+**                  acl_handle: Pointer to ACL handle must NOT be nullptr
+**
+** Returns          true if acl_handle lookup was successful
+**
+******************************************************************************/
+[[nodiscard]] bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle);
diff --git a/system/stack/l2cap/l2c_api.cc b/system/stack/l2cap/l2c_api.cc
index 2b67132..68aacec 100644
--- a/system/stack/l2cap/l2c_api.cc
+++ b/system/stack/l2cap/l2c_api.cc
@@ -1731,6 +1731,38 @@
   return ret;
 }
 
+/*******************************************************************************
+ *
+ *  Function        L2CA_GetAclHandle
+ *
+ *  Description     Given a local channel identifier, |lcid|, this function
+ *                  returns the bound ACL handle, |acl_handle|. If |acl_handle|
+ *                  is not known or is invalid, this function returns false and
+ *                  does not modify the value pointed at by |acl_handle|.
+ *
+ *  Parameters:     lcid: Local CID
+ *                  rcid: Pointer to ACL handle must NOT be nullptr
+ *
+ *  Return value:   true if acl_handle lookup was successful
+ *
+ ******************************************************************************/
+bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) {
+  log::assert_that(acl_handle != nullptr, "assert failed: acl_handle != nullptr");
+
+  tL2C_CCB* p_ccb = l2cu_find_ccb_by_cid(nullptr, lcid);
+  if (p_ccb == nullptr) {
+    log::error("No CCB for CID:0x{:04x}", lcid);
+    return false;
+  }
+  uint16_t handle = p_ccb->p_lcb->Handle();
+  if (handle == HCI_INVALID_HANDLE) {
+    log::error("Invalid ACL handle");
+    return false;
+  }
+  *acl_handle = handle;
+  return true;
+}
+
 using namespace bluetooth;
 
 #define DUMPSYS_TAG "shim::legacy::l2cap"
diff --git a/system/stack/l2cap/l2c_api.h b/system/stack/l2cap/l2c_api.h
index f645803..f89a5cc 100644
--- a/system/stack/l2cap/l2c_api.h
+++ b/system/stack/l2cap/l2c_api.h
@@ -133,6 +133,8 @@
   void L2CA_SetMediaStreamChannel(uint16_t local_media_cid, bool status) override;
   [[nodiscard]] bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id,
                                          bool is_local_cid) override;
+
+  [[nodiscard]] bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) override;
 };
 
 }  // namespace l2cap
diff --git a/system/stack/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc
index 9d69e5a..2fdb63f 100644
--- a/system/stack/l2cap/l2c_ble.cc
+++ b/system/stack/l2cap/l2c_ble.cc
@@ -777,11 +777,21 @@
       p_ccb->p_rcb = p_rcb;
       p_ccb->remote_cid = rcid;
 
-      p_ccb->local_conn_cfg.mtu = L2CAP_SDU_LENGTH_LE_MAX;
-      p_ccb->local_conn_cfg.mps =
-              bluetooth::shim::GetController()->GetLeBufferSize().le_data_packet_length_;
-      p_ccb->local_conn_cfg.credits = L2CA_LeCreditDefault();
-      p_ccb->remote_credit_count = L2CA_LeCreditDefault();
+      if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349377
+        p_ccb->local_conn_cfg.mtu = p_rcb->coc_cfg.mtu;
+        p_ccb->local_conn_cfg.mps = p_rcb->coc_cfg.mps;
+      } else {
+        p_ccb->local_conn_cfg.mtu = L2CAP_SDU_LENGTH_LE_MAX;
+        p_ccb->local_conn_cfg.mps =
+                bluetooth::shim::GetController()->GetLeBufferSize().le_data_packet_length_;
+      }
+      if (com::android::bluetooth::flags::socket_settings_api()) {  // Added with aosp/3349376
+        p_ccb->local_conn_cfg.credits = p_rcb->coc_cfg.credits;
+        p_ccb->remote_credit_count = p_rcb->coc_cfg.credits;
+      } else {
+        p_ccb->local_conn_cfg.credits = L2CA_LeCreditDefault();
+        p_ccb->remote_credit_count = L2CA_LeCreditDefault();
+      }
 
       p_ccb->peer_conn_cfg.mtu = mtu;
       p_ccb->peer_conn_cfg.mps = mps;
diff --git a/system/stack/l2cap/l2c_utils.cc b/system/stack/l2cap/l2c_utils.cc
index c04379b..c18ba8c 100644
--- a/system/stack/l2cap/l2c_utils.cc
+++ b/system/stack/l2cap/l2c_utils.cc
@@ -3331,6 +3331,9 @@
   p = (uint8_t*)(p_buf + 1) + L2CAP_SEND_CMD_OFFSET + HCI_DATA_PREAMBLE_SIZE + L2CAP_PKT_OVERHEAD +
       L2CAP_CMD_OVERHEAD;
 
+  log::verbose("local cid: {}, mtu: {}, mps: {}, initial credits: {}", p_ccb->local_cid,
+               p_ccb->local_conn_cfg.mtu, p_ccb->local_conn_cfg.mps, p_ccb->local_conn_cfg.credits);
+
   UINT16_TO_STREAM(p, p_ccb->local_cid);              /* Local CID */
   UINT16_TO_STREAM(p, p_ccb->local_conn_cfg.mtu);     /* MTU */
   UINT16_TO_STREAM(p, p_ccb->local_conn_cfg.mps);     /* MPS */
diff --git a/system/stack/l2cap/l2cap_api.cc b/system/stack/l2cap/l2cap_api.cc
index 94466c9..2e1b12c 100644
--- a/system/stack/l2cap/l2cap_api.cc
+++ b/system/stack/l2cap/l2cap_api.cc
@@ -257,3 +257,8 @@
                                                                           uint16_t* rcid) {
   return ::L2CA_GetRemoteChannelId(lcid, rcid);
 }
+
+[[nodiscard]] bool bluetooth::stack::l2cap::Impl::L2CA_GetAclHandle(uint16_t lcid,
+                                                                    uint16_t* acl_handle) {
+  return ::L2CA_GetAclHandle(lcid, acl_handle);
+}
diff --git a/system/test/mock/mock_btif_sock_l2cap.cc b/system/test/mock/mock_btif_sock_l2cap.cc
new file mode 100644
index 0000000..41f19f8
--- /dev/null
+++ b/system/test/mock/mock_btif_sock_l2cap.cc
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+/*
+ * Generated mock file from original source file
+ *   Functions generated:13
+ *
+ *  mockcify.pl ver 0.7.1
+ */
+
+// Mock include file to share data between tests and mock
+#include "test/mock/mock_btif_sock_l2cap.h"
+
+#include <cstdint>
+
+#include "test/common/mock_functions.h"
+
+// TODO(b/369381361) Enfore -Wmissing-prototypes
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+// Original usings
+
+// Mocked internal structures, if any
+
+namespace test {
+namespace mock {
+namespace btif_sock_l2cap {
+
+// Function state capture and return values, if needed
+struct on_btsocket_l2cap_close on_btsocket_l2cap_close;
+struct on_btsocket_l2cap_opened_complete on_btsocket_l2cap_opened_complete;
+
+}  // namespace btif_sock_l2cap
+}  // namespace mock
+}  // namespace test
+
+// Mocked function return values, if any
+namespace test {
+namespace mock {
+namespace btif_sock_l2cap {}  // namespace btif_sock_l2cap
+}  // namespace mock
+}  // namespace test
+
+// Mocked functions, if any
+void on_btsocket_l2cap_close(uint64_t socket_id) {
+  inc_func_call_count(__func__);
+  test::mock::btif_sock_l2cap::on_btsocket_l2cap_close(socket_id);
+}
+void on_btsocket_l2cap_opened_complete(uint64_t socket_id, bool success) {
+  inc_func_call_count(__func__);
+  test::mock::btif_sock_l2cap::on_btsocket_l2cap_opened_complete(socket_id, success);
+}
+// Mocked functions complete
+// END mockcify generation
diff --git a/system/test/mock/mock_btif_sock_l2cap.h b/system/test/mock/mock_btif_sock_l2cap.h
new file mode 100644
index 0000000..f883f8b
--- /dev/null
+++ b/system/test/mock/mock_btif_sock_l2cap.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+#pragma once
+
+/*
+ * Generated mock file from original source file
+ *   Functions generated:13
+ *
+ *  mockcify.pl ver 0.7.1
+ */
+
+#include <cstdint>
+#include <functional>
+
+// Original included files, if any
+#include <cstring>
+
+// Original usings
+
+// Mocked compile conditionals, if any
+
+namespace test {
+namespace mock {
+namespace btif_sock_l2cap {
+
+// Shared state between mocked functions and tests
+// Name: on_btsocket_l2cap_close
+// Params: uint64_t socket_id
+// Return: void
+struct on_btsocket_l2cap_close {
+  std::function<void(uint64_t socket_id)> body{[](uint64_t /* socket_id */) {}};
+  void operator()(uint64_t socket_id) { body(socket_id); }
+};
+extern struct on_btsocket_l2cap_close on_btsocket_l2cap_close;
+
+// Name: on_btsocket_l2cap_opened_complete
+// Params: uint64_t socket_id, bool success
+// Return: void
+struct on_btsocket_l2cap_opened_complete {
+  std::function<void(uint64_t socket_id, bool success)> body{
+          [](uint64_t /* socket_id */, bool /* success */) {}};
+  void operator()(uint64_t socket_id, bool success) { body(socket_id, success); }
+};
+extern struct on_btsocket_l2cap_opened_complete on_btsocket_l2cap_opened_complete;
+
+}  // namespace btif_sock_l2cap
+}  // namespace mock
+}  // namespace test
+
+// END mockcify generation
diff --git a/system/test/mock/mock_main_shim_entry.cc b/system/test/mock/mock_main_shim_entry.cc
index 72054da..d48a382 100644
--- a/system/test/mock/mock_main_shim_entry.cc
+++ b/system/test/mock/mock_main_shim_entry.cc
@@ -20,6 +20,7 @@
 #include "hci/hci_interface.h"
 #include "hci/le_advertising_manager_mock.h"
 #include "hci/le_scanning_manager_mock.h"
+#include "lpp/lpp_offload_interface_mock.h"
 #include "main/shim/entry.h"
 #include "os/handler.h"
 #include "storage/storage_module.h"
@@ -40,6 +41,10 @@
 }  // namespace testing
 }  // namespace hci
 
+namespace lpp::testing {
+MockLppOffloadInterface* mock_lpp_offload_interface_{nullptr};
+}  // namespace lpp::testing
+
 class Dumpsys;
 
 namespace shim {
@@ -59,6 +64,9 @@
 metrics::CounterMetrics* GetCounterMetrics() { return nullptr; }
 hci::MsftExtensionManager* GetMsftExtensionManager() { return nullptr; }
 hci::RemoteNameRequestModule* GetRemoteNameRequest() { return nullptr; }
+lpp::LppOffloadInterface* GetLppOffloadManager() {
+  return lpp::testing::mock_lpp_offload_interface_;
+}
 
 }  // namespace shim
 }  // namespace bluetooth
diff --git a/system/test/mock/mock_stack_gap_conn.cc b/system/test/mock/mock_stack_gap_conn.cc
index 19034a9..07193c9 100644
--- a/system/test/mock/mock_stack_gap_conn.cc
+++ b/system/test/mock/mock_stack_gap_conn.cc
@@ -61,4 +61,16 @@
   inc_func_call_count(__func__);
   return 0;
 }
+bool GAP_GetLeChannelInfo(uint16_t /* gap_handle */, uint16_t* /*remote_mtu */,
+                          uint16_t* /* local_mps */, uint16_t* /* remote_mps */,
+                          uint16_t* /* local_credit */, uint16_t* /* remote_credit */,
+                          uint16_t* /* local_cid */, uint16_t* /* remote_cid */,
+                          uint16_t* /* acl_handle */) {
+  inc_func_call_count(__func__);
+  return false;
+}
+bool GAP_IsTransportLe(uint16_t /* gap_handle */) {
+  inc_func_call_count(__func__);
+  return false;
+}
 void GAP_Init(void) { inc_func_call_count(__func__); }
diff --git a/system/test/mock/mock_stack_l2cap_api.cc b/system/test/mock/mock_stack_l2cap_api.cc
index b8f3f4e..3dd0ef9 100644
--- a/system/test/mock/mock_stack_l2cap_api.cc
+++ b/system/test/mock/mock_stack_l2cap_api.cc
@@ -75,6 +75,7 @@
 struct L2CA_isMediaChannel L2CA_isMediaChannel;
 struct L2CA_LeCreditDefault L2CA_LeCreditDefault;
 struct L2CA_LeCreditThreshold L2CA_LeCreditThreshold;
+struct L2CA_GetAclHandle L2CA_GetAclHandle;
 
 }  // namespace stack_l2cap_api
 }  // namespace mock
@@ -250,6 +251,10 @@
   inc_func_call_count(__func__);
   return test::mock::stack_l2cap_api::L2CA_LeCreditThreshold();
 }
+bool L2CA_GetAclHandle(uint16_t lcid, uint16_t* acl_handle) {
+  inc_func_call_count(__func__);
+  return test::mock::stack_l2cap_api::L2CA_GetAclHandle(lcid, acl_handle);
+}
 
 // END mockcify generation
 
diff --git a/system/test/mock/mock_stack_l2cap_api.h b/system/test/mock/mock_stack_l2cap_api.h
index 40d746e..ed20c34 100644
--- a/system/test/mock/mock_stack_l2cap_api.h
+++ b/system/test/mock/mock_stack_l2cap_api.h
@@ -457,6 +457,16 @@
 };
 extern struct L2CA_LeCreditThreshold L2CA_LeCreditThreshold;
 
+// Name: L2CA_GetAclHandle
+// Params: uint16_t lcid, uint16_t* acl_handle
+// Returns: bool
+struct L2CA_GetAclHandle {
+  std::function<bool(uint16_t lcid, uint16_t* acl_handle)> body{
+          [](uint16_t /* lcid */, uint16_t* /* acl_handle */) { return false; }};
+  bool operator()(uint16_t lcid, uint16_t* acl_handle) { return body(lcid, acl_handle); }
+};
+extern struct L2CA_GetAclHandle L2CA_GetAclHandle;
+
 }  // namespace stack_l2cap_api
 }  // namespace mock
 }  // namespace test
diff --git a/system/test/mock/mock_stack_l2cap_interface.h b/system/test/mock/mock_stack_l2cap_interface.h
index c87bf16..7756f4a 100644
--- a/system/test/mock/mock_stack_l2cap_interface.h
+++ b/system/test/mock/mock_stack_l2cap_interface.h
@@ -106,6 +106,7 @@
   MOCK_METHOD(uint16_t, L2CA_LeCreditThreshold, ());
 
   MOCK_METHOD(void, L2CA_Consolidate, (const RawAddress& identity_addr, const RawAddress& rpa));
+  MOCK_METHOD(bool, L2CA_GetAclHandle, (uint16_t lcid, uint16_t* acl_handle));
 
   // Disconnect methods an active connection for both BR/EDR and BLE
   MOCK_METHOD(bool, L2CA_DisconnectReq, (uint16_t cid));