Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 602bd1ef authored by William Escande's avatar William Escande Committed by Gerrit Code Review
Browse files

Merge changes I83e3cd14,I03330f82 into main

* changes:
  Prevent EATT teardown from affecting ACL lifetime
  AVATAR: add test for gatt server timeout
parents 1d2eec44 545bc4e4
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@

import asyncio
import avatar
import grpc
import logging

from avatar import BumblePandoraDevice, PandoraDevice, PandoraDevices
@@ -23,6 +24,7 @@ from bumble.pairing import PairingConfig
from bumble_experimental.gatt import GATTService
from mobly import base_test, signals, test_runner
from mobly.asserts import assert_equal  # type: ignore
from mobly.asserts import assert_true  # type: ignore
from mobly.asserts import assert_in  # type: ignore
from mobly.asserts import assert_is_not_none  # type: ignore
from mobly.asserts import assert_not_in  # type: ignore
@@ -238,6 +240,59 @@ class GattTest(base_test.BaseTestClass): # type: ignore[misc]
        assert_in(SERVICE_UUID_1, (service.uuid for service in second_discovery.services))
        assert_in(SERVICE_UUID_2, (service.uuid for service in second_discovery.services))

    @avatar.asynchronous
    async def test_eatt_when_not_encrypted_no_timeout(self) -> None:
        if not isinstance(self.ref, BumblePandoraDevice):
            raise signals.TestSkip('Test require Bumble as reference device(s)')
        advertise = self.dut.aio.host.Advertise(
            legacy=True,
            connectable=True,
            own_address_type=RANDOM,
            data=DataTypes(manufacturer_specific_data=b'pause cafe'),
        )

        scan = self.ref.aio.host.Scan()
        dut = await anext((x async for x in scan if b'pause cafe' in x.data.manufacturer_specific_data))
        scan.cancel()

        ref_dut = (await self.ref.aio.host.ConnectLE(own_address_type=RANDOM, **dut.address_asdict())).connection
        assert_is_not_none(ref_dut)
        assert ref_dut
        advertise.cancel()

        connection = self.ref.device.lookup_connection(int.from_bytes(ref_dut.cookie.value, 'big'))
        assert connection

        connection_request = (
            b"\x17"  # code of L2CAP_CREDIT_BASED_CONNECTION_REQ
            b"\x01"  # identifier
            b"\x0a\x00"  # data length
            b"\x27\x00"  # psm(EATT)
            b"\x64\x00"  # MTU
            b"\x64\x00"  # MPS
            b"\x64\x00"  # initial credit
            b"\x40\x00"  # source cid[0]
        )

        fut = asyncio.get_running_loop().create_future()
        setattr(self.ref.device.l2cap_channel_manager, "on_[0x18]", lambda _, _1, frame: fut.set_result(frame))
        self.ref.device.l2cap_channel_manager.send_control_frame(  # type:ignore
            connection, 0x05, connection_request
        )
        control_frame = await fut

        assert_equal(bytes(control_frame)[10], 0x05)  # All connections refused – insufficient authentication
        assert_true(await is_connected(self.ref, ref_dut), "Device is no longer connected")


async def is_connected(device: PandoraDevice, connection: Connection) -> bool:
    try:
        await device.aio.host.WaitDisconnection(connection=connection, timeout=5)
        return False
    except grpc.RpcError as e:
        assert_equal(e.code(), grpc.StatusCode.DEADLINE_EXCEEDED)  # type: ignore
        return True


if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
+2 −1
Original line number Diff line number Diff line
@@ -838,7 +838,8 @@ std::vector<uint16_t> L2CA_ConnectCreditBasedReq(uint16_t psm,

  for (int i = 0; i < p_cfg->number_of_channels; i++) {
    /* Allocate a channel control block */
    tL2C_CCB* p_ccb = l2cu_allocate_ccb(p_lcb, 0);
    tL2C_CCB* p_ccb =
        l2cu_allocate_ccb(p_lcb, 0, psm == BT_PSM_EATT /* is_eatt */);
    if (p_ccb == NULL) {
      if (i == 0) {
        L2CAP_TRACE_WARNING("%s no CCB, PSM: 0x%04x", __func__, psm);
+4 −2
Original line number Diff line number Diff line
@@ -657,7 +657,8 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
              L2CAP_LE_RESULT_SOURCE_CID_ALREADY_ALLOCATED;
        } else {
          /* Allocate a ccb for this.*/
          temp_p_ccb = l2cu_allocate_ccb(p_lcb, 0);
          temp_p_ccb = l2cu_allocate_ccb(
              p_lcb, 0, con_info.psm == BT_PSM_EATT /* is_eatt */);
          if (temp_p_ccb == NULL) {
            LOG_ERROR("L2CAP - unable to allocate CCB");
            p_lcb->pending_ecoc_connection_cids[i] = 0;
@@ -1004,7 +1005,8 @@ void l2cble_process_sig_cmd(tL2C_LCB* p_lcb, uint8_t* p, uint16_t pkt_len) {
      }

      /* Allocate a ccb for this.*/
      p_ccb = l2cu_allocate_ccb(p_lcb, 0);
      p_ccb = l2cu_allocate_ccb(p_lcb, 0,
                                con_info.psm == BT_PSM_EATT /* is_eatt */);
      if (p_ccb == NULL) {
        L2CAP_TRACE_ERROR("L2CAP - unable to allocate CCB");
        l2cu_reject_ble_connection(p_ccb, id, L2CAP_CONN_NO_RESOURCES);
+2 −1
Original line number Diff line number Diff line
@@ -719,7 +719,8 @@ void l2cu_enqueue_ccb(tL2C_CCB* p_ccb);
void l2cu_dequeue_ccb(tL2C_CCB* p_ccb);
void l2cu_change_pri_ccb(tL2C_CCB* p_ccb, tL2CAP_CHNL_PRIORITY priority);

tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid);
tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid,
                            bool is_eatt = false);
void l2cu_release_ccb(tL2C_CCB* p_ccb);
tL2C_CCB* l2cu_find_ccb_by_cid(tL2C_LCB* p_lcb, uint16_t local_cid);
tL2C_CCB* l2cu_find_ccb_by_remote_cid(tL2C_LCB* p_lcb, uint16_t remote_cid);
+7 −2
Original line number Diff line number Diff line
@@ -1351,7 +1351,7 @@ void l2cu_change_pri_ccb(tL2C_CCB* p_ccb, tL2CAP_CHNL_PRIORITY priority) {
 * Returns          pointer to CCB, or NULL if none
 *
 ******************************************************************************/
tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid) {
tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid, bool is_eatt) {
  LOG_DEBUG("is_dynamic = %d, cid 0x%04x", p_lcb != nullptr, cid);
  if (!l2cb.p_free_ccb_first) {
    LOG_ERROR("First free ccb is null for cid 0x%04x", cid);
@@ -1471,8 +1471,13 @@ tL2C_CCB* l2cu_allocate_ccb(tL2C_LCB* p_lcb, uint16_t cid) {

  if (p_lcb != NULL) {
    // once a dynamic channel is opened, timeouts become active
    // the exception for this is EATT, since that is managed by GATT clients,
    // not by the L2CAP layer (GATT will keep the idle timeout at infinity while
    // clients are active)
    if (!is_eatt) {
      p_lcb->with_active_local_clients = true;
    }
  }

  return p_ccb;
}
Loading