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));