OSDN Git Service

Cert test scaffolding for iso layer
authorJakub Pawlowski <jpawlowski@google.com>
Tue, 23 Feb 2021 08:19:43 +0000 (09:19 +0100)
committerJakub Pawlowski <jpawlowski@google.com>
Fri, 5 Mar 2021 07:47:36 +0000 (07:47 +0000)
Tag: #feature
Test: cert/run LeIsoTest
Bug: 177907117
Change-Id: Icc23db17d5f6fc8d0fd1f9497a64c99d9f0cfd5e

17 files changed:
gd/Android.bp
gd/cert/all_cert_testcases
gd/cert/gd_device.py
gd/cert/matchers.py
gd/cert/py_le_iso.py [new file with mode: 0644]
gd/facade/grpc_root_server.cc
gd/iso/Android.bp
gd/iso/cert/cert_le_iso.py [new file with mode: 0644]
gd/iso/cert/le_iso_test.py [new file with mode: 0644]
gd/iso/facade.cc [new file with mode: 0644]
gd/iso/facade.h [new file with mode: 0644]
gd/iso/facade.proto [new file with mode: 0644]
gd/iso/internal/iso_manager_impl.cc
gd/iso/internal/iso_manager_impl.h
gd/iso/iso_manager.cc
gd/iso/iso_manager.h
gd/iso/iso_module.cc

index cd2648d..bd3a8a4 100644 (file)
@@ -220,6 +220,7 @@ cc_binary {
         ":BluetoothFacade_hci_layer",
         ":BluetoothFacade_l2cap_layer",
         ":BluetoothFacade_neighbor",
+        ":BluetoothFacade_iso_layer",
         ":BluetoothFacade_security_layer",
         ":BluetoothFacade_shim_layer",
     ],
@@ -652,6 +653,7 @@ filegroup {
         "hci/facade/le_advertising_manager_facade.proto",
         "hci/facade/le_initiator_address_facade.proto",
         "hci/facade/le_scanning_manager_facade.proto",
+        "iso/facade.proto",
         "neighbor/facade/facade.proto",
         "l2cap/classic/facade.proto",
         "l2cap/le/facade.proto",
@@ -691,6 +693,8 @@ genrule {
         "hci/facade/le_initiator_address_facade.pb.h",
         "hci/facade/le_scanning_manager_facade.grpc.pb.h",
         "hci/facade/le_scanning_manager_facade.pb.h",
+        "iso/facade.grpc.pb.h",
+        "iso/facade.pb.h",
         "l2cap/classic/facade.grpc.pb.h",
         "l2cap/classic/facade.pb.h",
         "l2cap/le/facade.grpc.pb.h",
@@ -735,6 +739,8 @@ genrule {
         "hci/facade/le_initiator_address_facade.pb.cc",
         "hci/facade/le_scanning_manager_facade.grpc.pb.cc",
         "hci/facade/le_scanning_manager_facade.pb.cc",
+        "iso/facade.grpc.pb.cc",
+        "iso/facade.pb.cc",
         "l2cap/classic/facade.grpc.pb.cc",
         "l2cap/classic/facade.pb.cc",
         "l2cap/le/facade.grpc.pb.cc",
@@ -763,6 +769,7 @@ genrule {
         "touch $(genDir)/files/hal/__init__.py && " +
         "touch $(genDir)/files/hci/__init__.py && " +
         "touch $(genDir)/files/hci/facade/__init__.py && " +
+        "touch $(genDir)/files/iso/__init__.py && " +
         "touch $(genDir)/files/l2cap/classic/__init__.py && " +
         "touch $(genDir)/files/l2cap/le/__init__.py && " +
         "touch $(genDir)/files/neighbor/facade/__init__.py && " +
index d81f85d..fbbcaee 100644 (file)
@@ -16,3 +16,4 @@ LeSecurityTest
 L2capPerformanceTest
 SecurityTest
 ShimTest
+LeIsoTest
index 15f383f..65d85d2 100644 (file)
@@ -56,6 +56,7 @@ from hci.facade import le_initiator_address_facade_pb2_grpc
 from hci.facade import le_scanning_manager_facade_pb2_grpc
 from l2cap.classic import facade_pb2_grpc as l2cap_facade_pb2_grpc
 from l2cap.le import facade_pb2_grpc as l2cap_le_facade_pb2_grpc
+from iso import facade_pb2_grpc as iso_facade_pb2_grpc
 from neighbor.facade import facade_pb2_grpc as neighbor_facade_pb2_grpc
 from security import facade_pb2_grpc as security_facade_pb2_grpc
 from shim.facade import facade_pb2_grpc as shim_facade_pb2_grpc
@@ -236,6 +237,7 @@ class GdDeviceBase(ABC):
         self.hci = hci_facade_pb2_grpc.HciFacadeStub(self.grpc_channel)
         self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(self.grpc_channel)
         self.l2cap_le = l2cap_le_facade_pb2_grpc.L2capLeModuleFacadeStub(self.grpc_channel)
+        self.iso = iso_facade_pb2_grpc.IsoModuleFacadeStub(self.grpc_channel)
         self.hci_acl_manager = acl_manager_facade_pb2_grpc.AclManagerFacadeStub(self.grpc_channel)
         self.hci_le_acl_manager = le_acl_manager_facade_pb2_grpc.LeAclManagerFacadeStub(self.grpc_channel)
         self.hci_le_initiator_address = le_initiator_address_facade_pb2_grpc.LeInitiatorAddressFacadeStub(
index 6e7465f..5d8fa1f 100644 (file)
@@ -716,3 +716,14 @@ class SecurityMatchers(object):
     @staticmethod
     def HelperMsg(type, address=None):
         return lambda event: True if event.message_type == type and (address == None or address == event.peer) else False
+
+
+class IsoMatchers(object):
+
+    @staticmethod
+    def Data(payload):
+        return lambda packet: packet.payload == payload
+
+    @staticmethod
+    def PacketPayloadWithMatchingCisHandle(cis_handle):
+        return lambda packet: None if cis_handle != packet.handle else packet
diff --git a/gd/cert/py_le_iso.py b/gd/cert/py_le_iso.py
new file mode 100644 (file)
index 0000000..bc22f49
--- /dev/null
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - 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.
+
+import logging
+
+from bluetooth_packets_python3 import hci_packets
+from cert.capture import Capture
+from cert.captures import SecurityCaptures
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.event_stream import EventStream, IEventStream
+from cert.event_stream import FilteringEventStream
+from cert.matchers import IsoMatchers
+from cert.truth import assertThat
+from datetime import timedelta
+from facade import common_pb2 as common
+from google.protobuf import empty_pb2 as empty_proto
+from iso import facade_pb2 as iso_facade_pb2
+
+
+class CisTestParameters():
+
+    def __init__(self, cis_id, nse, max_sdu_m_to_s, max_sdu_s_to_m, max_pdu_m_to_s, max_pdu_s_to_m, phy_m_to_s,
+                 phy_s_to_m, bn_m_to_s, bn_s_to_m):
+        self.cis_id = cis_id
+        self.nse = nse
+        self.max_sdu_m_to_s = max_sdu_m_to_s
+        self.max_sdu_s_to_m = max_sdu_s_to_m
+        self.max_pdu_m_to_s = max_pdu_m_to_s
+        self.max_pdu_s_to_m = max_pdu_s_to_m
+        self.phy_m_to_s = phy_m_to_s
+        self.phy_s_to_m = phy_s_to_m
+        self.bn_m_to_s = bn_m_to_s
+        self.bn_s_to_m = bn_s_to_m
+
+
+class PyLeIsoStream(IEventStream):
+
+    def __init__(self, device, cis_handle, iso_data_stream):
+        self._device = device
+        self._cis_handle = cis_handle
+        self._le_iso_data_stream = iso_data_stream
+        self._our_le_iso_cis_view = FilteringEventStream(
+            self._le_iso_data_stream, IsoMatchers.PacketPayloadWithMatchingCisHandle(self._cis_handle))
+
+    def get_event_queue(self):
+        return self._our_le_iso_cis_view.get_event_queue()
+
+    def send(self, payload):
+        self._device.iso.SendIsoPacket(iso_facade_pb2.IsoPacket(handle=self._cis_handle, payload=payload))
+
+
+class PyLeIso(Closable):
+    """
+        Abstraction for iso tasks and GRPC calls
+    """
+
+    _iso_event_stream = None
+
+    def __init__(self, device):
+        logging.info("DUT: Init")
+        self._device = device
+        self._device.wait_channel_ready()
+        self._iso_event_stream = EventStream(self._device.iso.FetchIsoEvents(empty_proto.Empty()))
+        self._iso_data_stream = EventStream(self._device.iso.FetchIsoData(empty_proto.Empty()))
+
+    def close(self):
+        if self._iso_event_stream is not None:
+            safeClose(self._iso_event_stream)
+        else:
+            logging.info("DUT: ISO Event Stream is None!")
+        if self._iso_data_stream is not None:
+            safeClose(self._iso_data_stream)
+        else:
+            logging.info("DUT: ISO Data Stream is None!")
+
+        logging.info("DUT: close")
+
+    def le_set_cig_parameters(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, peripherals_clock_accuracy,
+                              packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_id,
+                              max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, rtn_m_to_s, rtn_s_to_m):
+
+        resp = self._device.iso.LeSetCigParameters(
+            iso_facade_pb2.LeSetCigParametersRequest(
+                cig_id=cig_id,
+                sdu_interval_m_to_s=sdu_interval_m_to_s,
+                sdu_interval_s_to_m=sdu_interval_s_to_m,
+                peripherals_clock_accuracy=peripherals_clock_accuracy,
+                packing=packing,
+                framing=framing,
+                max_transport_latency_m_to_s=max_transport_latency_m_to_s,
+                max_transport_latency_s_to_m=max_transport_latency_s_to_m,
+                cis_id=cis_id,
+                max_sdu_m_to_s=max_sdu_m_to_s,
+                max_sdu_s_to_m=max_sdu_s_to_m,
+                phy_m_to_s=phy_m_to_s,
+                phy_s_to_m=phy_s_to_m,
+                rtn_m_to_s=rtn_m_to_s,
+                rtn_s_to_m=rtn_s_to_m))
+
+    def le_set_cig_parameters_test(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m,
+                                   iso_interval, peripherals_clock_accuracy, packing, framing,
+                                   max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_configs):
+        configs = []
+        for cc in cis_configs:
+            configs.append(
+                iso_facade_pb2.LeSetCigParametersTestRequest.LeCisParametersTestConfig(
+                    cis_id=cc.cis_id,
+                    nse=cc.nse,
+                    max_sdu_m_to_s=cc.max_sdu_m_to_s,
+                    max_sdu_s_to_m=cc.max_sdu_s_to_m,
+                    max_pdu_m_to_s=cc.max_pdu_m_to_s,
+                    max_pdu_s_to_m=cc.max_pdu_s_to_m,
+                    phy_m_to_s=cc.phy_m_to_s,
+                    phy_s_to_m=cc.phy_s_to_m,
+                    bn_m_to_s=cc.bn_m_to_s,
+                    bn_s_to_m=cc.bn_s_to_m,
+                ))
+
+        resp = self._device.iso.LeSetCigParameters(
+            iso_facade_pb2.LeSetCigParametersTestRequest(
+                cig_id=cig_id,
+                sdu_interval_m_to_s=sdu_interval_m_to_s,
+                sdu_interval_s_to_m=sdu_interval_s_to_m,
+                ft_m_to_s=ft_m_to_s,
+                ft_s_to_m=ft_s_to_m,
+                iso_interval=iso_interval,
+                peripherals_clock_accuracy=peripherals_clock_accuracy,
+                packing=packing,
+                framing=framing,
+                max_transport_latency_m_to_s=max_transport_latency_m_to_s,
+                max_transport_latency_s_to_m=max_transport_latency_s_to_m,
+                cis_configs=configs))
+
+    def wait_le_set_cig_parameters_complete(self):
+        set_cig_params_complete_capture = PyLeIso.IsoCigComplete(iso_facade_pb2.IsoMsgType.ISO_PARAMETERS_SET_COMPLETE)
+
+        assertThat(self._iso_event_stream).emits(set_cig_params_complete_capture, timeout=timedelta(seconds=5))
+        return set_cig_params_complete_capture.get()
+
+    @staticmethod
+    def IsoCigComplete(type=None):
+        return Capture(lambda event: True if event.message_type == type else False, PyLeIso._extract_cis_handles)
+
+    @staticmethod
+    def _extract_cis_handles(event):
+        if event is None:
+            return None
+        return event.cis_handle
+
+    def le_create_cis(self, cis_and_acl_handle_array):
+        handles_pairs = []
+        for hp_tmp in cis_and_acl_handle_array:
+            handles_pairs.append(
+                iso_facade_pb2.LeCreateCisRequest.HandlePair(cis_handle=hp_tmp[0], acl_handle=hp_tmp[1]))
+
+        self._device.iso.LeCreateCis(iso_facade_pb2.LeCreateCisRequest(handle_pair=handles_pairs))
+
+    def wait_le_cis_established(self):
+        cis_establshed_capture = PyLeIso.IsoCigEstablished(iso_facade_pb2.IsoMsgType.ISO_CIS_ESTABLISHED)
+        assertThat(self._iso_event_stream).emits(cis_establshed_capture, timeout=timedelta(seconds=5))
+        cis_handle = cis_establshed_capture.get()[0]
+        return PyLeIsoStream(self._device, cis_handle, self._iso_data_stream)
+
+    @staticmethod
+    def IsoCigEstablished(type):
+        return Capture(lambda event: True if event.message_type == type else False, PyLeIso._extract_cis_handles)
index edf68d0..21e98e4 100644 (file)
@@ -32,6 +32,7 @@
 #include "hci/hci_layer.h"
 #include "hci/le_advertising_manager.h"
 #include "hci/le_scanning_manager.h"
+#include "iso/facade.h"
 #include "l2cap/classic/facade.h"
 #include "l2cap/le/facade.h"
 #include "neighbor/connectability.h"
@@ -90,6 +91,7 @@ class RootFacadeService : public ::bluetooth::facade::RootFacade::Service {
         modules.add<::bluetooth::hci::facade::LeInitiatorAddressFacadeModule>();
         modules.add<::bluetooth::hci::facade::LeScanningManagerFacadeModule>();
         modules.add<::bluetooth::neighbor::facade::NeighborFacadeModule>();
+        modules.add<::bluetooth::iso::IsoModuleFacadeModule>();
         break;
       case BluetoothModule::L2CAP:
         modules.add<::bluetooth::hci::facade::ControllerFacadeModule>();
@@ -100,6 +102,7 @@ class RootFacadeService : public ::bluetooth::facade::RootFacade::Service {
         modules.add<::bluetooth::l2cap::classic::L2capClassicModuleFacadeModule>();
         modules.add<::bluetooth::l2cap::le::L2capLeModuleFacadeModule>();
         modules.add<::bluetooth::hci::facade::HciFacadeModule>();
+        modules.add<::bluetooth::iso::IsoModuleFacadeModule>();
         break;
       case BluetoothModule::SECURITY:
         modules.add<::bluetooth::facade::ReadOnlyPropertyServerModule>();
index 060b115..6d07c39 100644 (file)
@@ -25,5 +25,6 @@ filegroup {
 filegroup {
     name: "BluetoothFacade_iso_layer",
     srcs: [
+        "facade.cc",
     ],
 }
diff --git a/gd/iso/cert/cert_le_iso.py b/gd/iso/cert/cert_le_iso.py
new file mode 100644 (file)
index 0000000..9404364
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+#
+#   Copyright 2021 - 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.
+
+import logging
+
+from cert.closable import Closable
+from cert.closable import safeClose
+from cert.py_le_iso import PyLeIso
+import bluetooth_packets_python3 as bt_packets
+from bluetooth_packets_python3 import l2cap_packets
+
+
+class CertLeIso(Closable):
+
+    def __init__(self, device):
+        self._device = device
+        self._le_iso = PyLeIso(device)
+
+    def close(self):
+        logging.info("DUT: close")
+        self._le_iso.close()
+
+    def le_set_cig_parameters(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, peripherals_clock_accuracy,
+                              packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_id,
+                              max_sdu_m_to_s, max_sdu_s_to_m, phy_m_to_s, phy_s_to_m, rtn_m_to_s, rtn_s_to_m):
+        return self._le_iso.le_set_cig_parameters(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, peripherals_clock_accuracy, packing, framing,
+            max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_id, max_sdu_m_to_s, max_sdu_s_to_m,
+            phy_m_to_s, phy_s_to_m, rtn_m_to_s, rtn_s_to_m)
+
+    def le_set_cig_parameters_test(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m,
+                                   iso_interval, peripherals_clock_accuracy, packing, framing,
+                                   max_transport_latency_m_to_s, max_transport_latency_s_to_m, cis_configs):
+        return self._le_iso.le_set_cig_parameters_test(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s,
+                                                       ft_s_to_m, iso_interval, peripherals_clock_accuracy, packing,
+                                                       framing, max_transport_latency_m_to_s,
+                                                       max_transport_latency_s_to_m, cis_configs)
+
+    def wait_le_set_cig_parameters_complete(self):
+        return self._le_iso.wait_le_set_cig_parameters_complete()
+
+    def le_cretate_cis(self, cis_and_acl_handle_array):
+        self._le_iso.le_create_cis(cis_and_acl_handle_array)
+
+    def wait_le_cis_established(self):
+        return self._le_iso.wait_le_cis_established()
diff --git a/gd/iso/cert/le_iso_test.py b/gd/iso/cert/le_iso_test.py
new file mode 100644 (file)
index 0000000..c1d3d65
--- /dev/null
@@ -0,0 +1,286 @@
+#
+#   Copyright 2021 - 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.
+
+import time
+import logging
+
+from bluetooth_packets_python3 import hci_packets
+from cert.event_stream import EventStream
+from cert.gd_base_test import GdBaseTestClass
+from cert.matchers import HciMatchers, IsoMatchers, L2capMatchers
+from cert.metadata import metadata
+from cert.py_hci import PyHci
+from cert.py_l2cap import PyLeL2cap
+from cert.py_le_iso import PyLeIso
+from cert.py_le_iso import CisTestParameters
+from cert.truth import assertThat
+from datetime import timedelta
+from facade import common_pb2 as common
+from hci.facade import controller_facade_pb2 as controller_facade
+from hci.facade import le_advertising_manager_facade_pb2 as le_advertising_facade
+from hci.facade import le_initiator_address_facade_pb2 as le_initiator_address_facade
+from google.protobuf import empty_pb2 as empty_proto
+from neighbor.facade import facade_pb2 as neighbor_facade
+from l2cap.le.cert.cert_le_l2cap import CertLeL2cap
+from iso.cert.cert_le_iso import CertLeIso
+
+import time
+from bluetooth_packets_python3.hci_packets import OpCode
+
+
+class LeIsoTest(GdBaseTestClass):
+    """
+        Collection of tests that each sample results from
+        different (unique) combinations of io capabilities, authentication requirements, and oob data.
+    """
+
+    def setup_class(self):
+        super().setup_class(dut_module='L2CAP', cert_module='HCI_INTERFACES')
+
+    def setup_test(self):
+        super().setup_test()
+
+        self.dut_l2cap = PyLeL2cap(self.dut)
+        self.cert_l2cap = CertLeL2cap(self.cert)
+        self.dut_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'D0:05:04:03:02:01')), type=common.RANDOM_DEVICE_ADDRESS)
+        self.cert_address = common.BluetoothAddressWithType(
+            address=common.BluetoothAddress(address=bytes(b'C0:11:FF:AA:33:22')), type=common.RANDOM_DEVICE_ADDRESS)
+        dut_privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.dut_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.dut_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(dut_privacy_policy)
+        privacy_policy = le_initiator_address_facade.PrivacyPolicy(
+            address_policy=le_initiator_address_facade.AddressPolicy.USE_STATIC_ADDRESS,
+            address_with_type=self.cert_address,
+            rotation_irk=b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
+            minimum_rotation_time=0,
+            maximum_rotation_time=0)
+        self.cert_l2cap._device.hci_le_initiator_address.SetPrivacyPolicyForInitiatorAddress(privacy_policy)
+
+        self.dut_iso = PyLeIso(self.dut)
+        self.cert_iso = CertLeIso(self.cert)
+
+    def teardown_test(self):
+        self.dut_iso.close()
+        self.cert_iso.close()
+
+        self.cert_l2cap.close()
+        self.dut_l2cap.close()
+        super().teardown_test()
+
+    #cert becomes central of connection, dut peripheral
+    def _setup_link_from_cert(self):
+        # DUT Advertises
+        gap_name = hci_packets.GapData()
+        gap_name.data_type = hci_packets.GapDataType.COMPLETE_LOCAL_NAME
+        gap_name.data = list(bytes(b'Im_The_DUT'))
+        gap_data = le_advertising_facade.GapDataMsg(data=bytes(gap_name.Serialize()))
+        config = le_advertising_facade.AdvertisingConfig(
+            advertisement=[gap_data],
+            interval_min=512,
+            interval_max=768,
+            advertising_type=le_advertising_facade.AdvertisingEventType.ADV_IND,
+            own_address_type=common.USE_RANDOM_DEVICE_ADDRESS,
+            channel_map=7,
+            filter_policy=le_advertising_facade.AdvertisingFilterPolicy.ALL_DEVICES)
+        request = le_advertising_facade.CreateAdvertiserRequest(config=config)
+        create_response = self.dut.hci_le_advertising_manager.CreateAdvertiser(request)
+        self.cert_l2cap.connect_le_acl(self.dut_address)
+
+    def _setup_cis_from_cert(self, cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+                             peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s,
+                             max_transport_latency_s_to_m, cis_configs):
+        self.cert_iso.le_set_cig_parameters_test(cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m,
+                                                 iso_interval, peripherals_clock_accuracy, packing, framing,
+                                                 max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+                                                 cis_configs)
+
+        cis_handles = self.cert_iso.wait_le_set_cig_parameters_complete()
+
+        cis_handle = cis_handles[0]
+
+        acl_connection_handle = self.cert_l2cap._le_acl.handle
+        self.cert_iso.le_cretate_cis([(cis_handle, acl_connection_handle)])
+        dut_cis_stream = self.dut_iso.wait_le_cis_established()
+        cert_cis_stream = self.cert_iso.wait_le_cis_established()
+        return (dut_cis_stream, cert_cis_stream)
+
+    @metadata(
+        pts_test_id="IAL/CIS/UNF/SLA/BV-01-C",
+        pts_test_name="connected isochronous stream, unframed data, peripheral role")
+    def test_iso_cis_unf_sla_bv_01_c(self):
+        """
+            Verify that the IUT can send an SDU with length â‰¤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0
+        sdu_interval_s_to_m = 0x186a
+        ft_m_to_s = 0
+        ft_s_to_m = 1
+        iso_interval = 0x0A
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 0
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=2,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=0,
+                bn_s_to_m=2,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
+
+    @metadata(
+        pts_test_id="IAL/CIS/UNF/SLA/BV-25-C",
+        pts_test_name="connected isochronous stream, unframed data, peripheral role")
+    def test_iso_cis_unf_sla_bv_25_c(self):
+        """
+            Verify that the IUT can send an SDU with length â‰¤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0x7530
+        sdu_interval_s_to_m = 0x7530
+        ft_m_to_s = 3
+        ft_s_to_m = 2
+        iso_interval = 0x18
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 0
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=5,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=3,
+                bn_s_to_m=1,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
+
+    @metadata(
+        pts_test_id="IAL/CIS/FRA/SLA/BV-03-C",
+        pts_test_name="connected isochronous stream, framed data, peripheral role")
+    def test_iso_cis_fra_sla_bv_03_c(self):
+        """
+            Verify that the IUT can send an SDU with length â‰¤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0x0000
+        sdu_interval_s_to_m = 0x4e30
+        ft_m_to_s = 0
+        ft_s_to_m = 2
+        iso_interval = 0x14
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 1
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=4,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=0,
+                bn_s_to_m=2,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
+
+    @metadata(
+        pts_test_id="IAL/CIS/FRA/SLA/BV-26-C",
+        pts_test_name="connected isochronous stream, framed data, peripheral role")
+    def test_iso_cis_fra_sla_bv_26_c(self):
+        """
+            Verify that the IUT can send an SDU with length â‰¤ the Isochronous PDU length.
+        """
+        cig_id = 0x01
+        sdu_interval_m_to_s = 0x14D5
+        sdu_interval_s_to_m = 0x14D5
+        ft_m_to_s = 1
+        ft_s_to_m = 1
+        iso_interval = 0x08
+        peripherals_clock_accuracy = 0
+        packing = 0
+        framing = 1
+        max_transport_latency_m_to_s = 0
+        max_transport_latency_s_to_m = 0
+        cis_configs = [
+            CisTestParameters(
+                cis_id=0x01,
+                nse=2,
+                max_sdu_m_to_s=100,
+                max_sdu_s_to_m=100,
+                max_pdu_m_to_s=100,
+                max_pdu_s_to_m=100,
+                phy_m_to_s=0x02,
+                phy_s_to_m=0x00,
+                bn_m_to_s=1,
+                bn_s_to_m=1,
+            )
+        ]
+
+        self._setup_link_from_cert()
+        (dut_cis_stream, cert_cis_stream) = self._setup_cis_from_cert(
+            cig_id, sdu_interval_m_to_s, sdu_interval_s_to_m, ft_m_to_s, ft_s_to_m, iso_interval,
+            peripherals_clock_accuracy, packing, framing, max_transport_latency_m_to_s, max_transport_latency_s_to_m,
+            cis_configs)
+        dut_cis_stream.send(b'abcdefgh' * 10)
+        assertThat(cert_cis_stream).emits(IsoMatchers.Data(b'abcdefgh' * 10))
diff --git a/gd/iso/facade.cc b/gd/iso/facade.cc
new file mode 100644 (file)
index 0000000..9b5c88e
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2021 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 "iso/facade.h"
+
+#include "common/contextual_callback.h"
+#include "grpc/grpc_event_queue.h"
+#include "hci/acl_manager.h"
+#include "hci/address_with_type.h"
+#include "hci/le_address_manager.h"
+#include "iso/facade.grpc.pb.h"
+#include "iso/iso_module.h"
+#include "os/handler.h"
+
+using bluetooth::hci::AclManager;
+
+namespace bluetooth {
+namespace iso {
+
+class IsoModuleFacadeService : public IsoModuleFacade::Service {
+ public:
+  IsoModuleFacadeService(IsoModule* iso_module, AclManager* acl_manager, ::bluetooth::os::Handler* iso_handler)
+      : iso_module_(iso_module), acl_manager_(acl_manager), iso_handler_(iso_handler) {
+    ASSERT(iso_module_);
+    ASSERT(iso_handler_);
+
+    iso_module_->GetIsoManager()->RegisterIsoEstablishedCallback(iso_handler_->Bind(
+        [](::bluetooth::grpc::GrpcEventQueue<LeIsoEventsMsg>* le_iso_events_, uint16_t cis_connection_handle) {
+          LeIsoEventsMsg msg;
+          msg.set_message_type(IsoMsgType::ISO_CIS_ESTABLISHED);
+          msg.add_cis_handle(cis_connection_handle);
+          le_iso_events_->OnIncomingEvent(msg);
+        },
+        &le_iso_events_));
+
+    iso_module_->GetIsoManager()->RegisterIsoDataCallback(
+        iso_handler_->BindOn(this, &IsoModuleFacadeService::OnIsoPacketReceived));
+  }
+
+  ::grpc::Status LeSetCigParameters(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::iso::LeSetCigParametersRequest* request,
+      ::google::protobuf::Empty* response) override {
+    std::vector<hci::CisParametersConfig> cis_config;
+
+    hci::CisParametersConfig cfg;
+    cfg.cis_id_ = request->cis_id();
+    cfg.max_sdu_m_to_s_ = request->max_sdu_m_to_s();
+    cfg.max_sdu_s_to_m_ = request->max_sdu_s_to_m();
+    cfg.phy_m_to_s_ = request->phy_m_to_s();
+    cfg.phy_s_to_m_ = request->phy_s_to_m();
+    cfg.rtn_m_to_s_ = request->rtn_m_to_s();
+    cfg.rtn_s_to_m_ = request->rtn_s_to_m();
+
+    cis_config.push_back(cfg);
+
+    iso_module_->GetIsoManager()->SetCigParameters(
+        request->cig_id(),
+        request->sdu_interval_m_to_s(),
+        request->sdu_interval_s_to_m(),
+        static_cast<hci::ClockAccuracy>(request->peripherals_clock_accuracy()),
+        static_cast<hci::Packing>(request->packing()),
+        static_cast<hci::Enable>(request->framing()),
+        request->max_transport_latency_m_to_s(),
+        request->max_transport_latency_s_to_m(),
+        cis_config,
+        iso_handler_->BindOnce(
+            [](::bluetooth::grpc::GrpcEventQueue<LeIsoEventsMsg>* le_iso_events_, std::vector<uint16_t> conn_handles) {
+              LeIsoEventsMsg msg;
+
+              msg.set_message_type(IsoMsgType::ISO_PARAMETERS_SET_COMPLETE);
+              for (const uint16_t conn_handle : conn_handles) {
+                msg.add_cis_handle(conn_handle);
+              }
+              le_iso_events_->OnIncomingEvent(msg);
+            },
+            &le_iso_events_));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status LeSetCigParametersTest(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::iso::LeSetCigParametersTestRequest* request,
+      ::google::protobuf::Empty* response) override {
+    std::vector<hci::LeCisParametersTestConfig> cis_config;
+
+    for (const auto& cc : request->cis_configs()) {
+      hci::LeCisParametersTestConfig cfg;
+      cfg.cis_id_ = cc.cis_id();
+      cfg.nse_ = cc.nse();
+      cfg.max_sdu_m_to_s_ = cc.max_sdu_m_to_s();
+      cfg.max_sdu_s_to_m_ = cc.max_sdu_s_to_m();
+      cfg.max_pdu_m_to_s_ = cc.max_pdu_m_to_s();
+      cfg.max_pdu_s_to_m_ = cc.max_pdu_s_to_m();
+      cfg.phy_m_to_s_ = cc.phy_m_to_s();
+      cfg.phy_s_to_m_ = cc.phy_s_to_m();
+      cfg.bn_m_to_s_ = cc.bn_m_to_s();
+      cfg.bn_s_to_m_ = cc.bn_s_to_m();
+      cis_config.push_back(cfg);
+    }
+    iso_module_->GetIsoManager()->SetCigParametersTest(
+        request->cig_id(),
+        request->sdu_interval_m_to_s(),
+        request->sdu_interval_s_to_m(),
+        request->ft_m_to_s(),
+        request->ft_s_to_m(),
+        request->iso_interval(),
+        static_cast<hci::ClockAccuracy>(request->peripherals_clock_accuracy()),
+        static_cast<hci::Packing>(request->packing()),
+        static_cast<hci::Enable>(request->framing()),
+        request->max_transport_latency_m_to_s(),
+        request->max_transport_latency_s_to_m(),
+        cis_config,
+        iso_handler_->BindOnce(
+            [](::bluetooth::grpc::GrpcEventQueue<LeIsoEventsMsg>* le_iso_events_, std::vector<uint16_t> conn_handles) {
+              LeIsoEventsMsg msg;
+
+              msg.set_message_type(IsoMsgType::ISO_PARAMETERS_SET_COMPLETE);
+              for (const uint16_t conn_handle : conn_handles) {
+                msg.add_cis_handle(conn_handle);
+              }
+              le_iso_events_->OnIncomingEvent(msg);
+            },
+            &le_iso_events_));
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status LeCreateCis(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::iso::LeCreateCisRequest* request,
+      ::google::protobuf::Empty* response) override {
+    std::vector<std::pair<uint16_t, uint16_t>> create_cis_params;
+    for (const auto& handle_pair : request->handle_pair()) {
+      create_cis_params.push_back(
+          std::make_pair<uint16_t, uint16_t>(handle_pair.cis_handle(), handle_pair.acl_handle()));
+    }
+    iso_module_->GetIsoManager()->LeCreateCis(create_cis_params);
+
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status FetchIsoData(
+      ::grpc::ServerContext* context, const LeCisHandleMsg* request, ::grpc::ServerWriter<IsoPacket>* writer) override {
+    return le_iso_data_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status FetchIsoEvents(
+      ::grpc::ServerContext* context,
+      const google::protobuf::Empty* request,
+      ::grpc::ServerWriter<LeIsoEventsMsg>* writer) override {
+    return le_iso_events_.RunLoop(context, writer);
+  }
+
+  ::grpc::Status SendIsoPacket(
+      ::grpc::ServerContext* context,
+      const ::bluetooth::iso::IsoPacket* request,
+      ::google::protobuf::Empty* response) override {
+    std::vector<uint8_t> packet(request->payload().begin(), request->payload().end());
+    iso_module_->GetIsoManager()->SendIsoPacket(request->handle(), packet);
+    return ::grpc::Status::OK;
+  }
+
+  void OnIsoPacketReceived(std::unique_ptr<hci::IsoView> iso_view) {
+    ASSERT(iso_view->IsValid());
+
+    IsoPacket packet;
+    packet.set_handle(iso_view->GetConnectionHandle());
+
+    if (iso_view->GetTsFlag() == hci::TimeStampFlag::NOT_PRESENT) {
+      hci::IsoWithoutTimestampView nts = hci::IsoWithoutTimestampView::Create(*iso_view);
+      ASSERT(nts.IsValid());
+
+      auto data_vec = nts.GetPayload();
+      std::string data = std::string(data_vec.begin(), data_vec.end());
+      packet.set_payload(data);
+      le_iso_data_.OnIncomingEvent(packet);
+    } else {
+      hci::IsoWithTimestampView tsv = hci::IsoWithTimestampView::Create(*iso_view);
+      ASSERT(tsv.IsValid());
+
+      auto data_vec = tsv.GetPayload();
+      std::string data = std::string(data_vec.begin(), data_vec.end());
+      packet.set_payload(data);
+      le_iso_data_.OnIncomingEvent(packet);
+    }
+  }
+
+ private:
+  IsoModule* iso_module_;
+  ::bluetooth::grpc::GrpcEventQueue<LeIsoEventsMsg> le_iso_events_{"LE ISO events"};
+  ::bluetooth::grpc::GrpcEventQueue<IsoPacket> le_iso_data_{"LE ISO data"};
+  AclManager* acl_manager_ __attribute__((unused));
+  ::bluetooth::os::Handler* iso_handler_;
+};
+
+void IsoModuleFacadeModule::ListDependencies(ModuleList* list) {
+  ::bluetooth::grpc::GrpcFacadeModule::ListDependencies(list);
+  list->add<IsoModule>();
+  list->add<AclManager>();
+}
+
+void IsoModuleFacadeModule::Start() {
+  ::bluetooth::grpc::GrpcFacadeModule::Start();
+  service_ = new IsoModuleFacadeService(GetDependency<IsoModule>(), GetDependency<AclManager>(), GetHandler());
+}
+
+void IsoModuleFacadeModule::Stop() {
+  delete service_;
+  ::bluetooth::grpc::GrpcFacadeModule::Stop();
+}
+
+::grpc::Service* IsoModuleFacadeModule::GetService() const {
+  return service_;
+}
+
+const ModuleFactory IsoModuleFacadeModule::Factory =
+    ::bluetooth::ModuleFactory([]() { return new IsoModuleFacadeModule(); });
+
+}  // namespace iso
+}  // namespace bluetooth
diff --git a/gd/iso/facade.h b/gd/iso/facade.h
new file mode 100644 (file)
index 0000000..266bb09
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019 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 <grpc++/grpc++.h>
+
+#include "grpc/grpc_module.h"
+
+namespace bluetooth {
+namespace iso {
+
+class IsoModuleFacadeService;
+
+class IsoModuleFacadeModule : public ::bluetooth::grpc::GrpcFacadeModule {
+ public:
+  static const ModuleFactory Factory;
+
+  void ListDependencies(ModuleList* list) override;
+  void Start() override;
+  void Stop() override;
+  ::grpc::Service* GetService() const override;
+
+ private:
+  IsoModuleFacadeService* service_;
+};
+
+}  // namespace iso
+}  // namespace bluetooth
diff --git a/gd/iso/facade.proto b/gd/iso/facade.proto
new file mode 100644 (file)
index 0000000..28f9d27
--- /dev/null
@@ -0,0 +1,90 @@
+syntax = "proto3";
+
+package bluetooth.iso;
+
+import "google/protobuf/empty.proto";
+import "facade/common.proto";
+
+service IsoModuleFacade {
+  rpc LeSetCigParameters(LeSetCigParametersRequest) returns (google.protobuf.Empty) {}
+  rpc LeSetCigParametersTest(LeSetCigParametersTestRequest) returns (google.protobuf.Empty) {}
+  rpc LeCreateCis(LeCreateCisRequest) returns (google.protobuf.Empty) {}
+  rpc FetchIsoData(LeCisHandleMsg) returns (stream IsoPacket) {}
+  rpc FetchIsoEvents(google.protobuf.Empty) returns (stream LeIsoEventsMsg) {}
+  rpc SendIsoPacket(IsoPacket) returns (google.protobuf.Empty) {}
+}
+
+message IsoPacket {
+  uint32 handle = 1;
+  bytes payload = 3;
+}
+
+message LeSetCigParametersRequest {
+  uint32 cig_id = 1;
+  uint32 sdu_interval_m_to_s = 2;
+  uint32 sdu_interval_s_to_m = 3;
+  uint32 peripherals_clock_accuracy = 4;
+  uint32 packing = 5;
+  uint32 framing = 6;
+  uint32 max_transport_latency_m_to_s = 7;
+  uint32 max_transport_latency_s_to_m = 8;
+  uint32 cis_id = 9;
+  uint32 max_sdu_m_to_s = 10;
+  uint32 max_sdu_s_to_m = 11;
+  uint32 phy_m_to_s = 12;
+  uint32 phy_s_to_m = 13;
+  uint32 rtn_m_to_s = 14;
+  uint32 rtn_s_to_m = 15;
+}
+
+message LeSetCigParametersTestRequest {
+  uint32 cig_id = 1;
+  uint32 sdu_interval_m_to_s = 2;
+  uint32 sdu_interval_s_to_m = 3;
+  uint32 ft_m_to_s = 4;
+  uint32 ft_s_to_m = 5;
+  uint32 iso_interval = 6;
+  uint32 peripherals_clock_accuracy = 7;
+  uint32 packing = 8;
+  uint32 framing = 9;
+  uint32 max_transport_latency_m_to_s = 10;
+  uint32 max_transport_latency_s_to_m = 11;
+
+  message LeCisParametersTestConfig {
+    uint32 cis_id = 1;
+    uint32 nse = 2;
+    uint32 max_sdu_m_to_s = 3;
+    uint32 max_sdu_s_to_m = 4;
+    uint32 max_pdu_m_to_s = 5;
+    uint32 max_pdu_s_to_m = 6;
+    uint32 phy_m_to_s = 7;
+    uint32 phy_s_to_m = 8;
+    uint32 bn_m_to_s = 9;
+    uint32 bn_s_to_m = 10;
+  }
+
+  repeated LeCisParametersTestConfig cis_configs = 12;
+}
+
+message LeCreateCisRequest {
+  message HandlePair {
+    uint32 cis_handle = 1;
+    uint32 acl_handle = 2;
+  }
+
+  repeated HandlePair handle_pair = 1;
+}
+
+enum IsoMsgType {
+  ISO_PARAMETERS_SET_COMPLETE = 0;
+  ISO_CIS_ESTABLISHED = 1;
+}
+
+message LeIsoEventsMsg {
+  IsoMsgType message_type = 1;
+  repeated uint32 cis_handle = 2;
+}
+
+message LeCisHandleMsg {
+  uint32 handle = 1;
+}
index 8f17a14..1ea3858 100644 (file)
 #include "iso_manager_impl.h"
 
 #include "common/bind.h"
+#include "hci/hci_packets.h"
 #include "iso/iso_manager.h"
+#include "os/handler.h"
 #include "os/log.h"
+#include "packet/raw_builder.h"
 
 namespace bluetooth {
 namespace iso {
 namespace internal {
 
+using bluetooth::hci::IsoBuilder;
+
 IsoManagerImpl::IsoManagerImpl(os::Handler* iso_handler, hci::HciLayer* hci_layer, hci::Controller* controller)
     : iso_handler_(iso_handler),
+      hci_layer_(hci_layer),
       hci_le_iso_interface_(hci_layer->GetLeIsoInterface(iso_handler_->BindOn(this, &IsoManagerImpl::OnHciLeEvent))),
-      controller_(controller) {}
+      controller_(controller) {
+  hci_layer_->GetIsoQueueEnd()->RegisterDequeue(
+      iso_handler_, common::Bind(&IsoManagerImpl::OnIncomingPacket, common::Unretained(this)));
+  iso_enqueue_buffer_ = std::make_unique<os::EnqueueBuffer<IsoBuilder>>(hci_layer_->GetIsoQueueEnd());
+}
+
+IsoManagerImpl::~IsoManagerImpl() {
+  hci_layer_->GetIsoQueueEnd()->UnregisterDequeue();
+  iso_enqueue_buffer_ = nullptr;
+}
 
 void IsoManagerImpl::OnHciLeEvent(hci::LeMetaEventView event) {
   hci::SubeventCode code = event.GetSubeventCode();
 
+  if (code == hci::SubeventCode::CIS_ESTABLISHED) {
+    hci::LeCisEstablishedView le_cis_established_view = hci::LeCisEstablishedView::Create(event);
+    if (!le_cis_established_view.IsValid()) {
+      LOG_ERROR("Invalid LeCisEstablishedView packet received");
+      return;
+    }
+
+    cis_established_callback.Invoke(le_cis_established_view.GetConnectionHandle());
+    return;
+  } else if (code == hci::SubeventCode::CIS_REQUEST) {
+    hci::LeCisRequestView le_cis_request_view = hci::LeCisRequestView::Create(event);
+    if (!le_cis_request_view.IsValid()) {
+      LOG_ERROR("Invalid LeCisRequestView packet received");
+      return;
+    }
+
+    hci_le_iso_interface_->EnqueueCommand(
+        hci::LeAcceptCisRequestBuilder::Create(le_cis_request_view.GetCisConnectionHandle()),
+        iso_handler_->BindOnce([](hci::CommandStatusView command_status) {
+          LOG_INFO("command_status=%hhu ", command_status.GetStatus());
+        }));
+
+    return;
+  }
+
   LOG_ERROR("Unhandled HCI LE ISO event, code %s", hci::SubeventCodeText(code).c_str());
+  ASSERT_LOG(false, "Unhandled HCI LE ISO event");
 }
 
 void IsoManagerImpl::SetCigParameters(
@@ -45,7 +86,8 @@ void IsoManagerImpl::SetCigParameters(
     hci::Enable framing,
     uint16_t max_transport_latency_m_to_s,
     uint16_t max_transport_latency_s_to_m,
-    const std::vector<hci::CisParametersConfig>& cis_config) {
+    const std::vector<hci::CisParametersConfig>& cis_configs,
+    SetCigParametersCallback command_complete_callback) {
   hci_le_iso_interface_->EnqueueCommand(
       hci::LeSetCigParametersBuilder::Create(
           cig_id,
@@ -56,22 +98,149 @@ void IsoManagerImpl::SetCigParameters(
           framing,
           max_transport_latency_m_to_s,
           max_transport_latency_s_to_m,
-          cis_config),
-      iso_handler_->BindOnce(&IsoManagerImpl::SetCigParametersComplete, base::Unretained(this)));
+          cis_configs),
+      iso_handler_->BindOnce(
+          &IsoManagerImpl::SetCigParametersComplete,
+          common::Unretained(this),
+          cig_id,
+          cis_configs,
+          std::move(command_complete_callback)));
 }
 
-void IsoManagerImpl::SetCigParametersComplete(hci::CommandCompleteView command_complete) {
+void IsoManagerImpl::SetCigParametersComplete(
+    uint8_t cig_id,
+    const std::vector<hci::CisParametersConfig>& cis_configs,
+    SetCigParametersCallback command_complete_callback,
+    hci::CommandCompleteView command_complete) {
   ASSERT(command_complete.IsValid());
 
   hci::LeSetCigParametersCompleteView setCigParamsComplete =
       hci::LeSetCigParametersCompleteView::Create(command_complete);
   ASSERT(setCigParamsComplete.IsValid());
+
+  if (setCigParamsComplete.GetStatus() == hci::ErrorCode::SUCCESS) {
+    uint8_t cig_id_back_from_ctrl = setCigParamsComplete.GetCigId();
+    auto conn_handles = setCigParamsComplete.GetConnectionHandle();
+
+    ASSERT(cig_id_back_from_ctrl == cig_id);
+    ASSERT(conn_handles.size() == cis_configs.size());
+
+    auto cis_it = cis_configs.begin();
+    auto handle_it = conn_handles.begin();
+
+    std::vector<uint16_t> handles;
+    while (cis_it != cis_configs.end()) {
+      iso_connections_.push_back({
+          .cig_id = cig_id,
+          .cis_id = cis_it->cis_id_,
+          .connection_handle = *handle_it,
+      });
+
+      handles.push_back(*handle_it);
+
+      cis_it++;
+      handle_it++;
+    }
+
+    command_complete_callback.Invoke(handles);
+  }
+}
+
+void IsoManagerImpl::SetCigParametersTest(
+    uint8_t cig_id,
+    uint32_t sdu_interval_m_to_s,
+    uint32_t sdu_interval_s_to_m,
+    uint8_t ft_m_to_s,
+    uint8_t ft_s_to_m,
+    uint16_t iso_interval,
+    hci::ClockAccuracy peripherals_clock_accuracy,
+    hci::Packing packing,
+    hci::Enable framing,
+    uint16_t max_transport_latency_m_to_s,
+    uint16_t max_transport_latency_s_to_m,
+    const std::vector<hci::LeCisParametersTestConfig>& cis_test_configs,
+    SetCigParametersCallback command_complete_callback) {
+  hci_le_iso_interface_->EnqueueCommand(
+      hci::LeSetCigParametersTestBuilder::Create(
+          cig_id,
+          sdu_interval_m_to_s,
+          sdu_interval_s_to_m,
+          ft_m_to_s,
+          ft_s_to_m,
+          iso_interval,
+          peripherals_clock_accuracy,
+          packing,
+          framing,
+          cis_test_configs),
+      iso_handler_->BindOnce(
+          &IsoManagerImpl::SetCigParametersTestComplete,
+          common::Unretained(this),
+          cig_id,
+          cis_test_configs,
+          std::move(command_complete_callback)));
+}
+
+void IsoManagerImpl::SetCigParametersTestComplete(
+    uint8_t cig_id,
+    const std::vector<hci::LeCisParametersTestConfig>& cis_configs,
+    SetCigParametersCallback command_complete_callback,
+    hci::CommandCompleteView command_complete) {
+  ASSERT(command_complete.IsValid());
+
+  hci::LeSetCigParametersTestCompleteView setCigParamsComplete =
+      hci::LeSetCigParametersTestCompleteView::Create(command_complete);
+  ASSERT(setCigParamsComplete.IsValid());
+
+  if (setCigParamsComplete.GetStatus() == hci::ErrorCode::SUCCESS) {
+    uint8_t cig_id_back_from_ctrl = setCigParamsComplete.GetCigId();
+    auto conn_handles = setCigParamsComplete.GetConnectionHandle();
+
+    ASSERT(cig_id_back_from_ctrl == cig_id);
+    ASSERT(conn_handles.size() == cis_configs.size());
+
+    auto cis_it = cis_configs.begin();
+    auto handle_it = conn_handles.begin();
+
+    std::vector<uint16_t> handles;
+    while (cis_it != cis_configs.end()) {
+      iso_connections_.push_back({
+          .cig_id = cig_id,
+          .cis_id = cis_it->cis_id_,
+          .connection_handle = *handle_it,
+      });
+
+      handles.push_back(*handle_it);
+
+      cis_it++;
+      handle_it++;
+    }
+
+    command_complete_callback.Invoke(handles);
+  }
+}
+
+void IsoManagerImpl::LeCreateCis(std::vector<std::pair<uint16_t, uint16_t>> cis_and_acl_handles) {
+  std::vector<hci::CreateCisConfig> cis_configs;
+
+  for (const auto& handle_pair : cis_and_acl_handles) {
+    hci::CreateCisConfig config;
+    config.cis_connection_handle_ = handle_pair.first;
+    config.acl_connection_handle_ = handle_pair.second;
+    cis_configs.push_back(config);
+  }
+
+  hci_le_iso_interface_->EnqueueCommand(
+      hci::LeCreateCisBuilder::Create(cis_configs), iso_handler_->BindOnce([](hci::CommandStatusView command_status) {
+        LOG_INFO("command_status=%hhu ", command_status.GetStatus());
+      }));
 }
 
 void IsoManagerImpl::RemoveCig(uint8_t cig_id) {
+  ASSERT(IsKnownCig(cig_id));
+
   hci_le_iso_interface_->EnqueueCommand(
       hci::LeRemoveCigBuilder::Create(cig_id),
-      iso_handler_->BindOnce(&IsoManagerImpl::RemoveCigComplete, base::Unretained(this)));
+      iso_handler_->BindOnce(&IsoManagerImpl::RemoveCigComplete, common::Unretained(this)));
 }
 
 void IsoManagerImpl::RemoveCigComplete(hci::CommandCompleteView command_complete) {
@@ -81,6 +250,22 @@ void IsoManagerImpl::RemoveCigComplete(hci::CommandCompleteView command_complete
   ASSERT(removeCigComplete.IsValid());
 }
 
+void IsoManagerImpl::SendIsoPacket(uint16_t cis_handle, std::vector<uint8_t> packet) {
+  auto builder = hci::IsoWithoutTimestampBuilder::Create(
+      cis_handle,
+      hci::IsoPacketBoundaryFlag::COMPLETE_SDU,
+      0 /* sequence_number*/,
+      hci::IsoPacketStatusFlag::VALID,
+      std::make_unique<bluetooth::packet::RawBuilder>(packet));
+  LOG_INFO("%c%c", packet[0], packet[1]);
+  iso_enqueue_buffer_->Enqueue(std::move(builder), iso_handler_);
+}
+
+void IsoManagerImpl::OnIncomingPacket() {
+  std::unique_ptr<hci::IsoView> packet = hci_layer_->GetIsoQueueEnd()->TryDequeue();
+  iso_data_callback.Invoke(std::move(packet));
+}
+
 }  // namespace internal
 }  // namespace iso
 }  // namespace bluetooth
index 946c2ed..b297025 100644 (file)
 #include "hci/hci_packets.h"
 #include "os/handler.h"
 
+#include <list>
+
 namespace bluetooth {
 namespace iso {
+using SetCigParametersCallback = common::ContextualOnceCallback<void(std::vector<uint16_t>)>;
+using CisEstablishedCallback = common::ContextualCallback<void(uint16_t)>;
+using IsoDataCallback = common::ContextualCallback<void(std::unique_ptr<hci::IsoView>)>;
 
 namespace internal {
 
+struct IsochronousConnection {
+  uint16_t connection_handle;
+  uint8_t cig_id;
+  uint8_t cis_id;
+};
+
 class IsoManagerImpl {
  public:
   explicit IsoManagerImpl(os::Handler* iso_handler, hci::HciLayer* hci_layer, hci::Controller* controller);
+  ~IsoManagerImpl();
+
+  void RegisterIsoEstablishedCallback(CisEstablishedCallback cb) {
+    this->cis_established_callback = cb;
+  }
+
+  void RegisterIsoDataCallback(IsoDataCallback cb) {
+    this->iso_data_callback = cb;
+  }
 
   void OnHciLeEvent(hci::LeMetaEventView event);
 
@@ -41,15 +61,57 @@ class IsoManagerImpl {
       hci::Enable framing,
       uint16_t max_transport_latency_m_to_s,
       uint16_t max_transport_latency_s_to_m,
-      const std::vector<hci::CisParametersConfig>& cis_config);
-  void SetCigParametersComplete(hci::CommandCompleteView command_complete);
+      const std::vector<hci::CisParametersConfig>& cis_config,
+      SetCigParametersCallback command_complete_callback);
+  void SetCigParametersComplete(
+      uint8_t cig_id,
+      const std::vector<hci::CisParametersConfig>& cis_configs,
+      SetCigParametersCallback command_complete_callback,
+      hci::CommandCompleteView command_complete);
+
+  void SetCigParametersTest(
+      uint8_t cig_id,
+      uint32_t sdu_interval_m_to_s,
+      uint32_t sdu_interval_s_to_m,
+      uint8_t ft_m_to_s,
+      uint8_t ft_s_to_m,
+      uint16_t iso_interval,
+      hci::ClockAccuracy peripherals_clock_accuracy,
+      hci::Packing packing,
+      hci::Enable framing,
+      uint16_t max_transport_latency_m_to_s,
+      uint16_t max_transport_latency_s_to_m,
+      const std::vector<hci::LeCisParametersTestConfig>& cis_config,
+      SetCigParametersCallback command_complete_callback);
+  void SetCigParametersTestComplete(
+      uint8_t cig_id,
+      const std::vector<hci::LeCisParametersTestConfig>& cis_configs,
+      SetCigParametersCallback command_complete_callback,
+      hci::CommandCompleteView command_complete);
+
+  void LeCreateCis(std::vector<std::pair<uint16_t, uint16_t>> cis_and_acl_handles);
+
   void RemoveCig(uint8_t cig_id);
   void RemoveCigComplete(hci::CommandCompleteView command_complete);
 
+  void SendIsoPacket(uint16_t cis_handle, std::vector<uint8_t> packet);
+  void OnIncomingPacket();
+
+  bool IsKnownCig(uint8_t cig_id) {
+    return find_if(iso_connections_.begin(), iso_connections_.end(), [cig_id](const IsochronousConnection& c) {
+             return c.cig_id == cig_id;
+           }) != iso_connections_.end();
+  }
+
  private:
-  os::Handler* iso_handler_ __attribute__((unused));
+  os::Handler* iso_handler_;
+  hci::HciLayer* hci_layer_;
   hci::LeIsoInterface* hci_le_iso_interface_;
+  std::unique_ptr<os::EnqueueBuffer<bluetooth::hci::IsoBuilder>> iso_enqueue_buffer_;
   hci::Controller* controller_ __attribute__((unused));
+  std::list<IsochronousConnection> iso_connections_;
+  CisEstablishedCallback cis_established_callback;
+  IsoDataCallback iso_data_callback;
 };
 }  // namespace internal
 }  // namespace iso
index 3d198c6..c072bae 100644 (file)
 namespace bluetooth {
 namespace iso {
 
+void IsoManager::RegisterIsoEstablishedCallback(CisEstablishedCallback cb) {
+  iso_handler_->CallOn(iso_manager_impl_, &internal::IsoManagerImpl::RegisterIsoEstablishedCallback, cb);
+}
+
+void IsoManager::RegisterIsoDataCallback(IsoDataCallback cb) {
+  iso_handler_->CallOn(iso_manager_impl_, &internal::IsoManagerImpl::RegisterIsoDataCallback, cb);
+}
+
 void IsoManager::SetCigParameters(
     uint8_t cig_id,
     uint32_t sdu_interval_m_to_s,
@@ -31,7 +39,8 @@ void IsoManager::SetCigParameters(
     hci::Enable framing,
     uint16_t max_transport_latency_m_to_s,
     uint16_t max_transport_latency_s_to_m,
-    const std::vector<hci::CisParametersConfig>& cis_config) {
+    std::vector<hci::CisParametersConfig> cis_config,
+    SetCigParametersCallback command_complete_callback) {
   iso_handler_->CallOn(
       iso_manager_impl_,
       &internal::IsoManagerImpl::SetCigParameters,
@@ -43,12 +52,53 @@ void IsoManager::SetCigParameters(
       framing,
       max_transport_latency_m_to_s,
       max_transport_latency_s_to_m,
-      cis_config);
+      cis_config,
+      std::move(command_complete_callback));
+}
+
+void IsoManager::SetCigParametersTest(
+    uint8_t cig_id,
+    uint32_t sdu_interval_m_to_s,
+    uint32_t sdu_interval_s_to_m,
+    uint8_t ft_m_to_s,
+    uint8_t ft_s_to_m,
+    uint16_t iso_interval,
+    hci::ClockAccuracy peripherals_clock_accuracy,
+    hci::Packing packing,
+    hci::Enable framing,
+    uint16_t max_transport_latency_m_to_s,
+    uint16_t max_transport_latency_s_to_m,
+    std::vector<hci::LeCisParametersTestConfig> cis_config,
+    SetCigParametersCallback command_complete_callback) {
+  iso_handler_->CallOn(
+      iso_manager_impl_,
+      &internal::IsoManagerImpl::SetCigParametersTest,
+      cig_id,
+      sdu_interval_m_to_s,
+      sdu_interval_s_to_m,
+      ft_m_to_s,
+      ft_s_to_m,
+      iso_interval,
+      peripherals_clock_accuracy,
+      packing,
+      framing,
+      max_transport_latency_m_to_s,
+      max_transport_latency_s_to_m,
+      cis_config,
+      std::move(command_complete_callback));
+}
+
+void IsoManager::LeCreateCis(std::vector<std::pair<uint16_t, uint16_t>> cis_and_acl_handles) {
+  iso_handler_->CallOn(iso_manager_impl_, &internal::IsoManagerImpl::LeCreateCis, cis_and_acl_handles);
 }
 
 void IsoManager::RemoveCig(uint8_t cig_id) {
   iso_handler_->CallOn(iso_manager_impl_, &internal::IsoManagerImpl::RemoveCig, cig_id);
 }
 
+void IsoManager::SendIsoPacket(uint16_t cis_handle, std::vector<uint8_t> packet) {
+  iso_handler_->CallOn(iso_manager_impl_, &internal::IsoManagerImpl::SendIsoPacket, cis_handle, packet);
+}
+
 }  // namespace iso
 }  // namespace bluetooth
index a396108..d38405c 100644 (file)
 
 #include "hci/address_with_type.h"
 #include "iso/internal/iso_manager_impl.h"
+#include "os/handler.h"
 
 namespace bluetooth {
 namespace iso {
+using SetCigParametersCallback = common::ContextualOnceCallback<void(std::vector<uint16_t> /* connectino handles*/)>;
+using CisEstablishedCallback = common::ContextualCallback<void(uint16_t)>;
+using IsoDataCallback = common::ContextualCallback<void(std::unique_ptr<hci::IsoView>)>;
 
 /**
  * Manages the iso attributes, pairing, bonding of devices, and the
@@ -35,6 +39,9 @@ class IsoManager {
  public:
   friend class IsoModule;
 
+  void RegisterIsoEstablishedCallback(CisEstablishedCallback cb);
+  void RegisterIsoDataCallback(IsoDataCallback cb);
+
   void SetCigParameters(
       uint8_t cig_id,
       uint32_t sdu_interval_m_to_s,
@@ -44,9 +51,28 @@ class IsoManager {
       hci::Enable framing,
       uint16_t max_transport_latency_m_to_s,
       uint16_t max_transport_latency_s_to_m,
-      const std::vector<hci::CisParametersConfig>& cis_config);
+      std::vector<hci::CisParametersConfig> cis_config,
+      SetCigParametersCallback command_complete_callback);
+  void SetCigParametersTest(
+      uint8_t cig_id,
+      uint32_t sdu_interval_m_to_s,
+      uint32_t sdu_interval_s_to_m,
+      uint8_t ft_m_to_s,
+      uint8_t ft_s_to_m,
+      uint16_t iso_interval,
+      hci::ClockAccuracy peripherals_clock_accuracy,
+      hci::Packing packing,
+      hci::Enable framing,
+      uint16_t max_transport_latency_m_to_s,
+      uint16_t max_transport_latency_s_to_m,
+      std::vector<hci::LeCisParametersTestConfig> cis_config,
+      SetCigParametersCallback command_complete_callback);
+
+  void LeCreateCis(std::vector<std::pair<uint16_t, uint16_t>> cis_and_acl_handles);
   void RemoveCig(uint8_t cig_id);
 
+  void SendIsoPacket(uint16_t cis_handle, std::vector<uint8_t> packet);
+
  protected:
   IsoManager(os::Handler* iso_handler, internal::IsoManagerImpl* iso_manager_impl)
       : iso_handler_(iso_handler), iso_manager_impl_(iso_manager_impl) {}
index fe57196..3678841 100644 (file)
@@ -24,7 +24,6 @@
 #include "hci/acl_manager.h"
 #include "hci/hci_layer.h"
 #include "iso/iso_module.h"
-#include "l2cap/le/l2cap_le_module.h"
 
 namespace bluetooth {
 namespace iso {