From 762667bd766f21123ea8b9913764202cb8c33bc0 Mon Sep 17 00:00:00 2001 From: Jayden Kim Date: Wed, 20 Nov 2024 09:10:45 +0000 Subject: Add helper functions to get LE L2CAP channel and ACL handle information Bug: 342012881 Bug: 367419086 Test: m -j Change-Id: I5babc03bac5f7c7c3d6c5565dce044e96ac0154c --- system/stack/gap/gap_conn.cc | 94 ++++++++++++++++++++++----- system/stack/include/gap_api.h | 37 +++++++++++ system/stack/include/l2cap_interface.h | 17 +++++ system/stack/l2cap/internal/l2c_api.h | 17 +++++ system/stack/l2cap/l2c_api.cc | 32 +++++++++ system/stack/l2cap/l2c_api.h | 2 + system/stack/l2cap/l2cap_api.cc | 5 ++ system/test/mock/mock_stack_gap_conn.cc | 12 ++++ system/test/mock/mock_stack_l2cap_api.cc | 5 ++ system/test/mock/mock_stack_l2cap_api.h | 10 +++ system/test/mock/mock_stack_l2cap_interface.h | 1 + 11 files changed, 217 insertions(+), 15 deletions(-) diff --git a/system/stack/gap/gap_conn.cc b/system/stack/gap/gap_conn.cc index fd988c8fd3..127ded1cff 100644 --- a/system/stack/gap/gap_conn.cc +++ b/system/stack/gap/gap_conn.cc @@ -55,7 +55,9 @@ typedef struct { 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; @@ -284,7 +286,7 @@ uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t service_id, bool is 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 +297,7 @@ uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t service_id, bool is 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 +331,14 @@ uint16_t GAP_ConnClose(uint16_t gap_handle) { /* 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 +453,9 @@ static bool gap_try_write_queued_data(tGAP_CCB* p_ccb) { 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 +578,67 @@ uint16_t GAP_ConnGetL2CAPCid(uint16_t gap_handle) { 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 +711,7 @@ static void gap_connect_ind(const RawAddress& bd_addr, uint16_t l2cap_cid, uint1 /* 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 +745,14 @@ static void gap_checks_con_flags(tGAP_CCB* p_ccb) { 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; } + 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 +990,7 @@ static void gap_congestion_ind(uint16_t lcid, bool is_congested) { * 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 +1001,7 @@ static tGAP_CCB* gap_find_ccb_by_cid(uint16_t cid) { /* 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 e4a185eefe..f216b12f3b 100644 --- a/system/stack/include/gap_api.h +++ b/system/stack/include/gap_api.h @@ -224,6 +224,43 @@ uint16_t GAP_ConnGetRemMtuSize(uint16_t gap_handle); ******************************************************************************/ uint16_t GAP_ConnGetL2CAPCid(uint16_t gap_handle); +/******************************************************************************* + * + * 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 diff --git a/system/stack/include/l2cap_interface.h b/system/stack/include/l2cap_interface.h index cfd88153f7..c3e32dcf36 100644 --- a/system/stack/include/l2cap_interface.h +++ b/system/stack/include/l2cap_interface.h @@ -916,6 +916,23 @@ public: ** *******************************************************************************/ 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/l2cap/internal/l2c_api.h b/system/stack/l2cap/internal/l2c_api.h index 5ed3403cff..99c1f57781 100644 --- a/system/stack/l2cap/internal/l2c_api.h +++ b/system/stack/l2cap/internal/l2c_api.h @@ -777,3 +777,20 @@ void L2CA_SetMediaStreamChannel(uint16_t local_media_cid, bool status); ** *******************************************************************************/ [[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 2b67132edb..68aacece21 100644 --- a/system/stack/l2cap/l2c_api.cc +++ b/system/stack/l2cap/l2c_api.cc @@ -1731,6 +1731,38 @@ bool L2CA_isMediaChannel(uint16_t handle, uint16_t channel_id, bool is_local_cid 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 f645803dad..f89a5cc6ca 100644 --- a/system/stack/l2cap/l2c_api.h +++ b/system/stack/l2cap/l2c_api.h @@ -133,6 +133,8 @@ public: 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/l2cap_api.cc b/system/stack/l2cap/l2cap_api.cc index 94466c90e2..2e1b12c7de 100644 --- a/system/stack/l2cap/l2cap_api.cc +++ b/system/stack/l2cap/l2cap_api.cc @@ -257,3 +257,8 @@ void bluetooth::stack::l2cap::Impl::L2CA_SetMediaStreamChannel(uint16_t local_me 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_stack_gap_conn.cc b/system/test/mock/mock_stack_gap_conn.cc index 19034a9fec..07193c9130 100644 --- a/system/test/mock/mock_stack_gap_conn.cc +++ b/system/test/mock/mock_stack_gap_conn.cc @@ -61,4 +61,16 @@ uint16_t GAP_ConnWriteData(uint16_t /* gap_handle */, BT_HDR* /* msg */) { 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 b8f3f4e227..3dd0ef991c 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_SetMediaStreamChannel L2CA_SetMediaStreamChannel; 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 @@ uint16_t L2CA_LeCreditThreshold() { 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 40d746ecd3..ed20c3438d 100644 --- a/system/test/mock/mock_stack_l2cap_api.h +++ b/system/test/mock/mock_stack_l2cap_api.h @@ -457,6 +457,16 @@ struct L2CA_LeCreditThreshold { }; extern struct L2CA_LeCreditThreshold L2CA_LeCreditThreshold; +// Name: L2CA_GetAclHandle +// Params: uint16_t lcid, uint16_t* acl_handle +// Returns: bool +struct L2CA_GetAclHandle { + std::function 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 c87bf164bb..7756f4a4ed 100644 --- a/system/test/mock/mock_stack_l2cap_interface.h +++ b/system/test/mock/mock_stack_l2cap_interface.h @@ -106,6 +106,7 @@ public: 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)); -- cgit v1.2.3-59-g8ed1b From ea59bfabf3eb65530c173f6ea19ff261c52501a9 Mon Sep 17 00:00:00 2001 From: Jayden Kim Date: Wed, 20 Nov 2024 04:33:38 +0000 Subject: Add low power processor offload manager with socket hal shim Bug: 342012881 Bug: 367419086 Test: m com.android.btservices Change-Id: Iec194e241bdafe8dce34db23cc65398a0af17be0 --- system/gd/Android.bp | 4 + system/gd/BUILD.gn | 1 + system/gd/hal/Android.bp | 14 ++ system/gd/hal/BUILD.gn | 9 ++ system/gd/hal/socket_hal.h | 174 +++++++++++++++++++++ system/gd/hal/socket_hal_android.cc | 236 +++++++++++++++++++++++++++++ system/gd/hal/socket_hal_host.cc | 44 ++++++ system/gd/lpp/Android.bp | 15 ++ system/gd/lpp/BUILD.gn | 31 ++++ system/gd/lpp/lpp_offload_interface.h | 75 +++++++++ system/gd/lpp/lpp_offload_interface_mock.h | 33 ++++ system/gd/lpp/lpp_offload_manager.cc | 100 ++++++++++++ system/gd/lpp/lpp_offload_manager.h | 62 ++++++++ system/main/shim/BUILD.gn | 2 + system/main/shim/entry.cc | 5 + system/main/shim/entry.h | 5 + system/main/shim/stack.cc | 3 + system/test/mock/mock_main_shim_entry.cc | 8 + 18 files changed, 821 insertions(+) create mode 100644 system/gd/hal/socket_hal.h create mode 100644 system/gd/hal/socket_hal_android.cc create mode 100644 system/gd/hal/socket_hal_host.cc create mode 100644 system/gd/lpp/Android.bp create mode 100644 system/gd/lpp/BUILD.gn create mode 100644 system/gd/lpp/lpp_offload_interface.h create mode 100644 system/gd/lpp/lpp_offload_interface_mock.h create mode 100644 system/gd/lpp/lpp_offload_manager.cc create mode 100644 system/gd/lpp/lpp_offload_manager.h diff --git a/system/gd/Android.bp b/system/gd/Android.bp index 9ac1e58995..f00f34557e 100644 --- a/system/gd/Android.bp +++ b/system/gd/Android.bp @@ -87,6 +87,7 @@ cc_defaults { srcs: [ ":BluetoothHalSources_hci_host", ":BluetoothHalSources_ranging_host", + ":BluetoothHalSources_socket_host", ":BluetoothOsSources_host", ":BluetoothSyspropsSources", ], @@ -95,6 +96,7 @@ cc_defaults { srcs: [ ":BluetoothHalSources_hci_android_hidl", ":BluetoothHalSources_ranging_android", + ":BluetoothHalSources_socket_android", ":BluetoothOsSources_android", ], shared_libs: [ @@ -117,6 +119,7 @@ cc_defaults { whole_static_libs: [ "android.hardware.bluetooth-V1-ndk", "android.hardware.bluetooth.ranging-V2-ndk", + "android.hardware.bluetooth.socket-V1-ndk", ], }, }, @@ -125,6 +128,7 @@ cc_defaults { ":BluetoothDumpsysSources", ":BluetoothHalSources", ":BluetoothHciSources", + ":BluetoothLppOffloadSources", ":BluetoothMetricsSources", ":BluetoothNeighborSources", ":BluetoothOsSources", diff --git a/system/gd/BUILD.gn b/system/gd/BUILD.gn index 14d5044d68..8056da7a36 100644 --- a/system/gd/BUILD.gn +++ b/system/gd/BUILD.gn @@ -69,6 +69,7 @@ static_library("libbluetooth_gd") { "//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 91e1573c0e..49683afbd1 100644 --- a/system/gd/hal/Android.bp +++ b/system/gd/hal/Android.bp @@ -42,6 +42,20 @@ filegroup { ], } +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: [ diff --git a/system/gd/hal/BUILD.gn b/system/gd/hal/BUILD.gn index 46be6e9d8a..ebf15255af 100644 --- a/system/gd/hal/BUILD.gn +++ b/system/gd/hal/BUILD.gn @@ -53,3 +53,12 @@ source_set("BluetoothHalSources_ranging_host") { 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 0000000000..2f3d583d30 --- /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 + +#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 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 0000000000..5e23974897 --- /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 +#include +#include +#include +#include + +// 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(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(); + ::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(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(context.socket_id), + .name = context.name, + .aclConnectionHandle = context.acl_connection_handle, + .hubId = static_cast(context.endpoint_info.hub_id), + .endpointId = static_cast(context.endpoint_info.endpoint_id), + }; + if (std::holds_alternative(context.channel_info)) { + auto& le_coc_context = std::get(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 socket_hal_instance_; + std::shared_ptr 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 0000000000..292428a821 --- /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/lpp/Android.bp b/system/gd/lpp/Android.bp new file mode 100644 index 0000000000..2be7b85d9e --- /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 0000000000..b773001ce8 --- /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 0000000000..388466f2e5 --- /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 0000000000..03cd5f4f52 --- /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 + +#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 0000000000..c5c1fb247e --- /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 + +#include + +#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(); } + +LppOffloadManager::~LppOffloadManager() = default; + +void LppOffloadManager::ListDependencies(ModuleList* list) const { list->add(); } + +void LppOffloadManager::Start() { pimpl_->start(GetHandler(), GetDependency()); } + +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 0000000000..90097f77bd --- /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 + +#include +#include + +#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 pimpl_; +}; + +} // namespace bluetooth::lpp diff --git a/system/main/shim/BUILD.gn b/system/main/shim/BUILD.gn index 88bbba0c6b..d1d7353950 100644 --- a/system/main/shim/BUILD.gn +++ b/system/main/shim/BUILD.gn @@ -33,6 +33,7 @@ source_set("BluetoothStackManagerSources") { "//bt/system/gd/common:BluetoothCommonSources", "//bt/system/gd/dumpsys/bundler:BluetoothGeneratedBundlerSchema_h_bfbs", "//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", @@ -79,6 +80,7 @@ source_set("LibBluetoothShimSources") { "//bt/system/gd/common:BluetoothCommonSources", "//bt/system/gd/dumpsys/bundler:BluetoothGeneratedBundlerSchema_h_bfbs", "//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 a1c61b7ab0..8f0c6c6020 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 @@ hal::SnoopLogger* GetSnoopLogger() { return Stack::GetInstance()->GetStackManager()->GetInstance(); } +lpp::LppOffloadInterface* GetLppOffloadManager() { + return Stack::GetInstance()->GetStackManager()->GetInstance(); +} + storage::StorageModule* GetStorage() { return Stack::GetInstance()->GetStackManager()->GetInstance(); } diff --git a/system/main/shim/entry.h b/system/main/shim/entry.h index e50cbc4d91..7e97c87716 100644 --- a/system/main/shim/entry.h +++ b/system/main/shim/entry.h @@ -48,6 +48,10 @@ class LeScanningManager; class MsftExtensionManager; } // namespace hci +namespace lpp { +class LppOffloadInterface; +} + namespace metrics { class CounterMetrics; } @@ -69,6 +73,7 @@ hci::HciInterface* GetHciLayer(); 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 73526bc4dd..3f99837a1d 100644 --- a/system/main/shim/stack.cc +++ b/system/main/shim/stack.cc @@ -36,6 +36,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 +75,8 @@ void Stack::StartEverything() { #if TARGET_FLOSS modules.add(); +#else + modules.add(); #endif modules.add(); modules.add(); diff --git a/system/test/mock/mock_main_shim_entry.cc b/system/test/mock/mock_main_shim_entry.cc index 72054dad81..d48a382727 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 @@ MockDistanceMeasurementManager* mock_distance_measurement_manager_{nullptr}; } // namespace testing } // namespace hci +namespace lpp::testing { +MockLppOffloadInterface* mock_lpp_offload_interface_{nullptr}; +} // namespace lpp::testing + class Dumpsys; namespace shim { @@ -59,6 +64,9 @@ storage::StorageModule* GetStorage() { return nullptr; } 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 -- cgit v1.2.3-59-g8ed1b From fa42d93294ced1a3e1e91b913348730dc725d365 Mon Sep 17 00:00:00 2001 From: Liang Li Date: Thu, 14 Nov 2024 17:31:43 +0000 Subject: Read socket offload capabilities from bluetooth low power proccesor Bug: 342012881 Bug: 367419086 Test: atest CtsBluetoothTestCases Change-Id: I581435207c9716ccdbedc3dbd77f81845d244a87 --- android/app/aidl/android/bluetooth/IBluetooth.aidl | 3 ++ .../bluetooth/btservice/AbstractionLayer.java | 1 + .../bluetooth/btservice/AdapterProperties.java | 27 +++++++++++++ .../bluetooth/btservice/AdapterService.java | 21 ++++++++++ framework/api/system-current.txt | 1 + .../java/android/bluetooth/BluetoothAdapter.java | 47 ++++++++++++++++++++++ system/bta/dm/bta_dm_act.cc | 7 ++++ system/bta/include/bta_api.h | 9 +++-- system/btif/src/btif_core.cc | 9 +++++ system/btif/src/btif_dm.cc | 3 ++ system/include/hardware/bluetooth.h | 11 +++++ 11 files changed, 135 insertions(+), 4 deletions(-) diff --git a/android/app/aidl/android/bluetooth/IBluetooth.aidl b/android/app/aidl/android/bluetooth/IBluetooth.aidl index 45692376c8..0df873f790 100644 --- a/android/app/aidl/android/bluetooth/IBluetooth.aidl +++ b/android/app/aidl/android/bluetooth/IBluetooth.aidl @@ -338,4 +338,7 @@ interface IBluetooth @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); } diff --git a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java index f1f0b51691..981b2db0c7 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 @@ public final class AbstractionLayer { 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/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java index 851df2c030..c34e72b5c7 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java @@ -134,6 +134,8 @@ class AdapterProperties { private boolean mIsLeIsochronousBroadcasterSupported; private boolean mIsLeChannelSoundingSupported; + private int mNumberOfSupportedOffloadedLeCocSockets; + // Lock for all getters and setters. // If finer grained locking is needer, more locks // can be added here. @@ -908,6 +910,10 @@ class AdapterProperties { 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 +1028,27 @@ class AdapterProperties { mBufferConstraintList.complete(bufferConstraintList); } + /** + * @return the mNumberOfSupportedOffloadedLeCocSockets + */ + int getNumberOfSupportedOffloadedLeCocSockets() { + return mNumberOfSupportedOffloadedLeCocSockets; + } + + private void updateLppOffloadFeatureSupport(byte[] val) { + if (val.length < 1) { + Log.e(TAG, "BT_PROPERTY_LPP_OFFLOAD_FEATURES: invalid value length"); + return; + } + mNumberOfSupportedOffloadedLeCocSockets = (0xFF & ((int) val[0])); + + Log.d( + TAG, + "BT_PROPERTY_LPP_OFFLOAD_FEATURES: update from Offload HAL" + + " mNumberOfSupportedOffloadedLeCocSockets = " + + mNumberOfSupportedOffloadedLeCocSockets); + } + 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 3a3e3c13a2..1004cccf28 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -4387,6 +4387,16 @@ public class AdapterService extends Service { 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(); + } } /** @@ -7156,4 +7166,15 @@ public class AdapterService extends Service { 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; + } } diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index 06f611fcaa..2bcaf567ba 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -127,6 +127,7 @@ package android.bluetooth { 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 @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); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 2d26bf3e1d..8eff3060ff 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -5916,4 +5916,51 @@ public final class BluetoothAdapter { mServiceLock.readLock().unlock(); } } + + /** + * Returns whether LE CoC socket hardware offload is supported. + * + *

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. + * + *

To configure a socket for hardware offload, use the following {@link + * BluetoothSocketSettings} methods: + * + *

    + *
  • {@link BluetoothSocketSettings#setDataPath(int)} with {@link + * BluetoothSocketSettings#DATA_PATH_HARDWARE_OFFLOAD} + *
  • {@link BluetoothSocketSettings#setHubId(long)} + *
  • {@link BluetoothSocketSettings#setEndpointId(long)} + *
+ * + *

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; + } } diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc index ec3ca882bc..6b65525050 100644 --- a/system/bta/dm/bta_dm_act.cc +++ b/system/bta/dm/bta_dm_act.cc @@ -269,6 +269,13 @@ void BTA_dm_on_hw_on() { } } + 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/include/bta_api.h b/system/bta/include/bta_api.h index 90f1508c50..51533f8069 100644 --- a/system/bta/include/bta_api.h +++ b/system/bta/include/bta_api.h @@ -157,10 +157,11 @@ typedef struct { 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/btif/src/btif_core.cc b/system/btif/src/btif_core.cc index 73fe810e23..30361defa2 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 @@ void btif_get_adapter_property(bt_property_type_t type) { 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 8ed9face97..b9bd933899 100644 --- a/system/btif/src/btif_dm.cc +++ b/system/btif/src/btif_dm.cc @@ -2257,6 +2257,9 @@ void btif_dm_acl_evt(tBTA_DM_ACL_EVT event, tBTA_DM_ACL* p_data) { 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/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h index 02a8f51778..1be80e144b 100644 --- a/system/include/hardware/bluetooth.h +++ b/system/include/hardware/bluetooth.h @@ -238,6 +238,10 @@ typedef struct { 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 @@ typedef enum { */ 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; -- cgit v1.2.3-59-g8ed1b From 77fbb39b60c7bcafc758f6e5851ca57d23404583 Mon Sep 17 00:00:00 2001 From: Liang Li Date: Thu, 14 Nov 2024 07:24:14 +0000 Subject: Add bluetooth offload socket API Bug: 342012881 Bug: 367419086 Test: atest CtsBluetoothTestCases Change-Id: I936f27ed5abc0f01a9687bd1ddf32f820fcb9799 --- .../android/bluetooth/IBluetoothSocketManager.aidl | 4 + ..._android_bluetooth_btservice_AdapterService.cpp | 35 +- .../btservice/AdapterNativeInterface.java | 75 +++- .../btservice/BluetoothSocketManagerBinder.java | 153 +++++++- framework/api/current.txt | 4 +- framework/api/system-current.txt | 19 + .../java/android/bluetooth/BluetoothAdapter.java | 86 ++++- .../java/android/bluetooth/BluetoothDevice.java | 31 +- .../android/bluetooth/BluetoothServerSocket.java | 55 +++ .../java/android/bluetooth/BluetoothSocket.java | 274 +++++++++++++- .../bluetooth/BluetoothSocketException.java | 23 +- .../android/bluetooth/BluetoothSocketSettings.java | 414 +++++++++++++++++++-- system/btif/include/btif_sock_l2cap.h | 9 +- system/btif/src/btif_sock.cc | 39 +- system/btif/src/btif_sock_l2cap.cc | 79 +++- system/btif/src/btif_sock_rfc.cc | 1 + system/btif/test/btif_core_test.cc | 30 +- system/gd/os/rand.h | 7 + system/gd/rust/topshim/src/profiles/socket.rs | 27 +- system/include/hardware/bt_sock.h | 25 +- 20 files changed, 1263 insertions(+), 127 deletions(-) diff --git a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl index 1bbf692cda..05f647d4a9 100644 --- a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl +++ b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl @@ -29,8 +29,12 @@ interface IBluetoothSocketManager { @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_btservice_AdapterService.cpp b/android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp index 3a0a8d1aac..805e22b0df 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 jboolean setBufferLengthMillisNative(JNIEnv* /* env */, jobject /* obj */ } 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 @@ static jint connectSocketNative(JNIEnv* env, jobject /* obj */, jbyteArray addre } btUuid = Uuid::From128BitBE(reinterpret_cast(uuidBytes)); + if (socketName != nullptr) { + nativeSocketName = env->GetStringUTFChars(socketName, nullptr); + } if (sBluetoothSocketInterface->connect(reinterpret_cast(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 @@ done: 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 @@ static jint createSocketChannelNative(JNIEnv* env, jobject /* obj */, jint type, goto done; } btUuid = Uuid::From128BitBE(reinterpret_cast(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 @@ done: if (nativeServiceName) { env->ReleaseStringUTFChars(serviceName, nativeServiceName); } + if (nativeSocketName) { + env->ReleaseStringUTFChars(socketName, nativeSocketName); + } return socket_fd; } @@ -2267,8 +2287,9 @@ int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) { {"setBufferLengthMillisNative", "(II)Z", reinterpret_cast(setBufferLengthMillisNative)}, {"getMetricIdNative", "([B)I", reinterpret_cast(getMetricIdNative)}, - {"connectSocketNative", "([BI[BIII)I", reinterpret_cast(connectSocketNative)}, - {"createSocketChannelNative", "(ILjava/lang/String;[BIII)I", + {"connectSocketNative", "([BI[BIIIILjava/lang/String;JJI)I", + reinterpret_cast(connectSocketNative)}, + {"createSocketChannelNative", "(ILjava/lang/String;[BIIIILjava/lang/String;JJI)I", reinterpret_cast(createSocketChannelNative)}, {"requestMaximumTxDataLengthNative", "([B)V", reinterpret_cast(requestMaximumTxDataLengthNative)}, diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java b/android/app/src/com/android/bluetooth/btservice/AdapterNativeInterface.java index 33bf3c3f8f..64b5a0dfae 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 @@ public class AdapterNativeInterface { 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 @@ public class AdapterNativeInterface { 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/BluetoothSocketManagerBinder.java b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java index d3062c3707..05b0362f41 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 static android.Manifest.permission.BLUETOOTH_PRIVILEGED; 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 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { 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 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { 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/framework/api/current.txt b/framework/api/current.txt index f77b9ec329..8ee8e08f2b 100644 --- a/framework/api/current.txt +++ b/framework/api/current.txt @@ -52,7 +52,7 @@ package android.bluetooth { 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 @@ package android.bluetooth { 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 2bcaf567ba..957f72f1fd 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -1153,6 +1153,25 @@ package android.bluetooth { 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/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 8eff3060ff..334b2c75a2 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -4564,6 +4564,14 @@ public final class BluetoothAdapter { *

Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening * {@link BluetoothServerSocket}. * + *

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. + * + *

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}. + * *

This API supports {@link BluetoothSocket#TYPE_RFCOMM} and {{@link BluetoothSocket#TYPE_LE} * only, which can be set using {@link BluetoothSocketSettings#setSocketType()}. *

  • For `BluetoothSocket.TYPE_RFCOMM`: The RFCOMM UUID must be provided using {@link @@ -4574,10 +4582,6 @@ public final class BluetoothAdapter { * application exits unexpectedly. The mechanism for disclosing the PSM value to the client * is application-defined. * - *

    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 @@ public final class BluetoothAdapter { * 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 @@ public final class BluetoothAdapter { 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); } diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java index d200e6a149..795e911501 100644 --- a/framework/java/android/bluetooth/BluetoothDevice.java +++ b/framework/java/android/bluetooth/BluetoothDevice.java @@ -3366,13 +3366,30 @@ public final class BluetoothDevice implements Parcelable, Attributable { 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 b1a0188626..f20a03d1cc 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; @@ -153,6 +154,60 @@ public final class BluetoothServerSocket implements Closeable { mSocketCreationLatencyMillis = System.currentTimeMillis() - mSocketCreationTimeMillis; } + /** + * 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. * diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 901407c00b..273bfaa565 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -157,6 +157,8 @@ public final class BluetoothSocket implements Closeable { /*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 @@ public final class BluetoothSocket implements Closeable { 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,14 @@ public final class BluetoothSocket implements Closeable { @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 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 +254,41 @@ public final class BluetoothSocket implements Closeable { 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 +311,11 @@ public final class BluetoothSocket implements Closeable { 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 +371,55 @@ public final class BluetoothSocket implements Closeable { 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 +441,11 @@ public final class BluetoothSocket implements Closeable { mEncrypt = encrypt; mDevice = device; mPort = port; + mDataPath = dataPath; + mSocketName = socketName; + mHubId = hubId; + mEndpointId = endpointId; + mMaximumPacketSize = maximumPacketSize; mSocketState = SocketState.INIT; @@ -393,11 +496,17 @@ public final class BluetoothSocket implements Closeable { 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 +633,17 @@ public final class BluetoothSocket implements Closeable { * *

    {@link #close} can be used to abort this call from another thread. * + *

    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 +662,30 @@ public final class BluetoothSocket implements Closeable { 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 +813,7 @@ public final class BluetoothSocket implements Closeable { 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 +830,98 @@ public final class BluetoothSocket implements Closeable { 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) { @@ -836,6 +1066,7 @@ public final class BluetoothSocket implements Closeable { mPfd = null; } mConnectionUuid = null; + mSocketId = INVALID_SOCKET_ID; } } } @@ -1016,6 +1247,25 @@ public final class BluetoothSocket implements Closeable { 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() { @@ -1035,16 +1285,21 @@ public final class BluetoothSocket implements Closeable { } 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 +1311,7 @@ public final class BluetoothSocket implements Closeable { 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 +1329,9 @@ public final class BluetoothSocket implements Closeable { + " 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 aba95a375a..4bf84ad982 100644 --- a/framework/java/android/bluetooth/BluetoothSocketException.java +++ b/framework/java/android/bluetooth/BluetoothSocketException.java @@ -31,8 +31,8 @@ import java.lang.annotation.Retention; /** * Thrown when an error occurs during a Bluetooth Socket related exception. * - *

    This is currently only intended to be thrown for a failure during {@link - * BluetoothSocket#connect()} operation. + *

    This is currently intended to be thrown for a failure during {@link BluetoothSocket} + * operations. */ public class BluetoothSocketException extends IOException { @@ -70,8 +70,8 @@ public class BluetoothSocketException extends IOException { 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 @@ public class BluetoothSocketException extends IOException { /** 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 6fa8b0dc8b..b7706d50fe 100644 --- a/framework/java/android/bluetooth/BluetoothSocketSettings.java +++ b/framework/java/android/bluetooth/BluetoothSocketSettings.java @@ -18,14 +18,21 @@ package android.bluetooth; 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 @@ public final class BluetoothSocketSettings { 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. + * + *

    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; @@ -60,6 +125,50 @@ public final class BluetoothSocketSettings { /** RFCOMM service UUID associated with the Bluetooth socket. */ 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: + * + *

      + *
    • {@link #DATA_PATH_NO_OFFLOAD}: Suitable for applications that require the full + * processing capabilities of the main Bluetooth stack. + *
    • {@link #DATA_PATH_HARDWARE_OFFLOAD}: Optimized for lower power consumption by utilizing + * an offloaded application running on a dedicated low-power processor. + *
    + */ + @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. + * + *

    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. + * + *

    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. * @@ -119,36 +228,138 @@ public final class BluetoothSocketSettings { return mAuthenticationRequired; } + /** + * Returns the data path used for this socket. The data path determines how data is routed and + * processed for the socket connection. + * + *

    Defaults to {@link #DATA_PATH_NO_OFFLOAD}. + * + *

    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. + * + *

    When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the offloaded + * application running on the low-power processor. + * + *

    Defaults to {@code null} if no name was explicitly set. + * + *

    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}. + * + *

    If the data path is not set to {@link #DATA_PATH_HARDWARE_OFFLOAD}, this method returns + * {@link #INVALID_HUB_ID}. + * + *

    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. + * + *

    If the data path is not set to {@link #DATA_PATH_HARDWARE_OFFLOAD}, this method returns + * {@link #INVALID_ENDPOINT_ID}. + * + *

    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}. + * + *

    Defaults to {@link #HARDWARE_OFFLOAD_PACKET_MAX_SIZE}. + * + *

    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 @@ public final class BluetoothSocketSettings { 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 @@ public final class BluetoothSocketSettings { 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() {} @@ -302,6 +528,126 @@ public final class BluetoothSocketSettings { return this; } + /** + * Sets the data path for this socket. The data path determines how data is routed and + * processed for the socket connection. + * + *

    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. + * + *

    When using {@link #DATA_PATH_HARDWARE_OFFLOAD}, this name is also passed to the + * offloaded application running on low-power processor. + * + *

    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}. + * + *

    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. + * + *

    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}. + * + *

    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. + * + *

    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. * @@ -336,14 +682,32 @@ public final class BluetoothSocketSettings { + 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/system/btif/include/btif_sock_l2cap.h b/system/btif/include/btif_sock_l2cap.h index c08ef532d7..8b8f169204 100644 --- a/system/btif/include/btif_sock_l2cap.h +++ b/system/btif/include/btif_sock_l2cap.h @@ -22,16 +22,19 @@ #define BTIF_SOCK_L2CAP_H #include +#include #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); diff --git a/system/btif/src/btif_sock.cc b/system/btif/src/btif_sock.cc index 04b43dda0e..b07231dae5 100644 --- a/system/btif/src/btif_sock.cc +++ b/system/btif/src/btif_sock.cc @@ -43,9 +43,13 @@ using bluetooth::Uuid; 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, @@ -148,7 +152,8 @@ static bt_status_t btsock_control_req(uint8_t dlci, const RawAddress& bd_addr, u 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 +163,10 @@ static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, 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 +174,13 @@ static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, 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 +203,16 @@ static bt_status_t btsock_listen(btsock_type_t type, const char* service_name, } 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 +226,13 @@ static bt_status_t btsock_connect(const RawAddress* bd_addr, btsock_type_t type, 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_l2cap.cc b/system/btif/src/btif_sock_l2cap.cc index 3aa7162b78..e925201b1e 100644 --- a/system/btif/src/btif_sock_l2cap.cc +++ b/system/btif/src/btif_sock_l2cap.cc @@ -16,11 +16,11 @@ */ #include +#include #include #include #include #include -#include #include #include @@ -84,9 +84,15 @@ typedef struct l2cap_socket { 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 } l2cap_socket; static void btsock_l2cap_server_listen(l2cap_socket* sock); +static uint64_t btif_l2cap_sock_generate_socket_id(); static std::mutex state_lock; @@ -246,8 +252,8 @@ static void btsock_l2cap_free_l(l2cap_socket* sock) { 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, @@ -340,6 +346,11 @@ static l2cap_socket* btsock_l2cap_alloc_l(const char* name, const RawAddress* ad 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; if (name) { strncpy(sock->name, name, sizeof(sock->name) - 1); @@ -446,7 +457,7 @@ static uint64_t uuid_msb(const Uuid& uuid) { 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 +467,7 @@ static bool send_app_connect_signal(int fd, const RawAddress* addr, int channel, 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; @@ -541,10 +553,17 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* 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; Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom()); 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; /* 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 +573,9 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* 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,7 +586,7 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* 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. @@ -580,6 +600,7 @@ static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* sock->remote_cid = p_open->remote_cid; Uuid uuid = Uuid::From128BitBE(bluetooth::os::GenerateRandom()); sock->conn_uuid = uuid; + 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 +608,16 @@ static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* } 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, @@ -837,7 +859,10 @@ static void btsock_l2cap_server_listen(l2cap_socket* sock) { 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; } @@ -876,6 +901,13 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd 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; + 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) { @@ -909,14 +941,19 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd 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 @@ -1074,3 +1111,13 @@ bt_status_t btsock_l2cap_get_l2cap_remote_cid(Uuid& conn_uuid, uint16_t* cid) { *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; +} diff --git a/system/btif/src/btif_sock_rfc.cc b/system/btif/src/btif_sock_rfc.cc index e8bf6b42fe..0f9daa8f51 100644 --- a/system/btif/src/btif_sock_rfc.cc +++ b/system/btif/src/btif_sock_rfc.cc @@ -510,6 +510,7 @@ static bool send_app_connect_signal(int fd, const RawAddress* addr, int channel, 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 8d78ce68cf..66881a92d8 100644 --- a/system/btif/test/btif_core_test.cc +++ b/system/btif/test/btif_core_test.cc @@ -1022,9 +1022,14 @@ TEST_F(BtifCoreSocketTest, CreateRfcommServerSocket) { 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 @@ TEST_F(BtifCoreSocketTest, CreateTwoRfcommServerSockets) { 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 @@ TEST_F(BtifCoreSocketTest, CreateTwoRfcommServerSockets) { 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 @@ TEST_F(BtifCoreSocketTest, CreateManyRfcommServerSockets) { 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/os/rand.h b/system/gd/os/rand.h index 04e29d30f4..03f29bc815 100644 --- a/system/gd/os/rand.h +++ b/system/gd/os/rand.h @@ -40,5 +40,12 @@ inline uint32_t GenerateRandom() { 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/src/profiles/socket.rs b/system/gd/rust/topshim/src/profiles/socket.rs index 49a0f15cde..d8cfd4590a 100644 --- a/system/gd/rust/topshim/src/profiles/socket.rs +++ b/system/gd/rust/topshim/src/profiles/socket.rs @@ -164,6 +164,12 @@ impl BtSocket { 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 @@ impl BtSocket { 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 @@ impl BtSocket { 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 @@ impl BtSocket { 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 @@ mod tests { 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/bt_sock.h b/system/include/hardware/bt_sock.h index c7fff44daf..f3143d46ff 100644 --- a/system/include/hardware/bt_sock.h +++ b/system/include/hardware/bt_sock.h @@ -38,6 +38,18 @@ typedef enum { 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,6 +68,9 @@ typedef struct { // 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 { @@ -73,7 +88,8 @@ typedef struct { */ 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 +99,9 @@ typedef struct { * 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 +146,9 @@ __END_DECLS namespace std { template <> struct formatter : enum_formatter {}; + +template <> +struct formatter : enum_formatter {}; } // namespace std #endif // __has_include() -- cgit v1.2.3-59-g8ed1b From 88ba5e075efbc539445b21a77c743fe1d027a039 Mon Sep 17 00:00:00 2001 From: Liang Li Date: Thu, 14 Nov 2024 19:07:45 +0000 Subject: Send signal to indicate if app is accepting incoming connection on listen socket Bug: 342012881 Bug: 367419086 Test: m -j Change-Id: Ibc89a729699359cbd5aaa0fec971cb15848863b0 --- .../java/android/bluetooth/BluetoothSocket.java | 40 ++++++- system/btif/src/btif_sock_l2cap.cc | 117 ++++++++++++++------- system/include/hardware/bt_sock.h | 5 + 3 files changed, 123 insertions(+), 39 deletions(-) diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 273bfaa565..97bdf595a0 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -193,6 +193,7 @@ public final class BluetoothSocket implements Closeable { 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. @@ -931,7 +932,13 @@ public final class BluetoothSocket implements Closeable { 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); } @@ -1284,6 +1291,37 @@ public final class BluetoothSocket implements Closeable { addr[5]); } + /** + * Sends a socket accept signal to the host stack. + * + *

    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. + * + *

    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_CONNECTION_SIGNAL_SIZE]; int ret = readAll(is, sig); diff --git a/system/btif/src/btif_sock_l2cap.cc b/system/btif/src/btif_sock_l2cap.cc index e925201b1e..a6d724fc53 100644 --- a/system/btif/src/btif_sock_l2cap.cc +++ b/system/btif/src/btif_sock_l2cap.cc @@ -89,6 +89,7 @@ typedef struct l2cap_socket { 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); @@ -351,6 +352,7 @@ static l2cap_socket* btsock_l2cap_alloc_l(const char* name, const RawAddress* ad 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); @@ -543,7 +545,7 @@ static void on_cl_l2cap_init(tBTA_JV_L2CAP_CL_INIT* p_init, uint32_t id) { * 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; @@ -591,6 +593,8 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* // 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 + 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) { @@ -912,6 +916,8 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd /* "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 + 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; @@ -999,6 +1005,69 @@ inline uint8_t* get_l2cap_sdu_start_ptr(BT_HDR* msg) { 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(&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(int fd, int flags, uint32_t user_id) { char drop_it = false; @@ -1009,45 +1078,17 @@ void btsock_l2cap_signaled(int fd, int flags, uint32_t user_id) { if (!sock) { return; } - - if ((flags & SOCK_THREAD_FD_RD) && !sock->server) { - // app sending data - if (sock->connected) { - 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); + 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 { - drop_it = true; + // 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) { diff --git a/system/include/hardware/bt_sock.h b/system/include/hardware/bt_sock.h index f3143d46ff..0436fad9cb 100644 --- a/system/include/hardware/bt_sock.h +++ b/system/include/hardware/bt_sock.h @@ -73,6 +73,11 @@ typedef struct { 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; -- cgit v1.2.3-59-g8ed1b From 5c298722d78529e9f97842468a70f876cc35a764 Mon Sep 17 00:00:00 2001 From: Jayden Kim Date: Thu, 14 Nov 2024 19:38:32 +0000 Subject: Set the initial local credits to 0 for offload LE socket Bug: 342012881 Bug: 367419086 Test: m -j Change-Id: Ie2967885b07a2da27ee16027b894db65cfbf2f58 --- system/btif/src/btif_sock_l2cap.cc | 14 ++++++++++++++ system/stack/gap/gap_conn.cc | 3 ++- system/stack/include/l2cap_types.h | 2 ++ system/stack/l2cap/l2c_ble.cc | 5 +++-- system/stack/l2cap/l2c_utils.cc | 3 +++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/system/btif/src/btif_sock_l2cap.cc b/system/btif/src/btif_sock_l2cap.cc index a6d724fc53..a9cd4fbcc3 100644 --- a/system/btif/src/btif_sock_l2cap.cc +++ b/system/btif/src/btif_sock_l2cap.cc @@ -851,6 +851,13 @@ static void btsock_l2cap_server_listen(l2cap_socket* sock) { /* Setup ETM settings: mtu will be set below */ std::unique_ptr cfg = std::make_unique( 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 (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + cfg->init_credit_present = true; + cfg->init_credit = 0; + } std::unique_ptr ertm_info; if (!sock->is_le_coc) { @@ -925,6 +932,13 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd /* Setup ETM settings: mtu will be set below */ std::unique_ptr cfg = std::make_unique( 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 (sock->data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + cfg->init_credit_present = true; + cfg->init_credit = 0; + } std::unique_ptr ertm_info; if (!sock->is_le_coc) { diff --git a/system/stack/gap/gap_conn.cc b/system/stack/gap/gap_conn.cc index 127ded1cff..aad35dedfd 100644 --- a/system/stack/gap/gap_conn.cc +++ b/system/stack/gap/gap_conn.cc @@ -213,7 +213,8 @@ uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t service_id, bool is /* Configure L2CAP COC, if transport is LE */ if (transport == BT_TRANSPORT_LE) { - p_ccb->local_coc_cfg.credits = L2CA_LeCreditDefault(); + p_ccb->local_coc_cfg.credits = + (p_ccb->cfg.init_credit_present) ? p_ccb->cfg.init_credit : L2CA_LeCreditDefault(); p_ccb->local_coc_cfg.mtu = p_cfg->mtu; uint16_t max_mps = bluetooth::shim::GetController()->GetLeBufferSize().le_data_packet_length_; diff --git a/system/stack/include/l2cap_types.h b/system/stack/include/l2cap_types.h index 311dab7528..d2dd74bad7 100644 --- a/system/stack/include/l2cap_types.h +++ b/system/stack/include/l2cap_types.h @@ -129,6 +129,8 @@ typedef struct { 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/l2cap/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc index 9d69e5ab36..e33a024386 100644 --- a/system/stack/l2cap/l2c_ble.cc +++ b/system/stack/l2cap/l2c_ble.cc @@ -780,8 +780,9 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) { 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(); + p_ccb->local_conn_cfg.credits = p_rcb->coc_cfg.credits; + + p_ccb->remote_credit_count = p_rcb->coc_cfg.credits; 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 c04379bf89..c18ba8c5a9 100644 --- a/system/stack/l2cap/l2c_utils.cc +++ b/system/stack/l2cap/l2c_utils.cc @@ -3331,6 +3331,9 @@ void l2cu_send_peer_ble_credit_based_conn_res(tL2C_CCB* p_ccb, tL2CAP_LE_RESULT_ 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 */ -- cgit v1.2.3-59-g8ed1b From e4447aef49bc811234ee9ddd04f977380a947596 Mon Sep 17 00:00:00 2001 From: Jayden Kim Date: Thu, 14 Nov 2024 21:38:27 +0000 Subject: Determine local mtu for offload LE socket The local MTU is selected as the minimum of: - The socket hal's offload capabilities - The application's requested maximum RX packet size However, the MTU must be at least the minimum required by the L2CAP LE specification. Bug: 342012881 Bug: 367419086 Test: m -j Change-Id: Ia6360280b0527cb1218701d99a4ca424e38d83c9 --- system/btif/src/btif_sock_l2cap.cc | 40 +++++++++++++++++++++++++++++++++++--- system/stack/include/l2cdefs.h | 1 + system/stack/l2cap/l2c_ble.cc | 5 ++--- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/system/btif/src/btif_sock_l2cap.cc b/system/btif/src/btif_sock_l2cap.cc index a9cd4fbcc3..0efb43a928 100644 --- a/system/btif/src/btif_sock_l2cap.cc +++ b/system/btif/src/btif_sock_l2cap.cc @@ -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" @@ -868,12 +870,38 @@ static void btsock_l2cap_server_listen(l2cap_socket* sock) { 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(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, btsock_data_path_t data_path, const char* socket_name, uint64_t hub_id, - uint64_t endpoint_id, - int /* max_rx_packet_size */) { + uint64_t endpoint_id, int max_rx_packet_size) { if (!is_inited()) { return BT_STATUS_NOT_READY; } @@ -911,7 +939,13 @@ static bt_status_t btsock_l2cap_listen_or_connect(const char* name, const RawAdd 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 (data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { + if (!btsock_l2cap_get_offload_mtu(&sock->rx_mtu, static_cast(max_rx_packet_size))) { + return BT_STATUS_UNSUPPORTED; + } + } else { + sock->rx_mtu = is_le_coc ? L2CAP_SDU_LENGTH_LE_MAX : L2CAP_SDU_LENGTH_MAX; + } sock->data_path = data_path; if (socket_name) { strncpy(sock->socket_name, socket_name, sizeof(sock->socket_name) - 1); diff --git a/system/stack/include/l2cdefs.h b/system/stack/include/l2cdefs.h index 88ed7a76d3..be101711da 100644 --- a/system/stack/include/l2cdefs.h +++ b/system/stack/include/l2cdefs.h @@ -487,6 +487,7 @@ inline std::string l2cap_cfg_result_text(const tL2CAP_CFG_RESULT& result) { */ #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/l2c_ble.cc b/system/stack/l2cap/l2c_ble.cc index e33a024386..56b50f809f 100644 --- a/system/stack/l2cap/l2c_ble.cc +++ b/system/stack/l2cap/l2c_ble.cc @@ -777,9 +777,8 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) { 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.mtu = p_rcb->coc_cfg.mtu; + p_ccb->local_conn_cfg.mps = p_rcb->coc_cfg.mps; p_ccb->local_conn_cfg.credits = p_rcb->coc_cfg.credits; p_ccb->remote_credit_count = p_rcb->coc_cfg.credits; -- cgit v1.2.3-59-g8ed1b From 73d8ba1e8258b95914b3002486f57eba0cb28f7d Mon Sep 17 00:00:00 2001 From: Liang Li Date: Thu, 14 Nov 2024 23:32:37 +0000 Subject: Notify offloaded LE COC socket channel info to socket hal Bug: 342012881 Bug: 367419086 Test: m -j Change-Id: Iab67fb510becfe97088321c8683a2c61916d5496 --- system/bta/include/bta_jv_api.h | 5 + system/bta/jv/bta_jv_act.cc | 60 +++++++-- system/btif/Android.bp | 1 + system/btif/BUILD.gn | 1 + system/btif/include/btif_sock_hal.h | 21 ++++ system/btif/include/btif_sock_l2cap.h | 2 + system/btif/src/btif_sock.cc | 9 ++ system/btif/src/btif_sock_hal.cc | 56 +++++++++ system/btif/src/btif_sock_l2cap.cc | 205 ++++++++++++++++++++++++++++++- system/test/mock/mock_btif_sock_l2cap.cc | 66 ++++++++++ system/test/mock/mock_btif_sock_l2cap.h | 63 ++++++++++ 11 files changed, 476 insertions(+), 13 deletions(-) create mode 100644 system/btif/include/btif_sock_hal.h create mode 100644 system/btif/src/btif_sock_hal.cc create mode 100644 system/test/mock/mock_btif_sock_l2cap.cc create mode 100644 system/test/mock/mock_btif_sock_l2cap.h diff --git a/system/bta/include/bta_jv_api.h b/system/bta/include/bta_jv_api.h index 4b43cbc485..1416f61ae0 100644 --- a/system/bta/include/bta_jv_api.h +++ b/system/bta/include/bta_jv_api.h @@ -260,6 +260,11 @@ typedef struct { 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 9d4715269c..343e2430cc 100644 --- a/system/bta/jv/bta_jv_act.cc +++ b/system/bta/jv/bta_jv_act.cc @@ -982,11 +982,31 @@ static void bta_jv_l2cap_client_cback(uint16_t gap_handle, uint16_t event, tGAP_ 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 @@ static void bta_jv_l2cap_server_cback(uint16_t gap_handle, uint16_t event, tGAP_ 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/btif/Android.bp b/system/btif/Android.bp index d442e0c2cf..59846db8b0 100644 --- a/system/btif/Android.bp +++ b/system/btif/Android.bp @@ -201,6 +201,7 @@ cc_library_static { "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 519237997a..38b8fa9c6a 100644 --- a/system/btif/BUILD.gn +++ b/system/btif/BUILD.gn @@ -75,6 +75,7 @@ static_library("btif") { "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/include/btif_sock_hal.h b/system/btif/include/btif_sock_hal.h new file mode 100644 index 0000000000..9588110920 --- /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 + +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 8b8f169204..272436efe9 100644 --- a/system/btif/include/btif_sock_l2cap.h +++ b/system/btif/include/btif_sock_l2cap.h @@ -40,5 +40,7 @@ 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_sock.cc b/system/btif/src/btif_sock.cc index b07231dae5..fdeac4b77c 100644 --- a/system/btif/src/btif_sock.cc +++ b/system/btif/src/btif_sock.cc @@ -22,12 +22,14 @@ #include #include +#include #include #include #include #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" @@ -116,6 +118,13 @@ bt_status_t btif_sock_init(uid_set_t* uid_set) { 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: diff --git a/system/btif/src/btif_sock_hal.cc b/system/btif/src/btif_sock_hal.cc new file mode 100644 index 0000000000..48b40c541c --- /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(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 0efb43a928..e3c23a5647 100644 --- a/system/btif/src/btif_sock_l2cap.cc +++ b/system/btif/src/btif_sock_l2cap.cc @@ -67,6 +67,7 @@ typedef struct l2cap_socket { 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 @@ -96,6 +97,8 @@ typedef struct 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; @@ -343,6 +346,7 @@ static l2cap_socket* btsock_l2cap_alloc_l(const char* name, const RawAddress* ad sock->our_fd = fds[0]; sock->app_fd = fds[1]; + sock->listen_fd = -1; sock->security = security; sock->server = is_server; sock->connected = false; @@ -560,6 +564,7 @@ static void on_srv_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* 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()); accept_rs->conn_uuid = uuid; accept_rs->socket_id = btif_l2cap_sock_generate_socket_id(); @@ -604,6 +609,7 @@ static void on_cl_l2cap_psm_connect_l(tBTA_JV_L2CAP_OPEN* p_open, l2cap_socket* 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()); sock->conn_uuid = uuid; sock->socket_id = btif_l2cap_sock_generate_socket_id(); @@ -649,10 +655,18 @@ static void on_l2cap_connect(tBTA_JV* p_data, uint32_t id) { 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 (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); @@ -1210,3 +1224,188 @@ static uint64_t btif_l2cap_sock_generate_socket_id() { } 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 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 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()); + 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(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()); + 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(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/test/mock/mock_btif_sock_l2cap.cc b/system/test/mock/mock_btif_sock_l2cap.cc new file mode 100644 index 0000000000..41f19f8cd0 --- /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 + +#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 0000000000..f883f8bf1f --- /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 +#include + +// Original included files, if any +#include + +// 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 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 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 -- cgit v1.2.3-59-g8ed1b From aa0a9cb56d75abb17b0cecc6141431c3fb39a229 Mon Sep 17 00:00:00 2001 From: Liang Li Date: Thu, 21 Nov 2024 20:05:32 +0000 Subject: Read RFCOMM socket offload capabilities from bluetooth low power proccesor Bug: 342012881 Bug: 367419086 Test: atest CtsBluetoothTestCases Change-Id: I0cdc2efb0d1448ee65a44ac6d41597e14ab23d5a --- android/app/aidl/android/bluetooth/IBluetooth.aidl | 3 ++ .../bluetooth/btservice/AdapterProperties.java | 13 +++++- .../bluetooth/btservice/AdapterService.java | 21 ++++++++++ framework/api/system-current.txt | 1 + .../java/android/bluetooth/BluetoothAdapter.java | 47 ++++++++++++++++++++++ 5 files changed, 84 insertions(+), 1 deletion(-) diff --git a/android/app/aidl/android/bluetooth/IBluetooth.aidl b/android/app/aidl/android/bluetooth/IBluetooth.aidl index 0df873f790..158e37823f 100644 --- a/android/app/aidl/android/bluetooth/IBluetooth.aidl +++ b/android/app/aidl/android/bluetooth/IBluetooth.aidl @@ -341,4 +341,7 @@ interface IBluetooth @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/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java index c34e72b5c7..e0c3f0c59d 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java @@ -135,6 +135,7 @@ class AdapterProperties { private boolean mIsLeChannelSoundingSupported; private int mNumberOfSupportedOffloadedLeCocSockets; + private int mNumberOfSupportedOffloadedRfcommSockets = 0; // Lock for all getters and setters. // If finer grained locking is needer, more locks @@ -1035,18 +1036,28 @@ class AdapterProperties { 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); + + mNumberOfSupportedOffloadedLeCocSockets + + " mNumberOfSupportedOffloadedRfcommSockets = " + + mNumberOfSupportedOffloadedRfcommSockets); } void onBluetoothReady() { diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index 1004cccf28..3639b86940 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -4397,6 +4397,16 @@ public class AdapterService extends Service { 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(); + } } /** @@ -7177,4 +7187,15 @@ public class AdapterService extends Service { 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/framework/api/system-current.txt b/framework/api/system-current.txt index 957f72f1fd..8368f676b8 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -129,6 +129,7 @@ package android.bluetooth { 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); diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 334b2c75a2..2ffc178e6e 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -6009,4 +6009,51 @@ public final class BluetoothAdapter { } return false; } + + /** + * Returns whether RFCOMM socket hardware offload is supported. + * + *

    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. + * + *

    To configure a socket for hardware offload, use the following {@link + * BluetoothSocketSettings} methods: + * + *

      + *
    • {@link BluetoothSocketSettings#setDataPath(int)} with {@link + * BluetoothSocketSettings#DATA_PATH_HARDWARE_OFFLOAD} + *
    • {@link BluetoothSocketSettings#setHubId(long)} + *
    • {@link BluetoothSocketSettings#setEndpointId(long)} + *
    + * + *

    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; + } } -- cgit v1.2.3-59-g8ed1b From 5615c053c730befc06a851ad42ede829f570fc0a Mon Sep 17 00:00:00 2001 From: Jayden Kim Date: Sun, 24 Nov 2024 23:36:32 +0000 Subject: Add conditional privileged permission annotation on pbap client This change is just required along with adding bluetooth socket offload option, but it not actually used by the pbap client. Bug: 342012881 Bug: 367419086 Test: atest com.android.bluetooth.pbapclient Test: m com.android.btservices Change-Id: Ib17b46b73667177b19e4ac8c6ae79e988f926fde --- .../com/android/bluetooth/pbapclient/obex/PbapClientObexClient.java | 5 ++++- .../src/com/android/bluetooth/pbapclient/obex/PbapClientSocket.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) 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 760b67d900..8099c269c8 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 @@ class PbapClientObexClient { /* 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 4a17721a3e..7438a02fe9 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 @@ public class PbapClientSocket { } /** 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(); -- cgit v1.2.3-59-g8ed1b