Merge remote-tracking branch 'aosp/upstream-main' am: 0947a3b324 am: 9808477a01 am: 68db002c9b
Original change: https://android-review.googlesource.com/c/platform/external/python/bumble/+/2808233
Change-Id: I4ee1e0531e688e6dc18ce192717837b904234a4b
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.github/workflows/python-avatar.yml b/.github/workflows/python-avatar.yml
new file mode 100644
index 0000000..eb1b270
--- /dev/null
+++ b/.github/workflows/python-avatar.yml
@@ -0,0 +1,43 @@
+name: Python Avatar
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ name: Avatar [${{ matrix.shard }}]
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ shard: [
+ 1/24, 2/24, 3/24, 4/24,
+ 5/24, 6/24, 7/24, 8/24,
+ 9/24, 10/24, 11/24, 12/24,
+ 13/24, 14/24, 15/24, 16/24,
+ 17/24, 18/24, 19/24, 20/24,
+ 21/24, 22/24, 23/24, 24/24,
+ ]
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set Up Python 3.11
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.11
+ - name: Install
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install .[avatar]
+ - name: Rootcanal
+ run: nohup python -m rootcanal > rootcanal.log &
+ - name: Test
+ run: |
+ avatar --list | grep -Ev '^=' > test-names.txt
+ timeout 5m avatar --test-beds bumble.bumbles --tests $(split test-names.txt -n l/${{ matrix.shard }})
+ - name: Rootcanal Logs
+ run: cat rootcanal.log
diff --git a/apps/bench.py b/apps/bench.py
index 4708403..8b37883 100644
--- a/apps/bench.py
+++ b/apps/bench.py
@@ -24,6 +24,7 @@
import click
+from bumble import l2cap
from bumble.core import (
BT_BR_EDR_TRANSPORT,
BT_LE_TRANSPORT,
@@ -85,6 +86,7 @@
DEFAULT_RFCOMM_CHANNEL = 8
+
# -----------------------------------------------------------------------------
# Utils
# -----------------------------------------------------------------------------
@@ -197,6 +199,7 @@
PACKET_FLAG_LAST = 1
+
# -----------------------------------------------------------------------------
# Sender
# -----------------------------------------------------------------------------
@@ -659,17 +662,19 @@
self.mps = mps
self.ready = asyncio.Event()
- async def on_connection(self, connection):
+ async def on_connection(self, connection: Connection) -> None:
connection.on('disconnection', self.on_disconnection)
# Connect a new L2CAP channel
print(color(f'>>> Opening L2CAP channel on PSM = {self.psm}', 'yellow'))
try:
- l2cap_channel = await connection.open_l2cap_channel(
- psm=self.psm,
- max_credits=self.max_credits,
- mtu=self.mtu,
- mps=self.mps,
+ l2cap_channel = await connection.create_l2cap_channel(
+ spec=l2cap.LeCreditBasedChannelSpec(
+ psm=self.psm,
+ max_credits=self.max_credits,
+ mtu=self.mtu,
+ mps=self.mps,
+ )
)
print(color('*** L2CAP channel:', 'cyan'), l2cap_channel)
except Exception as error:
@@ -695,7 +700,7 @@
class L2capServer(StreamedPacketIO):
def __init__(
self,
- device,
+ device: Device,
psm=DEFAULT_L2CAP_PSM,
max_credits=DEFAULT_L2CAP_MAX_CREDITS,
mtu=DEFAULT_L2CAP_MTU,
@@ -706,12 +711,11 @@
self.ready = asyncio.Event()
# Listen for incoming L2CAP CoC connections
- device.register_l2cap_channel_server(
- psm=psm,
- server=self.on_l2cap_channel,
- max_credits=max_credits,
- mtu=mtu,
- mps=mps,
+ device.create_l2cap_server(
+ spec=l2cap.LeCreditBasedChannelSpec(
+ psm=psm, mtu=mtu, mps=mps, max_credits=max_credits
+ ),
+ handler=self.on_l2cap_channel,
)
print(color(f'### Listening for CoC connection on PSM {psm}', 'yellow'))
diff --git a/apps/gg_bridge.py b/apps/gg_bridge.py
index 88ebdc5..12d16e4 100644
--- a/apps/gg_bridge.py
+++ b/apps/gg_bridge.py
@@ -21,6 +21,7 @@
import logging
import click
+from bumble import l2cap
from bumble.colors import color
from bumble.device import Device, Peer
from bumble.core import AdvertisingData
@@ -204,7 +205,7 @@
# -----------------------------------------------------------------------------
class GattlinkNodeBridge(GattlinkL2capEndpoint, Device.Listener):
- def __init__(self, device):
+ def __init__(self, device: Device):
super().__init__()
self.device = device
self.peer = None
@@ -218,7 +219,12 @@
# Listen for incoming L2CAP CoC connections
psm = 0xFB
- device.register_l2cap_channel_server(0xFB, self.on_coc)
+ device.create_l2cap_server(
+ spec=l2cap.LeCreditBasedChannelSpec(
+ psm=0xFB,
+ ),
+ handler=self.on_coc,
+ )
print(f'### Listening for CoC connection on PSM {psm}')
# Setup the Gattlink service
diff --git a/apps/l2cap_bridge.py b/apps/l2cap_bridge.py
index 83379a0..14bd759 100644
--- a/apps/l2cap_bridge.py
+++ b/apps/l2cap_bridge.py
@@ -20,6 +20,7 @@
import os
import click
+from bumble import l2cap
from bumble.colors import color
from bumble.transport import open_transport_or_link
from bumble.device import Device
@@ -47,14 +48,13 @@
self.tcp_host = tcp_host
self.tcp_port = tcp_port
- async def start(self, device):
+ async def start(self, device: Device) -> None:
# Listen for incoming L2CAP CoC connections
- device.register_l2cap_channel_server(
- psm=self.psm,
- server=self.on_coc,
- max_credits=self.max_credits,
- mtu=self.mtu,
- mps=self.mps,
+ device.create_l2cap_server(
+ spec=l2cap.LeCreditBasedChannelSpec(
+ psm=self.psm, mtu=self.mtu, mps=self.mps, max_credits=self.max_credits
+ ),
+ handler=self.on_coc,
)
print(color(f'### Listening for CoC connection on PSM {self.psm}', 'yellow'))
@@ -195,11 +195,13 @@
# Connect a new L2CAP channel
print(color(f'>>> Opening L2CAP channel on PSM = {self.psm}', 'yellow'))
try:
- l2cap_channel = await connection.open_l2cap_channel(
- psm=self.psm,
- max_credits=self.max_credits,
- mtu=self.mtu,
- mps=self.mps,
+ l2cap_channel = await connection.create_l2cap_channel(
+ spec=l2cap.LeCreditBasedChannelSpec(
+ psm=self.psm,
+ max_credits=self.max_credits,
+ mtu=self.mtu,
+ mps=self.mps,
+ )
)
print(color('*** L2CAP channel:', 'cyan'), l2cap_channel)
except Exception as error:
diff --git a/apps/pair.py b/apps/pair.py
index 2cc8188..39ee4fe 100644
--- a/apps/pair.py
+++ b/apps/pair.py
@@ -306,6 +306,7 @@
# Expose a GATT characteristic that can be used to trigger pairing by
# responding with an authentication error when read
if mode == 'le':
+ device.le_enabled = True
device.add_service(
Service(
'50DB505C-8AC4-4738-8448-3B1D9CC09CC5',
@@ -326,7 +327,6 @@
# Select LE or Classic
if mode == 'classic':
device.classic_enabled = True
- device.le_enabled = False
device.classic_smp_enabled = ctkd
# Get things going
diff --git a/apps/speaker/speaker.py b/apps/speaker/speaker.py
index e451c04..84e05a0 100644
--- a/apps/speaker/speaker.py
+++ b/apps/speaker/speaker.py
@@ -641,7 +641,7 @@
self.device.on('connection', self.on_bluetooth_connection)
# Create a listener to wait for AVDTP connections
- self.listener = Listener(Listener.create_registrar(self.device))
+ self.listener = Listener.for_device(self.device)
self.listener.on('connection', self.on_avdtp_connection)
print(f'Speaker ready to play, codec={color(self.codec, "cyan")}')
diff --git a/bumble/avdtp.py b/bumble/avdtp.py
index 3988f30..9a332f4 100644
--- a/bumble/avdtp.py
+++ b/bumble/avdtp.py
@@ -20,8 +20,24 @@
import struct
import time
import logging
+import enum
+import warnings
from pyee import EventEmitter
-from typing import Dict, Type
+from typing import (
+ Any,
+ Awaitable,
+ Dict,
+ Type,
+ Tuple,
+ Optional,
+ Callable,
+ List,
+ AsyncGenerator,
+ Iterable,
+ Union,
+ SupportsBytes,
+ cast,
+)
from .core import (
BT_ADVANCED_AUDIO_DISTRIBUTION_SERVICE,
@@ -38,7 +54,7 @@
SbcMediaCodecInformation,
VendorSpecificMediaCodecInformation,
)
-from . import sdp
+from . import sdp, device, l2cap
from .colors import color
# -----------------------------------------------------------------------------
@@ -206,7 +222,9 @@
# -----------------------------------------------------------------------------
-async def find_avdtp_service_with_sdp_client(sdp_client):
+async def find_avdtp_service_with_sdp_client(
+ sdp_client: sdp.Client,
+) -> Optional[Tuple[int, int]]:
'''
Find an AVDTP service, using a connected SDP client, and return its version,
or None if none is found
@@ -227,10 +245,13 @@
avdtp_version_major = profile_descriptor.value[1].value >> 8
avdtp_version_minor = profile_descriptor.value[1].value & 0xFF
return (avdtp_version_major, avdtp_version_minor)
+ return None
# -----------------------------------------------------------------------------
-async def find_avdtp_service_with_connection(device, connection):
+async def find_avdtp_service_with_connection(
+ device: device.Device, connection: device.Connection
+) -> Optional[Tuple[int, int]]:
'''
Find an AVDTP service, for a connection, and return its version,
or None if none is found
@@ -246,17 +267,17 @@
# -----------------------------------------------------------------------------
class RealtimeClock:
- def now(self):
+ def now(self) -> float:
return time.time()
- async def sleep(self, duration):
+ async def sleep(self, duration: float) -> None:
await asyncio.sleep(duration)
# -----------------------------------------------------------------------------
class MediaPacket:
@staticmethod
- def from_bytes(data):
+ def from_bytes(data: bytes) -> MediaPacket:
version = (data[0] >> 6) & 0x03
padding = (data[0] >> 5) & 0x01
extension = (data[0] >> 4) & 0x01
@@ -286,17 +307,17 @@
def __init__(
self,
- version,
- padding,
- extension,
- marker,
- sequence_number,
- timestamp,
- ssrc,
- csrc_list,
- payload_type,
- payload,
- ):
+ version: int,
+ padding: int,
+ extension: int,
+ marker: int,
+ sequence_number: int,
+ timestamp: int,
+ ssrc: int,
+ csrc_list: List[int],
+ payload_type: int,
+ payload: bytes,
+ ) -> None:
self.version = version
self.padding = padding
self.extension = extension
@@ -308,7 +329,7 @@
self.payload_type = payload_type
self.payload = payload
- def __bytes__(self):
+ def __bytes__(self) -> bytes:
header = bytes(
[
self.version << 6
@@ -322,7 +343,7 @@
header += struct.pack('>I', csrc)
return header + self.payload
- def __str__(self):
+ def __str__(self) -> str:
return (
f'RTP(v={self.version},'
f'p={self.padding},'
@@ -339,12 +360,16 @@
# -----------------------------------------------------------------------------
class MediaPacketPump:
- def __init__(self, packets, clock=RealtimeClock()):
+ pump_task: Optional[asyncio.Task]
+
+ def __init__(
+ self, packets: AsyncGenerator, clock: RealtimeClock = RealtimeClock()
+ ) -> None:
self.packets = packets
self.clock = clock
self.pump_task = None
- async def start(self, rtp_channel):
+ async def start(self, rtp_channel: l2cap.ClassicChannel) -> None:
async def pump_packets():
start_time = 0
start_timestamp = 0
@@ -376,7 +401,7 @@
# Pump packets
self.pump_task = asyncio.create_task(pump_packets())
- async def stop(self):
+ async def stop(self) -> None:
# Stop the pump
if self.pump_task:
self.pump_task.cancel()
@@ -385,32 +410,37 @@
# -----------------------------------------------------------------------------
-class MessageAssembler: # pylint: disable=attribute-defined-outside-init
- def __init__(self, callback):
+class MessageAssembler:
+ message: Optional[bytes]
+
+ def __init__(self, callback: Callable[[int, Message], Any]) -> None:
self.callback = callback
self.reset()
- def reset(self):
+ def reset(self) -> None:
self.transaction_label = 0
self.message = None
- self.message_type = 0
+ self.message_type = Message.MessageType.COMMAND
self.signal_identifier = 0
self.number_of_signal_packets = 0
self.packet_count = 0
- def on_pdu(self, pdu):
+ def on_pdu(self, pdu: bytes) -> None:
self.packet_count += 1
transaction_label = pdu[0] >> 4
- packet_type = (pdu[0] >> 2) & 3
- message_type = pdu[0] & 3
+ packet_type = Protocol.PacketType((pdu[0] >> 2) & 3)
+ message_type = Message.MessageType(pdu[0] & 3)
logger.debug(
f'transaction_label={transaction_label}, '
- f'packet_type={Protocol.packet_type_name(packet_type)}, '
- f'message_type={Message.message_type_name(message_type)}'
+ f'packet_type={packet_type.name}, '
+ f'message_type={message_type.name}'
)
- if packet_type in (Protocol.SINGLE_PACKET, Protocol.START_PACKET):
+ if packet_type in (
+ Protocol.PacketType.SINGLE_PACKET,
+ Protocol.PacketType.START_PACKET,
+ ):
if self.message is not None:
# The previous message has not been terminated
logger.warning(
@@ -423,13 +453,16 @@
self.signal_identifier = pdu[1] & 0x3F
self.message_type = message_type
- if packet_type == Protocol.SINGLE_PACKET:
+ if packet_type == Protocol.PacketType.SINGLE_PACKET:
self.message = pdu[2:]
self.on_message_complete()
else:
self.number_of_signal_packets = pdu[2]
self.message = pdu[3:]
- elif packet_type in (Protocol.CONTINUE_PACKET, Protocol.END_PACKET):
+ elif packet_type in (
+ Protocol.PacketType.CONTINUE_PACKET,
+ Protocol.PacketType.END_PACKET,
+ ):
if self.packet_count == 0:
logger.warning('unexpected continuation')
return
@@ -448,9 +481,9 @@
)
return
- self.message += pdu[1:]
+ self.message = (self.message or b'') + pdu[1:]
- if packet_type == Protocol.END_PACKET:
+ if packet_type == Protocol.PacketType.END_PACKET:
if self.packet_count != self.number_of_signal_packets:
logger.warning(
'incomplete fragmented message: '
@@ -471,24 +504,25 @@
self.reset()
return
- def on_message_complete(self):
+ def on_message_complete(self) -> None:
message = Message.create(
- self.signal_identifier, self.message_type, self.message
+ self.signal_identifier, self.message_type, self.message or b''
)
-
try:
self.callback(self.transaction_label, message)
except Exception as error:
logger.warning(color(f'!!! exception in callback: {error}'))
-
self.reset()
# -----------------------------------------------------------------------------
class ServiceCapabilities:
@staticmethod
- def create(service_category, service_capabilities_bytes):
+ def create(
+ service_category: int, service_capabilities_bytes: bytes
+ ) -> ServiceCapabilities:
# Select the appropriate subclass
+ cls: Type[ServiceCapabilities]
if service_category == AVDTP_MEDIA_CODEC_SERVICE_CATEGORY:
cls = MediaCodecCapabilities
else:
@@ -503,7 +537,7 @@
return instance
@staticmethod
- def parse_capabilities(payload):
+ def parse_capabilities(payload: bytes) -> List[ServiceCapabilities]:
capabilities = []
while payload:
service_category = payload[0]
@@ -518,7 +552,7 @@
return capabilities
@staticmethod
- def serialize_capabilities(capabilities):
+ def serialize_capabilities(capabilities: Iterable[ServiceCapabilities]) -> bytes:
serialized = b''
for item in capabilities:
serialized += (
@@ -527,21 +561,23 @@
)
return serialized
- def init_from_bytes(self):
+ def init_from_bytes(self) -> None:
pass
- def __init__(self, service_category, service_capabilities_bytes=b''):
+ def __init__(
+ self, service_category: int, service_capabilities_bytes: bytes = b''
+ ) -> None:
self.service_category = service_category
self.service_capabilities_bytes = service_capabilities_bytes
- def to_string(self, details=[]): # pylint: disable=dangerous-default-value
+ def to_string(self, details: List[str] = []) -> str:
attributes = ','.join(
[name_or_number(AVDTP_SERVICE_CATEGORY_NAMES, self.service_category)]
+ details
)
return f'ServiceCapabilities({attributes})'
- def __str__(self):
+ def __str__(self) -> str:
if self.service_capabilities_bytes:
details = [self.service_capabilities_bytes.hex()]
else:
@@ -551,7 +587,11 @@
# -----------------------------------------------------------------------------
class MediaCodecCapabilities(ServiceCapabilities):
- def init_from_bytes(self):
+ media_codec_information: Union[bytes, SupportsBytes]
+ media_type: int
+ media_codec_type: int
+
+ def init_from_bytes(self) -> None:
self.media_type = self.service_capabilities_bytes[0]
self.media_codec_type = self.service_capabilities_bytes[1]
self.media_codec_information = self.service_capabilities_bytes[2:]
@@ -571,7 +611,12 @@
)
)
- def __init__(self, media_type, media_codec_type, media_codec_information):
+ def __init__(
+ self,
+ media_type: int,
+ media_codec_type: int,
+ media_codec_information: Union[bytes, SupportsBytes],
+ ) -> None:
super().__init__(
AVDTP_MEDIA_CODEC_SERVICE_CATEGORY,
bytes([media_type, media_codec_type]) + bytes(media_codec_information),
@@ -580,7 +625,7 @@
self.media_codec_type = media_codec_type
self.media_codec_information = media_codec_information
- def __str__(self):
+ def __str__(self) -> str:
codec_info = (
self.media_codec_information.hex()
if isinstance(self.media_codec_information, bytes)
@@ -598,17 +643,17 @@
# -----------------------------------------------------------------------------
class EndPointInfo:
@staticmethod
- def from_bytes(payload):
+ def from_bytes(payload: bytes) -> EndPointInfo:
return EndPointInfo(
payload[0] >> 2, payload[0] >> 1 & 1, payload[1] >> 4, payload[1] >> 3 & 1
)
- def __bytes__(self):
+ def __bytes__(self) -> bytes:
return bytes(
[self.seid << 2 | self.in_use << 1, self.media_type << 4 | self.tsep << 3]
)
- def __init__(self, seid, in_use, media_type, tsep):
+ def __init__(self, seid: int, in_use: int, media_type: int, tsep: int) -> None:
self.seid = seid
self.in_use = in_use
self.media_type = media_type
@@ -617,24 +662,16 @@
# -----------------------------------------------------------------------------
class Message: # pylint:disable=attribute-defined-outside-init
- COMMAND = 0
- GENERAL_REJECT = 1
- RESPONSE_ACCEPT = 2
- RESPONSE_REJECT = 3
-
- MESSAGE_TYPE_NAMES = {
- COMMAND: 'COMMAND',
- GENERAL_REJECT: 'GENERAL_REJECT',
- RESPONSE_ACCEPT: 'RESPONSE_ACCEPT',
- RESPONSE_REJECT: 'RESPONSE_REJECT',
- }
+ class MessageType(enum.IntEnum):
+ COMMAND = 0
+ GENERAL_REJECT = 1
+ RESPONSE_ACCEPT = 2
+ RESPONSE_REJECT = 3
# Subclasses, by signal identifier and message type
subclasses: Dict[int, Dict[int, Type[Message]]] = {}
-
- @staticmethod
- def message_type_name(message_type):
- return name_or_number(Message.MESSAGE_TYPE_NAMES, message_type)
+ message_type: MessageType
+ signal_identifier: int
@staticmethod
def subclass(subclass):
@@ -643,23 +680,23 @@
if name == 'General_Reject':
subclass.signal_identifier = 0
signal_identifier_str = None
- message_type = Message.COMMAND
+ message_type = Message.MessageType.COMMAND
elif name.endswith('_Command'):
signal_identifier_str = name[:-8]
- message_type = Message.COMMAND
+ message_type = Message.MessageType.COMMAND
elif name.endswith('_Response'):
signal_identifier_str = name[:-9]
- message_type = Message.RESPONSE_ACCEPT
+ message_type = Message.MessageType.RESPONSE_ACCEPT
elif name.endswith('_Reject'):
signal_identifier_str = name[:-7]
- message_type = Message.RESPONSE_REJECT
+ message_type = Message.MessageType.RESPONSE_REJECT
else:
raise ValueError('invalid class name')
subclass.message_type = message_type
if signal_identifier_str is not None:
- for (name, signal_identifier) in AVDTP_SIGNAL_IDENTIFIERS.items():
+ for name, signal_identifier in AVDTP_SIGNAL_IDENTIFIERS.items():
if name.lower().endswith(signal_identifier_str.lower()):
subclass.signal_identifier = signal_identifier
break
@@ -674,7 +711,9 @@
# Factory method to create a subclass based on the signal identifier and message
# type
@staticmethod
- def create(signal_identifier, message_type, payload):
+ def create(
+ signal_identifier: int, message_type: MessageType, payload: bytes
+ ) -> Message:
# Look for a registered subclass
subclasses = Message.subclasses.get(signal_identifier)
if subclasses:
@@ -686,7 +725,7 @@
return instance
# Instantiate the appropriate class based on the message type
- if message_type == Message.RESPONSE_REJECT:
+ if message_type == Message.MessageType.RESPONSE_REJECT:
# Assume a simple reject message
instance = Simple_Reject(payload)
instance.init_from_payload()
@@ -696,16 +735,16 @@
instance.message_type = message_type
return instance
- def init_from_payload(self):
+ def init_from_payload(self) -> None:
pass
- def __init__(self, payload=b''):
+ def __init__(self, payload: bytes = b'') -> None:
self.payload = payload
- def to_string(self, details):
+ def to_string(self, details: Union[str, Iterable[str]]) -> str:
base = color(
f'{name_or_number(AVDTP_SIGNAL_NAMES, self.signal_identifier)}_'
- f'{Message.message_type_name(self.message_type)}',
+ f'{self.message_type.name}',
'yellow',
)
@@ -721,7 +760,7 @@
return base
- def __str__(self):
+ def __str__(self) -> str:
return self.to_string(self.payload.hex())
@@ -738,7 +777,7 @@
super().__init__(payload=bytes([seid << 2]))
self.acp_seid = seid
- def __str__(self):
+ def __str__(self) -> str:
return self.to_string([f'ACP SEID: {self.acp_seid}'])
@@ -755,7 +794,7 @@
super().__init__(payload=bytes([error_code]))
self.error_code = error_code
- def __str__(self):
+ def __str__(self) -> str:
details = [f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}']
return self.to_string(details)
@@ -775,6 +814,8 @@
See Bluetooth AVDTP spec - 8.6.2 Stream End Point Discovery Response
'''
+ endpoints: List[EndPointInfo]
+
def init_from_payload(self):
self.endpoints = []
endpoint_count = len(self.payload) // 2
@@ -787,7 +828,7 @@
super().__init__(payload=b''.join([bytes(endpoint) for endpoint in endpoints]))
self.endpoints = endpoints
- def __str__(self):
+ def __str__(self) -> str:
details = []
for endpoint in self.endpoints:
details.extend(
@@ -826,7 +867,7 @@
)
self.capabilities = capabilities
- def __str__(self):
+ def __str__(self) -> str:
details = [str(capability) for capability in self.capabilities]
return self.to_string(details)
@@ -875,7 +916,9 @@
self.int_seid = self.payload[1] >> 2
self.capabilities = ServiceCapabilities.parse_capabilities(self.payload[2:])
- def __init__(self, acp_seid, int_seid, capabilities):
+ def __init__(
+ self, acp_seid: int, int_seid: int, capabilities: Iterable[ServiceCapabilities]
+ ) -> None:
super().__init__(
payload=bytes([acp_seid << 2, int_seid << 2])
+ ServiceCapabilities.serialize_capabilities(capabilities)
@@ -884,7 +927,7 @@
self.int_seid = int_seid
self.capabilities = capabilities
- def __str__(self):
+ def __str__(self) -> str:
details = [f'ACP SEID: {self.acp_seid}', f'INT SEID: {self.int_seid}'] + [
str(capability) for capability in self.capabilities
]
@@ -915,7 +958,7 @@
self.service_category = service_category
self.error_code = error_code
- def __str__(self):
+ def __str__(self) -> str:
details = [
(
'service_category: '
@@ -947,13 +990,13 @@
def init_from_payload(self):
self.capabilities = ServiceCapabilities.parse_capabilities(self.payload)
- def __init__(self, capabilities):
+ def __init__(self, capabilities: Iterable[ServiceCapabilities]) -> None:
super().__init__(
payload=ServiceCapabilities.serialize_capabilities(capabilities)
)
self.capabilities = capabilities
- def __str__(self):
+ def __str__(self) -> str:
details = [str(capability) for capability in self.capabilities]
return self.to_string(details)
@@ -978,7 +1021,7 @@
self.acp_seid = self.payload[0] >> 2
self.capabilities = ServiceCapabilities.parse_capabilities(self.payload[1:])
- def __str__(self):
+ def __str__(self) -> str:
details = [
f'ACP SEID: {self.acp_seid}',
] + [str(capability) for capability in self.capabilities]
@@ -1035,11 +1078,11 @@
def init_from_payload(self):
self.acp_seids = [x >> 2 for x in self.payload]
- def __init__(self, seids):
+ def __init__(self, seids: Iterable[int]) -> None:
super().__init__(payload=bytes([seid << 2 for seid in seids]))
self.acp_seids = seids
- def __str__(self):
+ def __str__(self) -> str:
return self.to_string([f'ACP SEIDs: {self.acp_seids}'])
@@ -1067,7 +1110,7 @@
self.acp_seid = acp_seid
self.error_code = error_code
- def __str__(self):
+ def __str__(self) -> str:
details = [
f'acp_seid: {self.acp_seid}',
f'error_code: {name_or_number(AVDTP_ERROR_NAMES, self.error_code)}',
@@ -1186,7 +1229,7 @@
self.acp_seid = self.payload[0] >> 2
self.delay = (self.payload[1] << 8) | (self.payload[2])
- def __str__(self):
+ def __str__(self) -> str:
return self.to_string([f'ACP_SEID: {self.acp_seid}', f'delay: {self.delay}'])
@@ -1208,32 +1251,36 @@
# -----------------------------------------------------------------------------
class Protocol(EventEmitter):
- SINGLE_PACKET = 0
- START_PACKET = 1
- CONTINUE_PACKET = 2
- END_PACKET = 3
+ local_endpoints: List[LocalStreamEndPoint]
+ remote_endpoints: Dict[int, DiscoveredStreamEndPoint]
+ streams: Dict[int, Stream]
+ transaction_results: List[Optional[asyncio.Future[Message]]]
+ channel_connector: Callable[[], Awaitable[l2cap.ClassicChannel]]
- PACKET_TYPE_NAMES = {
- SINGLE_PACKET: 'SINGLE_PACKET',
- START_PACKET: 'START_PACKET',
- CONTINUE_PACKET: 'CONTINUE_PACKET',
- END_PACKET: 'END_PACKET',
- }
+ class PacketType(enum.IntEnum):
+ SINGLE_PACKET = 0
+ START_PACKET = 1
+ CONTINUE_PACKET = 2
+ END_PACKET = 3
@staticmethod
def packet_type_name(packet_type):
return name_or_number(Protocol.PACKET_TYPE_NAMES, packet_type)
@staticmethod
- async def connect(connection, version=(1, 3)):
- connector = connection.create_l2cap_connector(AVDTP_PSM)
- channel = await connector()
+ async def connect(
+ connection: device.Connection, version: Tuple[int, int] = (1, 3)
+ ) -> Protocol:
+ channel = await connection.create_l2cap_channel(
+ spec=l2cap.ClassicChannelSpec(psm=AVDTP_PSM)
+ )
protocol = Protocol(channel, version)
- protocol.channel_connector = connector
return protocol
- def __init__(self, l2cap_channel, version=(1, 3)):
+ def __init__(
+ self, l2cap_channel: l2cap.ClassicChannel, version: Tuple[int, int] = (1, 3)
+ ) -> None:
super().__init__()
self.l2cap_channel = l2cap_channel
self.version = version
@@ -1243,7 +1290,6 @@
self.transaction_semaphore = asyncio.Semaphore(16)
self.transaction_count = 0
self.channel_acceptor = None
- self.channel_connector = None
self.local_endpoints = [] # Local endpoints, with contiguous seid values
self.remote_endpoints = {} # Remote stream endpoints, by seid
self.streams = {} # Streams, by seid
@@ -1253,27 +1299,31 @@
l2cap_channel.on('open', self.on_l2cap_channel_open)
l2cap_channel.on('close', self.on_l2cap_channel_close)
- def get_local_endpoint_by_seid(self, seid):
+ def get_local_endpoint_by_seid(self, seid: int) -> Optional[LocalStreamEndPoint]:
if 0 < seid <= len(self.local_endpoints):
return self.local_endpoints[seid - 1]
return None
- def add_source(self, codec_capabilities, packet_pump):
+ def add_source(
+ self, codec_capabilities: MediaCodecCapabilities, packet_pump: MediaPacketPump
+ ) -> LocalSource:
seid = len(self.local_endpoints) + 1
source = LocalSource(self, seid, codec_capabilities, packet_pump)
self.local_endpoints.append(source)
return source
- def add_sink(self, codec_capabilities):
+ def add_sink(self, codec_capabilities: MediaCodecCapabilities) -> LocalSink:
seid = len(self.local_endpoints) + 1
sink = LocalSink(self, seid, codec_capabilities)
self.local_endpoints.append(sink)
return sink
- async def create_stream(self, source, sink):
+ async def create_stream(
+ self, source: LocalStreamEndPoint, sink: StreamEndPointProxy
+ ) -> Stream:
# Check that the source isn't already used in a stream
if source.in_use:
raise InvalidStateError('source already in use')
@@ -1290,10 +1340,10 @@
return stream
- async def discover_remote_endpoints(self):
+ async def discover_remote_endpoints(self) -> Iterable[DiscoveredStreamEndPoint]:
self.remote_endpoints = {}
- response = await self.send_command(Discover_Command())
+ response: Discover_Response = await self.send_command(Discover_Command())
for endpoint_entry in response.endpoints:
logger.debug(
f'getting endpoint capabilities for endpoint {endpoint_entry.seid}'
@@ -1311,7 +1361,9 @@
return self.remote_endpoints.values()
- def find_remote_sink_by_codec(self, media_type, codec_type):
+ def find_remote_sink_by_codec(
+ self, media_type: int, codec_type: int
+ ) -> Optional[DiscoveredStreamEndPoint]:
for endpoint in self.remote_endpoints.values():
if (
not endpoint.in_use
@@ -1330,9 +1382,10 @@
capabilities.service_category
== AVDTP_MEDIA_CODEC_SERVICE_CATEGORY
):
+ codec_capabilities = cast(MediaCodecCapabilities, capabilities)
if (
- capabilities.media_type == AVDTP_AUDIO_MEDIA_TYPE
- and capabilities.media_codec_type == codec_type
+ codec_capabilities.media_type == AVDTP_AUDIO_MEDIA_TYPE
+ and codec_capabilities.media_codec_type == codec_type
):
has_codec = True
if has_media_transport and has_codec:
@@ -1340,10 +1393,10 @@
return None
- def on_pdu(self, pdu):
+ def on_pdu(self, pdu: bytes) -> None:
self.message_assembler.on_pdu(pdu)
- def on_message(self, transaction_label, message):
+ def on_message(self, transaction_label: int, message: Message) -> None:
logger.debug(
f'{color("<<< Received AVDTP message", "magenta")}: '
f'[{transaction_label}] {message}'
@@ -1362,7 +1415,7 @@
logger.warning('!!! invalid signal identifier')
self.send_message(transaction_label, General_Reject())
- if message.message_type == Message.COMMAND:
+ if message.message_type == Message.MessageType.COMMAND:
# Command
signal_name = (
AVDTP_SIGNAL_NAMES.get(message.signal_identifier, "")
@@ -1407,7 +1460,7 @@
logger.debug(color('<<< L2CAP channel close', 'magenta'))
self.emit('close')
- def send_message(self, transaction_label, message):
+ def send_message(self, transaction_label: int, message: Message) -> None:
logger.debug(
f'{color(">>> Sending AVDTP message", "magenta")}: '
f'[{transaction_label}] {message}'
@@ -1418,9 +1471,9 @@
payload = message.payload
if len(payload) + 2 <= self.l2cap_channel.mtu:
# Fits in a single packet
- packet_type = self.SINGLE_PACKET
+ packet_type = self.PacketType.SINGLE_PACKET
else:
- packet_type = self.START_PACKET
+ packet_type = self.PacketType.START_PACKET
done = False
while not done:
@@ -1428,9 +1481,9 @@
transaction_label << 4 | packet_type << 2 | message.message_type
)
- if packet_type == self.SINGLE_PACKET:
+ if packet_type == self.PacketType.SINGLE_PACKET:
header = bytes([first_header_byte, message.signal_identifier])
- elif packet_type == self.START_PACKET:
+ elif packet_type == self.PacketType.START_PACKET:
packet_count = (
max_fragment_size - 1 + len(payload)
) // max_fragment_size
@@ -1447,14 +1500,14 @@
payload = payload[max_fragment_size:]
if payload:
packet_type = (
- self.CONTINUE_PACKET
- if payload > max_fragment_size
- else self.END_PACKET
+ self.PacketType.CONTINUE_PACKET
+ if len(payload) > max_fragment_size
+ else self.PacketType.END_PACKET
)
else:
done = True
- async def send_command(self, command):
+ async def send_command(self, command: Message):
# TODO: support timeouts
# Send the command
(transaction_label, transaction_result) = await self.start_transaction()
@@ -1464,12 +1517,16 @@
response = await transaction_result
# Check for errors
- if response.message_type in (Message.GENERAL_REJECT, Message.RESPONSE_REJECT):
+ if response.message_type in (
+ Message.MessageType.GENERAL_REJECT,
+ Message.MessageType.RESPONSE_REJECT,
+ ):
+ assert hasattr(response, 'error_code')
raise ProtocolError(response.error_code, 'avdtp')
return response
- async def start_transaction(self):
+ async def start_transaction(self) -> Tuple[int, asyncio.Future[Message]]:
# Wait until we can start a new transaction
await self.transaction_semaphore.acquire()
@@ -1484,34 +1541,38 @@
assert False # Should never reach this
- async def get_capabilities(self, seid):
+ async def get_capabilities(
+ self, seid: int
+ ) -> Union[Get_Capabilities_Response, Get_All_Capabilities_Response,]:
if self.version > (1, 2):
return await self.send_command(Get_All_Capabilities_Command(seid))
return await self.send_command(Get_Capabilities_Command(seid))
- async def set_configuration(self, acp_seid, int_seid, capabilities):
+ async def set_configuration(
+ self, acp_seid: int, int_seid: int, capabilities: Iterable[ServiceCapabilities]
+ ) -> Set_Configuration_Response:
return await self.send_command(
Set_Configuration_Command(acp_seid, int_seid, capabilities)
)
- async def get_configuration(self, seid):
+ async def get_configuration(self, seid: int) -> Get_Configuration_Response:
response = await self.send_command(Get_Configuration_Command(seid))
return response.capabilities
- async def open(self, seid):
+ async def open(self, seid: int) -> Open_Response:
return await self.send_command(Open_Command(seid))
- async def start(self, seids):
+ async def start(self, seids: Iterable[int]) -> Start_Response:
return await self.send_command(Start_Command(seids))
- async def suspend(self, seids):
+ async def suspend(self, seids: Iterable[int]) -> Suspend_Response:
return await self.send_command(Suspend_Command(seids))
- async def close(self, seid):
+ async def close(self, seid: int) -> Close_Response:
return await self.send_command(Close_Command(seid))
- async def abort(self, seid):
+ async def abort(self, seid: int) -> Abort_Response:
return await self.send_command(Abort_Command(seid))
def on_discover_command(self, _command):
@@ -1653,26 +1714,46 @@
# -----------------------------------------------------------------------------
class Listener(EventEmitter):
- @staticmethod
- def create_registrar(device):
- return device.create_l2cap_registrar(AVDTP_PSM)
+ servers: Dict[int, Protocol]
- def set_server(self, connection, server):
+ @staticmethod
+ def create_registrar(device: device.Device):
+ warnings.warn("Please use Listener.for_device()", DeprecationWarning)
+
+ def wrapper(handler: Callable[[l2cap.ClassicChannel], None]) -> None:
+ device.create_l2cap_server(l2cap.ClassicChannelSpec(psm=AVDTP_PSM), handler)
+
+ return wrapper
+
+ def set_server(self, connection: device.Connection, server: Protocol) -> None:
self.servers[connection.handle] = server
- def remove_server(self, connection):
+ def remove_server(self, connection: device.Connection) -> None:
if connection.handle in self.servers:
del self.servers[connection.handle]
- def __init__(self, registrar, version=(1, 3)):
+ def __init__(self, registrar=None, version=(1, 3)):
super().__init__()
self.version = version
self.servers = {} # Servers, by connection handle
# Listen for incoming L2CAP connections
- registrar(self.on_l2cap_connection)
+ if registrar:
+ warnings.warn("Please use Listener.for_device()", DeprecationWarning)
+ registrar(self.on_l2cap_connection)
- def on_l2cap_connection(self, channel):
+ @classmethod
+ def for_device(
+ cls, device: device.Device, version: Tuple[int, int] = (1, 3)
+ ) -> Listener:
+ listener = Listener(registrar=None, version=version)
+ l2cap_server = device.create_l2cap_server(
+ spec=l2cap.ClassicChannelSpec(psm=AVDTP_PSM)
+ )
+ l2cap_server.on('connection', listener.on_l2cap_connection)
+ return listener
+
+ def on_l2cap_connection(self, channel: l2cap.ClassicChannel) -> None:
logger.debug(f'{color("<<< incoming L2CAP connection:", "magenta")} {channel}')
if channel.connection.handle in self.servers:
@@ -1701,18 +1782,21 @@
Pair of a local and a remote stream endpoint that can stream from one to the other
'''
+ rtp_channel: Optional[l2cap.ClassicChannel]
+
@staticmethod
- def state_name(state):
+ def state_name(state: int) -> str:
return name_or_number(AVDTP_STATE_NAMES, state)
- def change_state(self, state):
+ def change_state(self, state: int) -> None:
logger.debug(f'{self} state change -> {color(self.state_name(state), "cyan")}')
self.state = state
- def send_media_packet(self, packet):
+ def send_media_packet(self, packet: MediaPacket) -> None:
+ assert self.rtp_channel
self.rtp_channel.send_pdu(bytes(packet))
- async def configure(self):
+ async def configure(self) -> None:
if self.state != AVDTP_IDLE_STATE:
raise InvalidStateError('current state is not IDLE')
@@ -1721,7 +1805,7 @@
)
self.change_state(AVDTP_CONFIGURED_STATE)
- async def open(self):
+ async def open(self) -> None:
if self.state != AVDTP_CONFIGURED_STATE:
raise InvalidStateError('current state is not CONFIGURED')
@@ -1731,9 +1815,13 @@
self.change_state(AVDTP_OPEN_STATE)
# Create a channel for RTP packets
- self.rtp_channel = await self.protocol.channel_connector()
+ self.rtp_channel = (
+ await self.protocol.l2cap_channel.connection.create_l2cap_channel(
+ l2cap.ClassicChannelSpec(psm=AVDTP_PSM)
+ )
+ )
- async def start(self):
+ async def start(self) -> None:
# Auto-open if needed
if self.state == AVDTP_CONFIGURED_STATE:
await self.open()
@@ -1749,7 +1837,7 @@
self.change_state(AVDTP_STREAMING_STATE)
- async def stop(self):
+ async def stop(self) -> None:
if self.state != AVDTP_STREAMING_STATE:
raise InvalidStateError('current state is not STREAMING')
@@ -1761,7 +1849,7 @@
self.change_state(AVDTP_OPEN_STATE)
- async def close(self):
+ async def close(self) -> None:
if self.state not in (AVDTP_OPEN_STATE, AVDTP_STREAMING_STATE):
raise InvalidStateError('current state is not OPEN or STREAMING')
@@ -1905,7 +1993,12 @@
else:
logger.warning('unexpected channel close while not CLOSING or ABORTING')
- def __init__(self, protocol, local_endpoint, remote_endpoint):
+ def __init__(
+ self,
+ protocol: Protocol,
+ local_endpoint: LocalStreamEndPoint,
+ remote_endpoint: StreamEndPointProxy,
+ ) -> None:
'''
remote_endpoint must be a subclass of StreamEndPointProxy
@@ -1919,7 +2012,7 @@
local_endpoint.stream = self
local_endpoint.in_use = 1
- def __str__(self):
+ def __str__(self) -> str:
return (
f'Stream({self.local_endpoint.seid} -> '
f'{self.remote_endpoint.seid} {self.state_name(self.state)})'
@@ -1928,14 +2021,21 @@
# -----------------------------------------------------------------------------
class StreamEndPoint:
- def __init__(self, seid, media_type, tsep, in_use, capabilities):
+ def __init__(
+ self,
+ seid: int,
+ media_type: int,
+ tsep: int,
+ in_use: int,
+ capabilities: Iterable[ServiceCapabilities],
+ ) -> None:
self.seid = seid
self.media_type = media_type
self.tsep = tsep
self.in_use = in_use
self.capabilities = capabilities
- def __str__(self):
+ def __str__(self) -> str:
media_type = f'{name_or_number(AVDTP_MEDIA_TYPE_NAMES, self.media_type)}'
tsep = f'{name_or_number(AVDTP_TSEP_NAMES, self.tsep)}'
return '\n'.join(
@@ -1955,40 +2055,58 @@
# -----------------------------------------------------------------------------
class StreamEndPointProxy:
- def __init__(self, protocol, seid):
+ def __init__(self, protocol: Protocol, seid: int) -> None:
self.seid = seid
self.protocol = protocol
- async def set_configuration(self, int_seid, configuration):
+ async def set_configuration(
+ self, int_seid: int, configuration: Iterable[ServiceCapabilities]
+ ) -> Set_Configuration_Response:
return await self.protocol.set_configuration(self.seid, int_seid, configuration)
- async def open(self):
+ async def open(self) -> Open_Response:
return await self.protocol.open(self.seid)
- async def start(self):
+ async def start(self) -> Start_Response:
return await self.protocol.start([self.seid])
- async def stop(self):
+ async def stop(self) -> Suspend_Response:
return await self.protocol.suspend([self.seid])
- async def close(self):
+ async def close(self) -> Close_Response:
return await self.protocol.close(self.seid)
- async def abort(self):
+ async def abort(self) -> Abort_Response:
return await self.protocol.abort(self.seid)
# -----------------------------------------------------------------------------
class DiscoveredStreamEndPoint(StreamEndPoint, StreamEndPointProxy):
- def __init__(self, protocol, seid, media_type, tsep, in_use, capabilities):
+ def __init__(
+ self,
+ protocol: Protocol,
+ seid: int,
+ media_type: int,
+ tsep: int,
+ in_use: int,
+ capabilities: Iterable[ServiceCapabilities],
+ ) -> None:
StreamEndPoint.__init__(self, seid, media_type, tsep, in_use, capabilities)
StreamEndPointProxy.__init__(self, protocol, seid)
# -----------------------------------------------------------------------------
class LocalStreamEndPoint(StreamEndPoint, EventEmitter):
+ stream: Optional[Stream]
+
def __init__(
- self, protocol, seid, media_type, tsep, capabilities, configuration=None
+ self,
+ protocol: Protocol,
+ seid: int,
+ media_type: int,
+ tsep: int,
+ capabilities: Iterable[ServiceCapabilities],
+ configuration: Optional[Iterable[ServiceCapabilities]] = None,
):
StreamEndPoint.__init__(self, seid, media_type, tsep, 0, capabilities)
EventEmitter.__init__(self)
@@ -2043,7 +2161,13 @@
# -----------------------------------------------------------------------------
class LocalSource(LocalStreamEndPoint):
- def __init__(self, protocol, seid, codec_capabilities, packet_pump):
+ def __init__(
+ self,
+ protocol: Protocol,
+ seid: int,
+ codec_capabilities: MediaCodecCapabilities,
+ packet_pump: MediaPacketPump,
+ ) -> None:
capabilities = [
ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
codec_capabilities,
@@ -2058,13 +2182,13 @@
)
self.packet_pump = packet_pump
- async def start(self):
- if self.packet_pump:
+ async def start(self) -> None:
+ if self.packet_pump and self.stream and self.stream.rtp_channel:
return await self.packet_pump.start(self.stream.rtp_channel)
self.emit('start')
- async def stop(self):
+ async def stop(self) -> None:
if self.packet_pump:
return await self.packet_pump.stop()
@@ -2079,7 +2203,9 @@
# -----------------------------------------------------------------------------
class LocalSink(LocalStreamEndPoint):
- def __init__(self, protocol, seid, codec_capabilities):
+ def __init__(
+ self, protocol: Protocol, seid: int, codec_capabilities: MediaCodecCapabilities
+ ) -> None:
capabilities = [
ServiceCapabilities(AVDTP_MEDIA_TRANSPORT_SERVICE_CATEGORY),
codec_capabilities,
diff --git a/bumble/company_ids.py b/bumble/company_ids.py
index 1c40951..af99b01 100644
--- a/bumble/company_ids.py
+++ b/bumble/company_ids.py
@@ -14,12 +14,12 @@
# -----------------------------------------------------------------------------
# The contents of this file are generated by copy/pasting the output of
-# the `generate_company_id_list.py` script
+# the `tools/generate_company_id_list.py` script
# -----------------------------------------------------------------------------
# pylint: disable=line-too-long
COMPANY_IDENTIFIERS = {
- 0x0000: "Ericsson Technology Licensing",
+ 0x0000: "Ericsson AB",
0x0001: "Nokia Mobile Phones",
0x0002: "Intel Corp.",
0x0003: "IBM Corp.",
@@ -40,7 +40,7 @@
0x0012: "Zeevo, Inc.",
0x0013: "Atmel Corporation",
0x0014: "Mitsubishi Electric Corporation",
- 0x0015: "RTX Telecom A/S",
+ 0x0015: "RTX A/S",
0x0016: "KC Technology Inc.",
0x0017: "Newlogic",
0x0018: "Transilica, Inc.",
@@ -56,7 +56,7 @@
0x0022: "NEC Corporation",
0x0023: "WavePlus Technology Co., Ltd.",
0x0024: "Alcatel",
- 0x0025: "NXP Semiconductors (formerly Philips Semiconductors)",
+ 0x0025: "NXP B.V.",
0x0026: "C Technologies",
0x0027: "Open Interface",
0x0028: "R F Micro Devices",
@@ -77,9 +77,9 @@
0x0037: "Mobilian Corporation",
0x0038: "Syntronix Corporation",
0x0039: "Integrated System Solution Corp.",
- 0x003A: "Panasonic Corporation (formerly Matsushita Electric Industrial Co., Ltd.)",
+ 0x003A: "Panasonic Holdings Corporation",
0x003B: "Gennum Corporation",
- 0x003C: "BlackBerry Limited (formerly Research In Motion)",
+ 0x003C: "BlackBerry Limited",
0x003D: "IPextreme, Inc.",
0x003E: "Systems and Chips, Inc",
0x003F: "Bluetooth SIG, Inc",
@@ -120,9 +120,9 @@
0x0062: "Gibson Guitars",
0x0063: "MiCommand Inc.",
0x0064: "Band XI International, LLC",
- 0x0065: "Hewlett-Packard Company",
+ 0x0065: "HP, Inc.",
0x0066: "9Solutions Oy",
- 0x0067: "GN Netcom A/S",
+ 0x0067: "GN Audio A/S",
0x0068: "General Motors",
0x0069: "A&D Engineering, Inc.",
0x006A: "MindTree Ltd.",
@@ -138,7 +138,7 @@
0x0074: "Zomm, LLC",
0x0075: "Samsung Electronics Co. Ltd.",
0x0076: "Creative Technology Ltd.",
- 0x0077: "Laird Technologies",
+ 0x0077: "Laird Connectivity LLC",
0x0078: "Nike, Inc.",
0x0079: "lesswire AG",
0x007A: "MStar Semiconductor, Inc.",
@@ -149,20 +149,20 @@
0x007F: "Autonet Mobile",
0x0080: "DeLorme Publishing Company, Inc.",
0x0081: "WuXi Vimicro",
- 0x0082: "Sennheiser Communications A/S",
+ 0x0082: "DSEA A/S",
0x0083: "TimeKeeping Systems, Inc.",
0x0084: "Ludus Helsinki Ltd.",
0x0085: "BlueRadios, Inc.",
0x0086: "Equinux AG",
0x0087: "Garmin International, Inc.",
0x0088: "Ecotest",
- 0x0089: "GN ReSound A/S",
+ 0x0089: "GN Hearing A/S",
0x008A: "Jawbone",
0x008B: "Topcon Positioning Systems, LLC",
- 0x008C: "Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)",
+ 0x008C: "Gimbal Inc.",
0x008D: "Zscan Software",
0x008E: "Quintic Corp",
- 0x008F: "Telit Wireless Solutions GmbH (formerly Stollmann E+V GmbH)",
+ 0x008F: "Telit Wireless Solutions GmbH",
0x0090: "Funai Electric Co., Ltd.",
0x0091: "Advanced PANMOBIL systems GmbH & Co. KG",
0x0092: "ThinkOptics, Inc.",
@@ -188,7 +188,7 @@
0x00A6: "Panda Ocean Inc.",
0x00A7: "Visteon Corporation",
0x00A8: "ARP Devices Limited",
- 0x00A9: "MARELLI EUROPE S.P.A. (formerly Magneti Marelli S.p.A.)",
+ 0x00A9: "MARELLI EUROPE S.P.A.",
0x00AA: "CAEN RFID srl",
0x00AB: "Ingenieur-Systemgruppe Zahn GmbH",
0x00AC: "Green Throttle Games",
@@ -205,7 +205,7 @@
0x00B7: "TreLab Ltd",
0x00B8: "Qualcomm Innovation Center, Inc. (QuIC)",
0x00B9: "Johnson Controls, Inc.",
- 0x00BA: "Starkey Laboratories Inc.",
+ 0x00BA: "Starkey Hearing Technologies",
0x00BB: "S-Power Electronics Limited",
0x00BC: "Ace Sensor Inc",
0x00BD: "Aplix Corporation",
@@ -225,7 +225,7 @@
0x00CB: "Binauric SE",
0x00CC: "Beats Electronics",
0x00CD: "Microchip Technology Inc.",
- 0x00CE: "Elgato Systems GmbH",
+ 0x00CE: "Eve Systems GmbH",
0x00CF: "ARCHOS SA",
0x00D0: "Dexcom, Inc.",
0x00D1: "Polar Electro Europe B.V.",
@@ -238,7 +238,7 @@
0x00D8: "Qualcomm Connected Experiences, Inc.",
0x00D9: "Voyetra Turtle Beach",
0x00DA: "txtr GmbH",
- 0x00DB: "Biosentronics",
+ 0x00DB: "Snuza (Pty) Ltd",
0x00DC: "Procter & Gamble",
0x00DD: "Hosiden Corporation",
0x00DE: "Muzik LLC",
@@ -247,13 +247,13 @@
0x00E1: "Danlers Ltd",
0x00E2: "Semilink Inc",
0x00E3: "inMusic Brands, Inc",
- 0x00E4: "Laird Connectivity, Inc. formerly L.S. Research Inc.",
+ 0x00E4: "L.S. Research, Inc.",
0x00E5: "Eden Software Consultants Ltd.",
0x00E6: "Freshtemp",
0x00E7: "KS Technologies",
0x00E8: "ACTS Technologies",
0x00E9: "Vtrack Systems",
- 0x00EA: "Nielsen-Kellerman Company",
+ 0x00EA: "www.vtracksystems.com",
0x00EB: "Server Technology Inc.",
0x00EC: "BioResearch Associates",
0x00ED: "Jolly Logic, LLC",
@@ -269,7 +269,7 @@
0x00F7: "VSN Technologies, Inc.",
0x00F8: "AceUni Corp., Ltd.",
0x00F9: "StickNFind",
- 0x00FA: "Crystal Code AB",
+ 0x00FA: "Crystal Alarm AB",
0x00FB: "KOUKAAM a.s.",
0x00FC: "Delphi Corporation",
0x00FD: "ValenceTech Limited",
@@ -282,13 +282,13 @@
0x0104: "PLUS Location Systems Pty Ltd",
0x0105: "Ubiquitous Computing Technology Corporation",
0x0106: "Innovative Yachtter Solutions",
- 0x0107: "William Demant Holding A/S",
+ 0x0107: "Demant A/S",
0x0108: "Chicony Electronics Co., Ltd.",
0x0109: "Atus BV",
0x010A: "Codegate Ltd",
0x010B: "ERi, Inc",
0x010C: "Transducers Direct, LLC",
- 0x010D: "DENSO TEN LIMITED (formerly Fujitsu Ten LImited)",
+ 0x010D: "DENSO TEN Limited",
0x010E: "Audi AG",
0x010F: "HiSilicon Technologies CO., LIMITED",
0x0110: "Nippon Seiki Co., Ltd.",
@@ -371,24 +371,24 @@
0x015D: "Estimote, Inc.",
0x015E: "Unikey Technologies, Inc.",
0x015F: "Timer Cap Co.",
- 0x0160: "Awox formerly AwoX",
+ 0x0160: "AwoX",
0x0161: "yikes",
0x0162: "MADSGlobalNZ Ltd.",
0x0163: "PCH International",
0x0164: "Qingdao Yeelink Information Technology Co., Ltd.",
- 0x0165: "Milwaukee Tool (Formally Milwaukee Electric Tools)",
+ 0x0165: "Milwaukee Electric Tools",
0x0166: "MISHIK Pte Ltd",
0x0167: "Ascensia Diabetes Care US Inc.",
0x0168: "Spicebox LLC",
0x0169: "emberlight",
- 0x016A: "Cooper-Atkins Corporation",
+ 0x016A: "Emerson Digital Cold Chain, Inc.",
0x016B: "Qblinks",
0x016C: "MYSPHERA",
0x016D: "LifeScan Inc",
0x016E: "Volantic AB",
0x016F: "Podo Labs, Inc",
0x0170: "Roche Diabetes Care AG",
- 0x0171: "Amazon.com Services, LLC (formerly Amazon Fulfillment Service)",
+ 0x0171: "Amazon.com Services LLC",
0x0172: "Connovate Technology Private Limited",
0x0173: "Kocomojo, LLC",
0x0174: "Everykey Inc.",
@@ -396,10 +396,10 @@
0x0176: "SentriLock",
0x0177: "I-SYST inc.",
0x0178: "CASIO COMPUTER CO., LTD.",
- 0x0179: "LAPIS Technology Co., Ltd. formerly LAPIS Semiconductor Co., Ltd.",
+ 0x0179: "LAPIS Semiconductor Co.,Ltd",
0x017A: "Telemonitor, Inc.",
0x017B: "taskit GmbH",
- 0x017C: "Daimler AG",
+ 0x017C: "Mercedes-Benz Group AG",
0x017D: "BatAndCat",
0x017E: "BluDotz Ltd",
0x017F: "XTel Wireless ApS",
@@ -429,7 +429,7 @@
0x0197: "WiSilica Inc.",
0x0198: "VENGIT Korlatolt Felelossegu Tarsasag",
0x0199: "SALTO SYSTEMS S.L.",
- 0x019A: "TRON Forum (formerly T-Engine Forum)",
+ 0x019A: "TRON Forum",
0x019B: "CUBETECH s.r.o.",
0x019C: "Cokiya Incorporated",
0x019D: "CVS Health",
@@ -439,14 +439,14 @@
0x01A1: "FIAMM",
0x01A2: "GIGALANE.CO.,LTD",
0x01A3: "EROAD",
- 0x01A4: "Mine Safety Appliances",
+ 0x01A4: "MSA Innovation, LLC",
0x01A5: "Icon Health and Fitness",
- 0x01A6: "Wille Engineering (formely as Asandoo GmbH)",
+ 0x01A6: "Wille Engineering",
0x01A7: "ENERGOUS CORPORATION",
0x01A8: "Taobao",
0x01A9: "Canon Inc.",
0x01AA: "Geophysical Technology Inc.",
- 0x01AB: "Facebook, Inc.",
+ 0x01AB: "Meta Platforms, Inc.",
0x01AC: "Trividia Health, Inc.",
0x01AD: "FlightSafety International",
0x01AE: "Earlens Corporation",
@@ -466,7 +466,7 @@
0x01BC: "SenionLab AB",
0x01BD: "Syszone Co., Ltd",
0x01BE: "Pulsate Mobile Ltd.",
- 0x01BF: "Hong Kong HunterSun Electronic Limited",
+ 0x01BF: "Hongkong OnMicro Electronics Limited",
0x01C0: "pironex GmbH",
0x01C1: "BRADATECH Corp.",
0x01C2: "Transenergooil AG",
@@ -496,7 +496,7 @@
0x01DA: "Logitech International SA",
0x01DB: "Innblue Consulting",
0x01DC: "iParking Ltd.",
- 0x01DD: "Koninklijke Philips Electronics N.V.",
+ 0x01DD: "Koninklijke Philips N.V.",
0x01DE: "Minelab Electronics Pty Limited",
0x01DF: "Bison Group Ltd.",
0x01E0: "Widex A/S",
@@ -574,7 +574,7 @@
0x0228: "Twocanoes Labs, LLC",
0x0229: "Muoverti Limited",
0x022A: "Stamer Musikanlagen GMBH",
- 0x022B: "Tesla Motors",
+ 0x022B: "Tesla, Inc.",
0x022C: "Pharynks Corporation",
0x022D: "Lupine",
0x022E: "Siemens AG",
@@ -600,7 +600,7 @@
0x0242: "16Lab Inc",
0x0243: "Masimo Corp",
0x0244: "Iotera Inc",
- 0x0245: "Endress+Hauser ",
+ 0x0245: "Endress+Hauser",
0x0246: "ACKme Networks, Inc.",
0x0247: "FiftyThree Inc.",
0x0248: "Parker Hannifin Corp",
@@ -664,7 +664,7 @@
0x0282: "Sonova AG",
0x0283: "Maven Machines, Inc.",
0x0284: "Synapse Electronics",
- 0x0285: "Standard Innovation Inc.",
+ 0x0285: "WOWTech Canada Ltd.",
0x0286: "RF Code, Inc.",
0x0287: "Wally Ventures S.L.",
0x0288: "Willowbank Electronics Ltd",
@@ -693,10 +693,10 @@
0x029F: "Areus Engineering GmbH",
0x02A0: "Impossible Camera GmbH",
0x02A1: "InventureTrack Systems",
- 0x02A2: "LockedUp",
+ 0x02A2: "Sera4 Ltd.",
0x02A3: "Itude",
0x02A4: "Pacific Lock Company",
- 0x02A5: "Tendyron Corporation ( 天地融科技股份有限公司 )",
+ 0x02A5: "Tendyron Corporation",
0x02A6: "Robert Bosch GmbH",
0x02A7: "Illuxtron international B.V.",
0x02A8: "miSport Ltd.",
@@ -709,7 +709,7 @@
0x02AF: "Technicolor USA Inc.",
0x02B0: "Bestechnic(Shanghai),Ltd",
0x02B1: "Raden Inc",
- 0x02B2: "JouZen Oy",
+ 0x02B2: "Oura Health Oy",
0x02B3: "CLABER S.P.A.",
0x02B4: "Hyginex, Inc.",
0x02B5: "HANSHIN ELECTRIC RAILWAY CO.,LTD.",
@@ -728,10 +728,10 @@
0x02C2: "Guillemot Corporation",
0x02C3: "Techtronic Power Tools Technology Limited",
0x02C4: "Wilson Sporting Goods",
- 0x02C5: "Lenovo (Singapore) Pte Ltd. ( 联想(新加坡) )",
+ 0x02C5: "Lenovo (Singapore) Pte Ltd.",
0x02C6: "Ayatan Sensors",
0x02C7: "Electronics Tomorrow Limited",
- 0x02C8: "VASCO Data Security International, Inc.",
+ 0x02C8: "OneSpan",
0x02C9: "PayRange Inc.",
0x02CA: "ABOV Semiconductor",
0x02CB: "AINA-Wireless Inc.",
@@ -760,7 +760,7 @@
0x02E2: "NTT docomo",
0x02E3: "Carmanah Technologies Corp.",
0x02E4: "Bytestorm Ltd.",
- 0x02E5: "Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 )",
+ 0x02E5: "Espressif Systems (Shanghai) Co., Ltd.",
0x02E6: "Unwire",
0x02E7: "Connected Yard, Inc.",
0x02E8: "American Music Environments",
@@ -768,10 +768,10 @@
0x02EA: "Fujitsu Limited",
0x02EB: "Ardic Technology",
0x02EC: "Delta Systems, Inc",
- 0x02ED: "HTC Corporation ",
- 0x02EE: "Citizen Holdings Co., Ltd. ",
+ 0x02ED: "HTC Corporation",
+ 0x02EE: "Citizen Holdings Co., Ltd.",
0x02EF: "SMART-INNOVATION.inc",
- 0x02F0: "Blackrat Software ",
+ 0x02F0: "Blackrat Software",
0x02F1: "The Idea Cave, LLC",
0x02F2: "GoPro, Inc.",
0x02F3: "AuthAir, Inc",
@@ -780,7 +780,7 @@
0x02F6: "Intemo Technologies",
0x02F7: "DreamVisions co., Ltd.",
0x02F8: "Runteq Oy Ltd",
- 0x02F9: "IMAGINATION TECHNOLOGIES LTD ",
+ 0x02F9: "IMAGINATION TECHNOLOGIES LTD",
0x02FA: "CoSTAR TEchnologies",
0x02FB: "Clarius Mobile Health Corp.",
0x02FC: "Shanghai Frequen Microelectronics Co., Ltd.",
@@ -793,7 +793,7 @@
0x0303: "IACA electronique",
0x0304: "Proxy Technologies, Inc.",
0x0305: "Swipp ApS",
- 0x0306: "Life Laboratory Inc. ",
+ 0x0306: "Life Laboratory Inc.",
0x0307: "FUJI INDUSTRIAL CO.,LTD.",
0x0308: "Surefire, LLC",
0x0309: "Dolby Labs",
@@ -805,101 +805,101 @@
0x030F: "Shortcut Labs",
0x0310: "SGL Italia S.r.l.",
0x0311: "PEEQ DATA",
- 0x0312: "Ducere Technologies Pvt Ltd ",
- 0x0313: "DiveNav, Inc. ",
+ 0x0312: "Ducere Technologies Pvt Ltd",
+ 0x0313: "DiveNav, Inc.",
0x0314: "RIIG AI Sp. z o.o.",
- 0x0315: "Thermo Fisher Scientific ",
- 0x0316: "AG Measurematics Pvt. Ltd. ",
- 0x0317: "CHUO Electronics CO., LTD. ",
- 0x0318: "Aspenta International ",
- 0x0319: "Eugster Frismag AG ",
- 0x031A: "Amber wireless GmbH ",
- 0x031B: "HQ Inc ",
- 0x031C: "Lab Sensor Solutions ",
- 0x031D: "Enterlab ApS ",
+ 0x0315: "Thermo Fisher Scientific",
+ 0x0316: "AG Measurematics Pvt. Ltd.",
+ 0x0317: "CHUO Electronics CO., LTD.",
+ 0x0318: "Aspenta International",
+ 0x0319: "Eugster Frismag AG",
+ 0x031A: "Wurth Elektronik eiSos GmbH & Co. KG",
+ 0x031B: "HQ Inc",
+ 0x031C: "Lab Sensor Solutions",
+ 0x031D: "Enterlab ApS",
0x031E: "Eyefi, Inc.",
- 0x031F: "MetaSystem S.p.A. ",
- 0x0320: "SONO ELECTRONICS. CO., LTD ",
- 0x0321: "Jewelbots ",
- 0x0322: "Compumedics Limited ",
- 0x0323: "Rotor Bike Components ",
- 0x0324: "Astro, Inc. ",
- 0x0325: "Amotus Solutions ",
- 0x0326: "Healthwear Technologies (Changzhou)Ltd ",
- 0x0327: "Essex Electronics ",
+ 0x031F: "MetaSystem S.p.A.",
+ 0x0320: "SONO ELECTRONICS. CO., LTD",
+ 0x0321: "Jewelbots",
+ 0x0322: "Compumedics Limited",
+ 0x0323: "Rotor Bike Components",
+ 0x0324: "Astro, Inc.",
+ 0x0325: "Amotus Solutions",
+ 0x0326: "Healthwear Technologies (Changzhou)Ltd",
+ 0x0327: "Essex Electronics",
0x0328: "Grundfos A/S",
- 0x0329: "Eargo, Inc. ",
- 0x032A: "Electronic Design Lab ",
- 0x032B: "ESYLUX ",
+ 0x0329: "Eargo, Inc.",
+ 0x032A: "Electronic Design Lab",
+ 0x032B: "ESYLUX",
0x032C: "NIPPON SMT.CO.,Ltd",
- 0x032D: "BM innovations GmbH ",
+ 0x032D: "BM innovations GmbH",
0x032E: "indoormap",
- 0x032F: "OttoQ Inc ",
- 0x0330: "North Pole Engineering ",
+ 0x032F: "OttoQ Inc",
+ 0x0330: "North Pole Engineering",
0x0331: "3flares Technologies Inc.",
- 0x0332: "Electrocompaniet A.S. ",
+ 0x0332: "Electrocompaniet A.S.",
0x0333: "Mul-T-Lock",
- 0x0334: "Corentium AS ",
+ 0x0334: "Airthings ASA",
0x0335: "Enlighted Inc",
0x0336: "GISTIC",
0x0337: "AJP2 Holdings, LLC",
- 0x0338: "COBI GmbH ",
- 0x0339: "Blue Sky Scientific, LLC ",
+ 0x0338: "COBI GmbH",
+ 0x0339: "Blue Sky Scientific, LLC",
0x033A: "Appception, Inc.",
- 0x033B: "Courtney Thorne Limited ",
+ 0x033B: "Courtney Thorne Limited",
0x033C: "Virtuosys",
- 0x033D: "TPV Technology Limited ",
+ 0x033D: "TPV Technology Limited",
0x033E: "Monitra SA",
- 0x033F: "Automation Components, Inc. ",
- 0x0340: "Letsense s.r.l. ",
- 0x0341: "Etesian Technologies LLC ",
- 0x0342: "GERTEC BRASIL LTDA. ",
+ 0x033F: "Automation Components, Inc.",
+ 0x0340: "Letsense s.r.l.",
+ 0x0341: "Etesian Technologies LLC",
+ 0x0342: "GERTEC BRASIL LTDA.",
0x0343: "Drekker Development Pty. Ltd.",
- 0x0344: "Whirl Inc ",
- 0x0345: "Locus Positioning ",
- 0x0346: "Acuity Brands Lighting, Inc ",
- 0x0347: "Prevent Biometrics ",
+ 0x0344: "Whirl Inc",
+ 0x0345: "Locus Positioning",
+ 0x0346: "Acuity Brands Lighting, Inc",
+ 0x0347: "Prevent Biometrics",
0x0348: "Arioneo",
- 0x0349: "VersaMe ",
- 0x034A: "Vaddio ",
- 0x034B: "Libratone A/S ",
- 0x034C: "HM Electronics, Inc. ",
+ 0x0349: "VersaMe",
+ 0x034A: "Vaddio",
+ 0x034B: "Libratone A/S",
+ 0x034C: "HM Electronics, Inc.",
0x034D: "TASER International, Inc.",
- 0x034E: "SafeTrust Inc. ",
- 0x034F: "Heartland Payment Systems ",
- 0x0350: "Bitstrata Systems Inc. ",
- 0x0351: "Pieps GmbH ",
+ 0x034E: "SafeTrust Inc.",
+ 0x034F: "Heartland Payment Systems",
+ 0x0350: "Bitstrata Systems Inc.",
+ 0x0351: "Pieps GmbH",
0x0352: "iRiding(Xiamen)Technology Co.,Ltd.",
- 0x0353: "Alpha Audiotronics, Inc. ",
- 0x0354: "TOPPAN FORMS CO.,LTD. ",
- 0x0355: "Sigma Designs, Inc. ",
- 0x0356: "Spectrum Brands, Inc. ",
- 0x0357: "Polymap Wireless ",
+ 0x0353: "Alpha Audiotronics, Inc.",
+ 0x0354: "TOPPAN FORMS CO.,LTD.",
+ 0x0355: "Sigma Designs, Inc.",
+ 0x0356: "Spectrum Brands, Inc.",
+ 0x0357: "Polymap Wireless",
0x0358: "MagniWare Ltd.",
- 0x0359: "Novotec Medical GmbH ",
- 0x035A: "Medicom Innovation Partner a/s ",
- 0x035B: "Matrix Inc. ",
- 0x035C: "Eaton Corporation ",
+ 0x0359: "Novotec Medical GmbH",
+ 0x035A: "Phillips-Medisize A/S",
+ 0x035B: "Matrix Inc.",
+ 0x035C: "Eaton Corporation",
0x035D: "KYS",
- 0x035E: "Naya Health, Inc. ",
- 0x035F: "Acromag ",
- 0x0360: "Insulet Corporation ",
- 0x0361: "Wellinks Inc. ",
+ 0x035E: "Naya Health, Inc.",
+ 0x035F: "Acromag",
+ 0x0360: "Insulet Corporation",
+ 0x0361: "Wellinks Inc.",
0x0362: "ON Semiconductor",
- 0x0363: "FREELAP SA ",
- 0x0364: "Favero Electronics Srl ",
- 0x0365: "BioMech Sensor LLC ",
+ 0x0363: "FREELAP SA",
+ 0x0364: "Favero Electronics Srl",
+ 0x0365: "BioMech Sensor LLC",
0x0366: "BOLTT Sports technologies Private limited",
- 0x0367: "Saphe International ",
- 0x0368: "Metormote AB ",
- 0x0369: "littleBits ",
- 0x036A: "SetPoint Medical ",
- 0x036B: "BRControls Products BV ",
- 0x036C: "Zipcar ",
- 0x036D: "AirBolt Pty Ltd ",
- 0x036E: "KeepTruckin Inc ",
- 0x036F: "Motiv, Inc. ",
- 0x0370: "Wazombi Labs OÜ ",
+ 0x0367: "Saphe International",
+ 0x0368: "Metormote AB",
+ 0x0369: "littleBits",
+ 0x036A: "SetPoint Medical",
+ 0x036B: "BRControls Products BV",
+ 0x036C: "Zipcar",
+ 0x036D: "AirBolt Pty Ltd",
+ 0x036E: "MOTIVE TECHNOLOGIES, INC.",
+ 0x036F: "Motiv, Inc.",
+ 0x0370: "Wazombi Labs OÜ",
0x0371: "ORBCOMM",
0x0372: "Nixie Labs, Inc.",
0x0373: "AppNearMe Ltd",
@@ -914,7 +914,7 @@
0x037C: "Cronologics Corporation",
0x037D: "MICRODIA Ltd.",
0x037E: "lulabytes S.L.",
- 0x037F: "Société des Produits Nestlé S.A. (formerly Nestec S.A.)",
+ 0x037F: "Société des Produits Nestlé S.A.",
0x0380: "LLC \"MEGA-F service\"",
0x0381: "Sharp Corporation",
0x0382: "Precision Outcomes Ltd",
@@ -1022,7 +1022,7 @@
0x03E8: "Reiner Kartengeraete GmbH & Co. KG.",
0x03E9: "SHENZHEN LEMONJOY TECHNOLOGY CO., LTD.",
0x03EA: "Hello Inc.",
- 0x03EB: "Evollve Inc.",
+ 0x03EB: "Ozo Edu, Inc.",
0x03EC: "Jigowatts Inc.",
0x03ED: "BASIC MICRO.COM,INC.",
0x03EE: "CUBE TECHNOLOGIES",
@@ -1040,7 +1040,7 @@
0x03FA: "Vyassoft Technologies Inc",
0x03FB: "Nox Medical",
0x03FC: "Kimberly-Clark",
- 0x03FD: "Trimble Navigation Ltd.",
+ 0x03FD: "Trimble Inc.",
0x03FE: "Littelfuse",
0x03FF: "Withings",
0x0400: "i-developer IT Beratung UG",
@@ -1052,8 +1052,8 @@
0x0406: "Airtago",
0x0407: "Swiss Audio SA",
0x0408: "ToGetHome Inc.",
- 0x0409: "AXIS",
- 0x040A: "Openmatics",
+ 0x0409: "RYSE INC.",
+ 0x040A: "ZF OPENMATICS s.r.o.",
0x040B: "Jana Care Inc.",
0x040C: "Senix Corporation",
0x040D: "NorthStar Battery Company, LLC",
@@ -1092,7 +1092,7 @@
0x042E: "MemCachier Inc.",
0x042F: "Danfoss A/S",
0x0430: "SnapStyk Inc.",
- 0x0431: "Amway Corporation",
+ 0x0431: "Alticor Inc.",
0x0432: "Silk Labs, Inc.",
0x0433: "Pillsy Inc.",
0x0434: "Hatch Baby, Inc.",
@@ -1126,7 +1126,7 @@
0x0450: "Teenage Engineering AB",
0x0451: "Tunstall Nordic AB",
0x0452: "Svep Design Center AB",
- 0x0453: "Qorvo Utrecht B.V. formerly GreenPeak Technologies BV",
+ 0x0453: "Qorvo Utrecht B.V.",
0x0454: "Sphinx Electronics GmbH & Co KG",
0x0455: "Atomation",
0x0456: "Nemik Consulting Inc",
@@ -1202,7 +1202,7 @@
0x049C: "DyOcean",
0x049D: "Uhlmann & Zacher GmbH",
0x049E: "AND!XOR LLC",
- 0x049F: "tictote AB",
+ 0x049F: "Popper Pay AB",
0x04A0: "Vypin, LLC",
0x04A1: "PNI Sensor Corporation",
0x04A2: "ovrEngineered, LLC",
@@ -1260,13 +1260,13 @@
0x04D6: "LUGLOC LLC",
0x04D7: "Blincam, Inc.",
0x04D8: "FUJIFILM Corporation",
- 0x04D9: "RandMcNally",
+ 0x04D9: "RM Acquisition LLC",
0x04DA: "Franceschi Marina snc",
0x04DB: "Engineered Audio, LLC.",
0x04DC: "IOTTIVE (OPC) PRIVATE LIMITED",
0x04DD: "4MOD Technology",
0x04DE: "Lutron Electronics Co., Inc.",
- 0x04DF: "Emerson",
+ 0x04DF: "Emerson Electric Co.",
0x04E0: "Guardtec, Inc.",
0x04E1: "REACTEC LIMITED",
0x04E2: "EllieGrid",
@@ -1290,7 +1290,7 @@
0x04F4: "ZanCompute Inc.",
0x04F5: "Pirelli Tyre S.P.A.",
0x04F6: "McLear Limited",
- 0x04F7: "Shenzhen Huiding Technology Co.,Ltd.",
+ 0x04F7: "Shenzhen Goodix Technology Co., Ltd",
0x04F8: "Convergence Systems Limited",
0x04F9: "Interactio",
0x04FA: "Androtec GmbH",
@@ -1304,11 +1304,11 @@
0x0502: "Specifi-Kali LLC",
0x0503: "Locoroll, Inc",
0x0504: "PHYPLUS Inc",
- 0x0505: "Inplay Technologies LLC",
+ 0x0505: "InPlay, Inc.",
0x0506: "Hager",
0x0507: "Yellowcog",
0x0508: "Axes System sp. z o. o.",
- 0x0509: "myLIFTER Inc.",
+ 0x0509: "Garage Smart, Inc.",
0x050A: "Shake-on B.V.",
0x050B: "Vibrissa Inc.",
0x050C: "OSRAM GmbH",
@@ -1322,7 +1322,7 @@
0x0514: "FIBRO GmbH",
0x0515: "RB Controls Co., Ltd.",
0x0516: "Footmarks",
- 0x0517: "Amtronic Sverige AB (formerly Amcore AB)",
+ 0x0517: "Amtronic Sverige AB",
0x0518: "MAMORIO.inc",
0x0519: "Tyto Life LLC",
0x051A: "Leica Camera AG",
@@ -1370,7 +1370,7 @@
0x0544: "OrthoSensor, Inc.",
0x0545: "Candy Hoover Group s.r.l",
0x0546: "Apexar Technologies S.A.",
- 0x0547: "LOGICDATA d.o.o.",
+ 0x0547: "LOGICDATA Electronic & Software Entwicklungs GmbH",
0x0548: "Knick Elektronische Messgeraete GmbH & Co. KG",
0x0549: "Smart Technologies and Investment Limited",
0x054A: "Linough Inc.",
@@ -1395,7 +1395,7 @@
0x055D: "Valve Corporation",
0x055E: "Hekatron Vertriebs GmbH",
0x055F: "PROTECH S.A.S. DI GIRARDI ANDREA & C.",
- 0x0560: "Sarita CareTech APS (formerly Sarita CareTech IVS)",
+ 0x0560: "Sarita CareTech APS",
0x0561: "Finder S.p.A.",
0x0562: "Thalmic Labs Inc.",
0x0563: "Steinel Vertrieb GmbH",
@@ -1439,9 +1439,9 @@
0x0589: "Star Technologies",
0x058A: "START TODAY CO.,LTD.",
0x058B: "Maxim Integrated Products",
- 0x058C: "MERCK Kommanditgesellschaft auf Aktien",
+ 0x058C: "Fracarro Radioindustrie SRL",
0x058D: "Jungheinrich Aktiengesellschaft",
- 0x058E: "Oculus VR, LLC",
+ 0x058E: "Meta Platforms Technologies, LLC",
0x058F: "HENDON SEMICONDUCTORS PTY LTD",
0x0590: "Pur3 Ltd",
0x0591: "Viasat Group S.p.A.",
@@ -1474,7 +1474,7 @@
0x05AC: "GoerTek Dynaudio Co., Ltd.",
0x05AD: "INIA",
0x05AE: "CARMATE MFG.CO.,LTD",
- 0x05AF: "OV LOOP, INC. (formerly ONvocal)",
+ 0x05AF: "OV LOOP, INC.",
0x05B0: "NewTec GmbH",
0x05B1: "Medallion Instrumentation Systems",
0x05B2: "CAREL INDUSTRIES S.P.A.",
@@ -1537,7 +1537,7 @@
0x05EB: "Bayerische Motoren Werke AG",
0x05EC: "Gycom Svenska AB",
0x05ED: "Fuji Xerox Co., Ltd",
- 0x05EE: "Glide Inc.",
+ 0x05EE: "Wristcam Inc.",
0x05EF: "SIKOM AS",
0x05F0: "beken",
0x05F1: "The Linux Foundation",
@@ -1570,7 +1570,7 @@
0x060C: "Vuzix Corporation",
0x060D: "TDK Corporation",
0x060E: "Blueair AB",
- 0x060F: "Signify Netherlands",
+ 0x060F: "Signify Netherlands B.V.",
0x0610: "ADH GUARDIAN USA LLC",
0x0611: "Beurer GmbH",
0x0612: "Playfinity AS",
@@ -1619,7 +1619,7 @@
0x063D: "Xradio Technology Co.,Ltd.",
0x063E: "The Indoor Lab, LLC",
0x063F: "LDL TECHNOLOGY",
- 0x0640: "Parkifi",
+ 0x0640: "Dish Network LLC",
0x0641: "Revenue Collection Systems FRANCE SAS",
0x0642: "Bluetrum Technology Co.,Ltd",
0x0643: "makita corporation",
@@ -1709,7 +1709,7 @@
0x0697: "Stemco Products Inc",
0x0698: "Wood IT Security, LLC",
0x0699: "RandomLab SAS",
- 0x069A: "Adero, Inc. (formerly as TrackR, Inc.)",
+ 0x069A: "Adero, Inc.",
0x069B: "Dragonchip Limited",
0x069C: "Noomi AB",
0x069D: "Vakaros LLC",
@@ -1719,7 +1719,7 @@
0x06A1: "Cardo Systems, Ltd",
0x06A2: "Globalworx GmbH",
0x06A3: "Nymbus, LLC",
- 0x06A4: "Sanyo Techno Solutions Tottori Co., Ltd.",
+ 0x06A4: "LIMNO Co. Ltd.",
0x06A5: "TEKZITEL PTY LTD",
0x06A6: "Roambee Corporation",
0x06A7: "Chipsea Technologies (ShenZhen) Corp.",
@@ -1778,8 +1778,8 @@
0x06DC: "Industrial Network Controls, LLC",
0x06DD: "Intellithings Ltd.",
0x06DE: "Navcast, Inc.",
- 0x06DF: "Hubbell Lighting, Inc.",
- 0x06E0: "Avaya ",
+ 0x06DF: "HLI Solutions Inc.",
+ 0x06E0: "Avaya Inc.",
0x06E1: "Milestone AV Technologies LLC",
0x06E2: "Alango Technologies Ltd",
0x06E3: "Spinlock Ltd",
@@ -1799,7 +1799,7 @@
0x06F1: "Shibutani Co., Ltd.",
0x06F2: "Trapper Data AB",
0x06F3: "Alfred International Inc.",
- 0x06F4: "Near Field Solutions Ltd",
+ 0x06F4: "Touché Technology Ltd",
0x06F5: "Vigil Technologies Inc.",
0x06F6: "Vitulo Plus BV",
0x06F7: "WILKA Schliesstechnik GmbH",
@@ -1832,11 +1832,11 @@
0x0712: "Bull Group Company Limited",
0x0713: "Respiri Limited",
0x0714: "MindPeace Safety LLC",
- 0x0715: "Vgyan Solutions",
+ 0x0715: "MBARC LABS Inc",
0x0716: "Altonics",
0x0717: "iQsquare BV",
0x0718: "IDIBAIX enginneering",
- 0x0719: "ECSG",
+ 0x0719: "COREIOT PTY LTD",
0x071A: "REVSMART WEARABLE HK CO LTD",
0x071B: "Precor",
0x071C: "F5 Sports, Inc",
@@ -1865,7 +1865,7 @@
0x0733: "Iguanavation, Inc.",
0x0734: "DiUS Computing Pty Ltd",
0x0735: "UpRight Technologies LTD",
- 0x0736: "FrancisFund, LLC",
+ 0x0736: "Luna XIO, Inc.",
0x0737: "LLC Navitek",
0x0738: "Glass Security Pte Ltd",
0x0739: "Jiangsu Qinheng Co., Ltd.",
@@ -1948,6 +1948,7 @@
0x0786: "HLP Controls Pty Limited",
0x0787: "Pangaea Solution",
0x0788: "BubblyNet, LLC",
+ 0x0789: "PCB Piezotronics, Inc.",
0x078A: "The Wildflower Foundation",
0x078B: "Optikam Tech Inc.",
0x078C: "MINIBREW HOLDING B.V",
@@ -1956,7 +1957,7 @@
0x078F: "Hanna Instruments, Inc.",
0x0790: "KOMPAN A/S",
0x0791: "Scosche Industries, Inc.",
- 0x0792: "Provo Craft",
+ 0x0792: "Cricut, Inc.",
0x0793: "AEV spol. s r.o.",
0x0794: "The Coca-Cola Company",
0x0795: "GASTEC CORPORATION",
@@ -2030,17 +2031,17 @@
0x07D9: "Micro-Design, Inc.",
0x07DA: "STARLITE Co., Ltd.",
0x07DB: "Remedee Labs",
- 0x07DC: "ThingOS GmbH",
+ 0x07DC: "ThingOS GmbH & Co KG",
0x07DD: "Linear Circuits",
0x07DE: "Unlimited Engineering SL",
0x07DF: "Snap-on Incorporated",
0x07E0: "Edifier International Limited",
0x07E1: "Lucie Labs",
0x07E2: "Alfred Kaercher SE & Co. KG",
- 0x07E3: "Audiowise Technology Inc.",
+ 0x07E3: "Airoha Technology Corp.",
0x07E4: "Geeksme S.L.",
0x07E5: "Minut, Inc.",
- 0x07E6: "Autogrow Systems Limited",
+ 0x07E6: "Waybeyond Limited",
0x07E7: "Komfort IQ, Inc.",
0x07E8: "Packetcraft, Inc.",
0x07E9: "Häfele GmbH & Co KG",
@@ -2051,7 +2052,7 @@
0x07EE: "KidzTek LLC",
0x07EF: "Aktiebolaget Sandvik Coromant",
0x07F0: "e-moola.com Pty Ltd",
- 0x07F1: "GSM Innovations Pty Ltd",
+ 0x07F1: "Zimi Innovations Pty Ltd",
0x07F2: "SERENE GROUP, INC",
0x07F3: "DIGISINE ENERGYTECH CO. LTD.",
0x07F4: "MEDIRLAB Orvosbiologiai Fejleszto Korlatolt Felelossegu Tarsasag",
@@ -2107,7 +2108,7 @@
0x0826: "Hyundai Motor Company",
0x0827: "Kickmaker",
0x0828: "Shanghai Suisheng Information Technology Co., Ltd.",
- 0x0829: "HEXAGON",
+ 0x0829: "HEXAGON METROLOGY DIVISION ROMER",
0x082A: "Mitutoyo Corporation",
0x082B: "shenzhen fitcare electronics Co.,Ltd",
0x082C: "INGICS TECHNOLOGY CO., LTD.",
@@ -2151,20 +2152,20 @@
0x0852: "Cognosos, Inc.",
0x0853: "Pektron Group Limited",
0x0854: "Tap Sound System",
- 0x0855: "Helios Hockey, Inc.",
+ 0x0855: "Helios Sports, Inc.",
0x0856: "Canopy Growth Corporation",
0x0857: "Parsyl Inc",
0x0858: "SOUNDBOKS",
0x0859: "BlueUp",
0x085A: "DAKATECH",
- 0x085B: "RICOH ELECTRONIC DEVICES CO., LTD.",
+ 0x085B: "Nisshinbo Micro Devices Inc.",
0x085C: "ACOS CO.,LTD.",
0x085D: "Guilin Zhishen Information Technology Co.,Ltd.",
0x085E: "Krog Systems LLC",
0x085F: "COMPEGPS TEAM,SOCIEDAD LIMITADA",
0x0860: "Alflex Products B.V.",
0x0861: "SmartSensor Labs Ltd",
- 0x0862: "SmartDrive Inc.",
+ 0x0862: "SmartDrive",
0x0863: "Yo-tronics Technology Co., Ltd.",
0x0864: "Rafaelmicro",
0x0865: "Emergency Lighting Products Limited",
@@ -2179,7 +2180,7 @@
0x086E: "Vorwerk Elektrowerke GmbH & Co. KG",
0x086F: "Trackunit A/S",
0x0870: "Wyze Labs, Inc",
- 0x0871: "Dension Elektronikai Kft. (formerly: Dension Audio Systems Ltd.)",
+ 0x0871: "Dension Elektronikai Kft.",
0x0872: "11 Health & Technologies Limited",
0x0873: "Innophase Incorporated",
0x0874: "Treegreen Limited",
@@ -2199,8 +2200,8 @@
0x0882: "PSYONIC, Inc.",
0x0883: "Wintersteiger AG",
0x0884: "Controlid Industria, Comercio de Hardware e Servicos de Tecnologia Ltda",
- 0x0885: "LEVOLOR, INC.",
- 0x0886: "Xsens Technologies B.V.",
+ 0x0885: "LEVOLOR INC",
+ 0x0886: "Movella Technologies B.V.",
0x0887: "Hydro-Gear Limited Partnership",
0x0888: "EnPointe Fencing Pty Ltd",
0x0889: "XANTHIO",
@@ -2288,7 +2289,7 @@
0x08DB: "Tertium Technology",
0x08DC: "SHENZHEN AUKEY E BUSINESS CO., LTD",
0x08DD: "code-Q",
- 0x08DE: "Tyco Electronics Corporation a TE Connectivity Ltd Company",
+ 0x08DE: "TE Connectivity Corporation",
0x08DF: "IRIS OHYAMA CO.,LTD.",
0x08E0: "Philia Technology",
0x08E1: "KOZO KEIKAKU ENGINEERING Inc.",
@@ -2297,14 +2298,14 @@
0x08E4: "Rashidov ltd",
0x08E5: "Crowd Connected Ltd",
0x08E6: "Eneso Tecnologia de Adaptacion S.L.",
- 0x08E7: "Barrot Technology Limited",
+ 0x08E7: "Barrot Technology Co.,Ltd.",
0x08E8: "Naonext",
0x08E9: "Taiwan Intelligent Home Corp.",
0x08EA: "COWBELL ENGINEERING CO.,LTD.",
0x08EB: "Beijing Big Moment Technology Co., Ltd.",
0x08EC: "Denso Corporation",
0x08ED: "IMI Hydronic Engineering International SA",
- 0x08EE: "ASKEY",
+ 0x08EE: "Askey Computer Corp.",
0x08EF: "Cumulus Digital Systems, Inc",
0x08F0: "Joovv, Inc.",
0x08F1: "The L.S. Starrett Company",
@@ -2389,7 +2390,7 @@
0x0940: "Hero Workout GmbH",
0x0941: "Rivian Automotive, LLC",
0x0942: "TRANSSION HOLDINGS LIMITED",
- 0x0943: "Inovonics Corp.",
+ 0x0943: "Reserved",
0x0944: "Agitron d.o.o.",
0x0945: "Globe (Jiangsu) Co., Ltd",
0x0946: "AMC International Alfa Metalcraft Corporation AG",
@@ -2415,7 +2416,7 @@
0x095A: "Selekt Bilgisayar, lletisim Urunleri lnsaat Sanayi ve Ticaret Limited Sirketi",
0x095B: "Lismore Instruments Limited",
0x095C: "LogiLube, LLC",
- 0x095D: "ETC",
+ 0x095D: "Electronic Theatre Controls",
0x095E: "BioEchoNet inc.",
0x095F: "NUANCE HEARING LTD",
0x0960: "Sena Technologies Inc.",
@@ -2490,7 +2491,7 @@
0x09A5: "Security Enhancement Systems, LLC",
0x09A6: "BEIJING ELECTRIC VEHICLE CO.,LTD",
0x09A7: "Paybuddy ApS",
- 0x09A8: "KHN Solutions Inc",
+ 0x09A8: "KHN Solutions LLC",
0x09A9: "Nippon Ceramic Co.,Ltd.",
0x09AA: "PHOTODYNAMIC INCORPORATED",
0x09AB: "DashLogic, Inc.",
@@ -2618,7 +2619,7 @@
0x0A25: "Eran Financial Services LLC",
0x0A26: "Louis Vuitton",
0x0A27: "AYU DEVICES PRIVATE LIMITED",
- 0x0A28: "NanoFlex",
+ 0x0A28: "NanoFlex Power Corporation",
0x0A29: "Worthcloud Technology Co.,Ltd",
0x0A2A: "Yamaha Corporation",
0x0A2B: "PaceBait IVS",
@@ -2697,13 +2698,652 @@
0x0A74: "MICROSON S.A.",
0x0A75: "Delta Cycle Corporation",
0x0A76: "Synaptics Incorporated",
- 0x0A77: "JMD PACIFIC PTE. LTD.",
+ 0x0A77: "AXTRO PTE. LTD.",
0x0A78: "Shenzhen Sunricher Technology Limited",
0x0A79: "Webasto SE",
0x0A7A: "Emlid Limited",
0x0A7B: "UniqAir Oy",
0x0A7C: "WAFERLOCK",
0x0A7D: "Freedman Electronics Pty Ltd",
- 0x0A7E: "Keba AG",
+ 0x0A7E: "KEBA Handover Automation GmbH",
0x0A7F: "Intuity Medical",
+ 0x0A80: "Cleer Limited",
+ 0x0A81: "Universal Biosensors Pty Ltd",
+ 0x0A82: "Corsair",
+ 0x0A83: "Rivata, Inc.",
+ 0x0A84: "Greennote Inc,",
+ 0x0A85: "Snowball Technology Co., Ltd.",
+ 0x0A86: "ALIZENT International",
+ 0x0A87: "Shanghai Smart System Technology Co., Ltd",
+ 0x0A88: "PSA Peugeot Citroen",
+ 0x0A89: "SES-Imagotag",
+ 0x0A8A: "HAINBUCH GMBH SPANNENDE TECHNIK",
+ 0x0A8B: "SANlight GmbH",
+ 0x0A8C: "DelpSys, s.r.o.",
+ 0x0A8D: "JCM TECHNOLOGIES S.A.",
+ 0x0A8E: "Perfect Company",
+ 0x0A8F: "TOTO LTD.",
+ 0x0A90: "Shenzhen Grandsun Electronic Co.,Ltd.",
+ 0x0A91: "Monarch International Inc.",
+ 0x0A92: "Carestream Dental LLC",
+ 0x0A93: "GiPStech S.r.l.",
+ 0x0A94: "OOBIK Inc.",
+ 0x0A95: "Pamex Inc.",
+ 0x0A96: "Lightricity Ltd",
+ 0x0A97: "SensTek",
+ 0x0A98: "Foil, Inc.",
+ 0x0A99: "Shanghai high-flying electronics technology Co.,Ltd",
+ 0x0A9A: "TEMKIN ASSOCIATES, LLC",
+ 0x0A9B: "Eello LLC",
+ 0x0A9C: "Xi'an Fengyu Information Technology Co., Ltd.",
+ 0x0A9D: "Canon Finetech Nisca Inc.",
+ 0x0A9E: "LifePlus, Inc.",
+ 0x0A9F: "ista International GmbH",
+ 0x0AA0: "Loy Tec electronics GmbH",
+ 0x0AA1: "LINCOGN TECHNOLOGY CO. LIMITED",
+ 0x0AA2: "Care Bloom, LLC",
+ 0x0AA3: "DIC Corporation",
+ 0x0AA4: "FAZEPRO LLC",
+ 0x0AA5: "Shenzhen Uascent Technology Co., Ltd",
+ 0x0AA6: "Realityworks, inc.",
+ 0x0AA7: "Urbanista AB",
+ 0x0AA8: "Zencontrol Pty Ltd",
+ 0x0AA9: "Spintly, Inc.",
+ 0x0AAA: "Computime International Ltd",
+ 0x0AAB: "Anhui Listenai Co",
+ 0x0AAC: "OSM HK Limited",
+ 0x0AAD: "Adevo Consulting AB",
+ 0x0AAE: "PS Engineering, Inc.",
+ 0x0AAF: "AIAIAI ApS",
+ 0x0AB0: "Visiontronic s.r.o.",
+ 0x0AB1: "InVue Security Products Inc",
+ 0x0AB2: "TouchTronics, Inc.",
+ 0x0AB3: "INNER RANGE PTY. LTD.",
+ 0x0AB4: "Ellenby Technologies, Inc.",
+ 0x0AB5: "Elstat Electronics Ltd.",
+ 0x0AB6: "Xenter, Inc.",
+ 0x0AB7: "LogTag North America Inc.",
+ 0x0AB8: "Sens.ai Incorporated",
+ 0x0AB9: "STL",
+ 0x0ABA: "Open Bionics Ltd.",
+ 0x0ABB: "R-DAS, s.r.o.",
+ 0x0ABC: "KCCS Mobile Engineering Co., Ltd.",
+ 0x0ABD: "Inventas AS",
+ 0x0ABE: "Robkoo Information & Technologies Co., Ltd.",
+ 0x0ABF: "PAUL HARTMANN AG",
+ 0x0AC0: "Omni-ID USA, INC.",
+ 0x0AC1: "Shenzhen Jingxun Technology Co., Ltd.",
+ 0x0AC2: "RealMega Microelectronics technology (Shanghai) Co. Ltd.",
+ 0x0AC3: "Kenzen, Inc.",
+ 0x0AC4: "CODIUM",
+ 0x0AC5: "Flexoptix GmbH",
+ 0x0AC6: "Barnes Group Inc.",
+ 0x0AC7: "Chengdu Aich Technology Co.,Ltd",
+ 0x0AC8: "Keepin Co., Ltd.",
+ 0x0AC9: "Swedlock AB",
+ 0x0ACA: "Shenzhen CoolKit Technology Co., Ltd",
+ 0x0ACB: "ise Individuelle Software und Elektronik GmbH",
+ 0x0ACC: "Nuvoton",
+ 0x0ACD: "Visuallex Sport International Limited",
+ 0x0ACE: "KOBATA GAUGE MFG. CO., LTD.",
+ 0x0ACF: "CACI Technologies",
+ 0x0AD0: "Nordic Strong ApS",
+ 0x0AD1: "EAGLE KINGDOM TECHNOLOGIES LIMITED",
+ 0x0AD2: "Lautsprecher Teufel GmbH",
+ 0x0AD3: "SSV Software Systems GmbH",
+ 0x0AD4: "Zhuhai Pantum Electronisc Co., Ltd",
+ 0x0AD5: "Streamit B.V.",
+ 0x0AD6: "nymea GmbH",
+ 0x0AD7: "AL-KO Geraete GmbH",
+ 0x0AD8: "Franz Kaldewei GmbH&Co KG",
+ 0x0AD9: "Shenzhen Aimore. Co.,Ltd",
+ 0x0ADA: "Codefabrik GmbH",
+ 0x0ADB: "Reelables, Inc.",
+ 0x0ADC: "Duravit AG",
+ 0x0ADD: "Boss Audio",
+ 0x0ADE: "Vocera Communications, Inc.",
+ 0x0ADF: "Douglas Dynamics L.L.C.",
+ 0x0AE0: "Viceroy Devices Corporation",
+ 0x0AE1: "ChengDu ForThink Technology Co., Ltd.",
+ 0x0AE2: "IMATRIX SYSTEMS, INC.",
+ 0x0AE3: "GlobalMed",
+ 0x0AE4: "DALI Alliance",
+ 0x0AE5: "unu GmbH",
+ 0x0AE6: "Hexology",
+ 0x0AE7: "Sunplus Technology Co., Ltd.",
+ 0x0AE8: "LEVEL, s.r.o.",
+ 0x0AE9: "FLIR Systems AB",
+ 0x0AEA: "Borda Technology",
+ 0x0AEB: "Square, Inc.",
+ 0x0AEC: "FUTEK ADVANCED SENSOR TECHNOLOGY, INC",
+ 0x0AED: "Saxonar GmbH",
+ 0x0AEE: "Velentium, LLC",
+ 0x0AEF: "GLP German Light Products GmbH",
+ 0x0AF0: "Leupold & Stevens, Inc.",
+ 0x0AF1: "CRADERS,CO.,LTD",
+ 0x0AF2: "Shanghai All Link Microelectronics Co.,Ltd",
+ 0x0AF3: "701x Inc.",
+ 0x0AF4: "Radioworks Microelectronics PTY LTD",
+ 0x0AF5: "Unitech Electronic Inc.",
+ 0x0AF6: "AMETEK, Inc.",
+ 0x0AF7: "Irdeto",
+ 0x0AF8: "First Design System Inc.",
+ 0x0AF9: "Unisto AG",
+ 0x0AFA: "Chengdu Ambit Technology Co., Ltd.",
+ 0x0AFB: "SMT ELEKTRONIK GmbH",
+ 0x0AFC: "Cerebrum Sensor Technologies Inc.",
+ 0x0AFD: "Weber Sensors, LLC",
+ 0x0AFE: "Earda Technologies Co.,Ltd",
+ 0x0AFF: "FUSEAWARE LIMITED",
+ 0x0B00: "Flaircomm Microelectronics Inc.",
+ 0x0B01: "RESIDEO TECHNOLOGIES, INC.",
+ 0x0B02: "IORA Technology Development Ltd. Sti.",
+ 0x0B03: "Precision Triathlon Systems Limited",
+ 0x0B04: "I-PERCUT",
+ 0x0B05: "Marquardt GmbH",
+ 0x0B06: "FAZUA GmbH",
+ 0x0B07: "Workaround Gmbh",
+ 0x0B08: "Shenzhen Qianfenyi Intelligent Technology Co., LTD",
+ 0x0B09: "soonisys",
+ 0x0B0A: "Belun Technology Company Limited",
+ 0x0B0B: "Sanistaal A/S",
+ 0x0B0C: "BluPeak",
+ 0x0B0D: "SANYO DENKO Co.,Ltd.",
+ 0x0B0E: "Honda Lock Mfg. Co.,Ltd.",
+ 0x0B0F: "B.E.A. S.A.",
+ 0x0B10: "Alfa Laval Corporate AB",
+ 0x0B11: "ThermoWorks, Inc.",
+ 0x0B12: "ToughBuilt Industries LLC",
+ 0x0B13: "IOTOOLS",
+ 0x0B14: "Olumee",
+ 0x0B15: "NAOS JAPAN K.K.",
+ 0x0B16: "Guard RFID Solutions Inc.",
+ 0x0B17: "SIG SAUER, INC.",
+ 0x0B18: "DECATHLON SE",
+ 0x0B19: "WBS PROJECT H PTY LTD",
+ 0x0B1A: "Roca Sanitario, S.A.",
+ 0x0B1B: "Enerpac Tool Group Corp.",
+ 0x0B1C: "Nanoleq AG",
+ 0x0B1D: "Accelerated Systems",
+ 0x0B1E: "PB INC.",
+ 0x0B1F: "Beijing ESWIN Computing Technology Co., Ltd.",
+ 0x0B20: "TKH Security B.V.",
+ 0x0B21: "ams AG",
+ 0x0B22: "Hygiene IQ, LLC.",
+ 0x0B23: "iRhythm Technologies, Inc.",
+ 0x0B24: "BeiJing ZiJie TiaoDong KeJi Co.,Ltd.",
+ 0x0B25: "NIBROTECH LTD",
+ 0x0B26: "Baracoda Daily Healthtech.",
+ 0x0B27: "Lumi United Technology Co., Ltd",
+ 0x0B28: "CHACON",
+ 0x0B29: "Tech-Venom Entertainment Private Limited",
+ 0x0B2A: "ACL Airshop B.V.",
+ 0x0B2B: "MAINBOT",
+ 0x0B2C: "ILLUMAGEAR, Inc.",
+ 0x0B2D: "REDARC ELECTRONICS PTY LTD",
+ 0x0B2E: "MOCA System Inc.",
+ 0x0B2F: "Duke Manufacturing Co",
+ 0x0B30: "ART SPA",
+ 0x0B31: "Silver Wolf Vehicles Inc.",
+ 0x0B32: "Hala Systems, Inc.",
+ 0x0B33: "ARMATURA LLC",
+ 0x0B34: "CONZUMEX INDUSTRIES PRIVATE LIMITED",
+ 0x0B35: "BH SENS",
+ 0x0B36: "SINTEF",
+ 0x0B37: "Omnivoltaic Energy Solutions Limited Company",
+ 0x0B38: "WISYCOM S.R.L.",
+ 0x0B39: "Red 100 Lighting Co., ltd.",
+ 0x0B3A: "Impact Biosystems, Inc.",
+ 0x0B3B: "AIC semiconductor (Shanghai) Co., Ltd.",
+ 0x0B3C: "Dodge Industrial, Inc.",
+ 0x0B3D: "REALTIMEID AS",
+ 0x0B3E: "ISEO Serrature S.p.a.",
+ 0x0B3F: "MindRhythm, Inc.",
+ 0x0B40: "Havells India Limited",
+ 0x0B41: "Sentrax GmbH",
+ 0x0B42: "TSI",
+ 0x0B43: "INCITAT ENVIRONNEMENT",
+ 0x0B44: "nFore Technology Co., Ltd.",
+ 0x0B45: "Electronic Sensors, Inc.",
+ 0x0B46: "Bird Rides, Inc.",
+ 0x0B47: "Gentex Corporation",
+ 0x0B48: "NIO USA, Inc.",
+ 0x0B49: "SkyHawke Technologies",
+ 0x0B4A: "Nomono AS",
+ 0x0B4B: "EMS Integrators, LLC",
+ 0x0B4C: "BiosBob.Biz",
+ 0x0B4D: "Adam Hall GmbH",
+ 0x0B4E: "ICP Systems B.V.",
+ 0x0B4F: "Breezi.io, Inc.",
+ 0x0B50: "Mesh Systems LLC",
+ 0x0B51: "FUN FACTORY GmbH",
+ 0x0B52: "ZIIP Inc",
+ 0x0B53: "SHENZHEN KAADAS INTELLIGENT TECHNOLOGY CO.,Ltd",
+ 0x0B54: "Emotion Fitness GmbH & Co. KG",
+ 0x0B55: "H G M Automotive Electronics, Inc.",
+ 0x0B56: "BORA - Vertriebs GmbH & Co KG",
+ 0x0B57: "CONVERTRONIX TECHNOLOGIES AND SERVICES LLP",
+ 0x0B58: "TOKAI-DENSHI INC",
+ 0x0B59: "Versa Group B.V.",
+ 0x0B5A: "H.P. Shelby Manufacturing, LLC.",
+ 0x0B5B: "Shenzhen ImagineVision Technology Limited",
+ 0x0B5C: "Exponential Power, Inc.",
+ 0x0B5D: "Fujian Newland Auto-ID Tech. Co., Ltd.",
+ 0x0B5E: "CELLCONTROL, INC.",
+ 0x0B5F: "Rivieh, Inc.",
+ 0x0B60: "RATOC Systems, Inc.",
+ 0x0B61: "Sentek Pty Ltd",
+ 0x0B62: "NOVEA ENERGIES",
+ 0x0B63: "Innolux Corporation",
+ 0x0B64: "NingBo klite Electric Manufacture Co.,LTD",
+ 0x0B65: "The Apache Software Foundation",
+ 0x0B66: "MITSUBISHI ELECTRIC AUTOMATION (THAILAND) COMPANY LIMITED",
+ 0x0B67: "CleanSpace Technology Pty Ltd",
+ 0x0B68: "Quha oy",
+ 0x0B69: "Addaday",
+ 0x0B6A: "Dymo",
+ 0x0B6B: "Samsara Networks, Inc",
+ 0x0B6C: "Sensitech, Inc.",
+ 0x0B6D: "SOLUM CO., LTD",
+ 0x0B6E: "React Mobile",
+ 0x0B6F: "Shenzhen Malide Technology Co.,Ltd",
+ 0x0B70: "JDRF Electromag Engineering Inc",
+ 0x0B71: "lilbit ODM AS",
+ 0x0B72: "Geeknet, Inc.",
+ 0x0B73: "HARADA INDUSTRY CO., LTD.",
+ 0x0B74: "BQN",
+ 0x0B75: "Triple W Japan Inc.",
+ 0x0B76: "MAX-co., ltd",
+ 0x0B77: "Aixlink(Chengdu) Co., Ltd.",
+ 0x0B78: "FIELD DESIGN INC.",
+ 0x0B79: "Sankyo Air Tech Co.,Ltd.",
+ 0x0B7A: "Shenzhen KTC Technology Co.,Ltd.",
+ 0x0B7B: "Hardcoder Oy",
+ 0x0B7C: "Scangrip A/S",
+ 0x0B7D: "FoundersLane GmbH",
+ 0x0B7E: "Offcode Oy",
+ 0x0B7F: "ICU tech GmbH",
+ 0x0B80: "AXELIFE",
+ 0x0B81: "SCM Group",
+ 0x0B82: "Mammut Sports Group AG",
+ 0x0B83: "Taiga Motors Inc.",
+ 0x0B84: "Presidio Medical, Inc.",
+ 0x0B85: "VIMANA TECH PTY LTD",
+ 0x0B86: "Trek Bicycle",
+ 0x0B87: "Ampetronic Ltd",
+ 0x0B88: "Muguang (Guangdong) Intelligent Lighting Technology Co., Ltd",
+ 0x0B89: "Rotronic AG",
+ 0x0B8A: "Seiko Instruments Inc.",
+ 0x0B8B: "American Technology Components, Incorporated",
+ 0x0B8C: "MOTREX",
+ 0x0B8D: "Pertech Industries Inc",
+ 0x0B8E: "Gentle Energy Corp.",
+ 0x0B8F: "Senscomm Semiconductor Co., Ltd.",
+ 0x0B90: "Ineos Automotive Limited",
+ 0x0B91: "Alfen ICU B.V.",
+ 0x0B92: "Citisend Solutions, SL",
+ 0x0B93: "Hangzhou BroadLink Technology Co., Ltd.",
+ 0x0B94: "Dreem SAS",
+ 0x0B95: "Netwake GmbH",
+ 0x0B96: "Telecom Design",
+ 0x0B97: "SILVER TREE LABS, INC.",
+ 0x0B98: "Gymstory B.V.",
+ 0x0B99: "The Goodyear Tire & Rubber Company",
+ 0x0B9A: "Beijing Wisepool Infinite Intelligence Technology Co.,Ltd",
+ 0x0B9B: "GISMAN",
+ 0x0B9C: "Komatsu Ltd.",
+ 0x0B9D: "Sensoria Holdings LTD",
+ 0x0B9E: "Audio Partnership Plc",
+ 0x0B9F: "Group Lotus Limited",
+ 0x0BA0: "Data Sciences International",
+ 0x0BA1: "Bunn-O-Matic Corporation",
+ 0x0BA2: "TireCheck GmbH",
+ 0x0BA3: "Sonova Consumer Hearing GmbH",
+ 0x0BA4: "Vervent Audio Group",
+ 0x0BA5: "SONICOS ENTERPRISES, LLC",
+ 0x0BA6: "Nissan Motor Co., Ltd.",
+ 0x0BA7: "hearX Group (Pty) Ltd",
+ 0x0BA8: "GLOWFORGE INC.",
+ 0x0BA9: "Allterco Robotics ltd",
+ 0x0BAA: "Infinitegra, Inc.",
+ 0x0BAB: "Grandex International Corporation",
+ 0x0BAC: "Machfu Inc.",
+ 0x0BAD: "Roambotics, Inc.",
+ 0x0BAE: "Soma Labs LLC",
+ 0x0BAF: "NITTO KOGYO CORPORATION",
+ 0x0BB0: "Ecolab Inc.",
+ 0x0BB1: "Beijing ranxin intelligence technology Co.,LTD",
+ 0x0BB2: "Fjorden Electra AS",
+ 0x0BB3: "Flender GmbH",
+ 0x0BB4: "New Cosmos USA, Inc.",
+ 0x0BB5: "Xirgo Technologies, LLC",
+ 0x0BB6: "Build With Robots Inc.",
+ 0x0BB7: "IONA Tech LLC",
+ 0x0BB8: "INNOVAG PTY. LTD.",
+ 0x0BB9: "SaluStim Group Oy",
+ 0x0BBA: "Huso, INC",
+ 0x0BBB: "SWISSINNO SOLUTIONS AG",
+ 0x0BBC: "T2REALITY SOLUTIONS PRIVATE LIMITED",
+ 0x0BBD: "ETHEORY PTY LTD",
+ 0x0BBE: "SAAB Aktiebolag",
+ 0x0BBF: "HIMSA II K/S",
+ 0x0BC0: "READY FOR SKY LLP",
+ 0x0BC1: "Miele & Cie. KG",
+ 0x0BC2: "EntWick Co.",
+ 0x0BC3: "MCOT INC.",
+ 0x0BC4: "TECHTICS ENGINEERING B.V.",
+ 0x0BC5: "Aperia Technologies, Inc.",
+ 0x0BC6: "TCL COMMUNICATION EQUIPMENT CO.,LTD.",
+ 0x0BC7: "Signtle Inc.",
+ 0x0BC8: "OTF Distribution, LLC",
+ 0x0BC9: "Neuvatek Inc.",
+ 0x0BCA: "Perimeter Technologies, Inc.",
+ 0x0BCB: "Divesoft s.r.o.",
+ 0x0BCC: "Sylvac sa",
+ 0x0BCD: "Amiko srl",
+ 0x0BCE: "Neurosity, Inc.",
+ 0x0BCF: "LL Tec Group LLC",
+ 0x0BD0: "Durag GmbH",
+ 0x0BD1: "Hubei Yuan Times Technology Co., Ltd.",
+ 0x0BD2: "IDEC",
+ 0x0BD3: "Procon Analytics, LLC",
+ 0x0BD4: "ndd Medizintechnik AG",
+ 0x0BD5: "Super B Lithium Power B.V.",
+ 0x0BD6: "Shenzhen Injoinic Technology Co., Ltd.",
+ 0x0BD7: "VINFAST TRADING AND PRODUCTION JOINT STOCK COMPANY",
+ 0x0BD8: "PURA SCENTS, INC.",
+ 0x0BD9: "Elics Basis Ltd.",
+ 0x0BDA: "Aardex Ltd.",
+ 0x0BDB: "CHAR-BROIL, LLC",
+ 0x0BDC: "Ledworks S.r.l.",
+ 0x0BDD: "Coroflo Limited",
+ 0x0BDE: "Yale",
+ 0x0BDF: "WINKEY ENTERPRISE (HONG KONG) LIMITED",
+ 0x0BE0: "Koizumi Lighting Technology corp.",
+ 0x0BE1: "Back40 Precision",
+ 0x0BE2: "OTC engineering",
+ 0x0BE3: "Comtel Systems Ltd.",
+ 0x0BE4: "Deepfield Connect GmbH",
+ 0x0BE5: "ZWILLING J.A. Henckels Aktiengesellschaft",
+ 0x0BE6: "Puratap Pty Ltd",
+ 0x0BE7: "Fresnel Technologies, Inc.",
+ 0x0BE8: "Sensormate AG",
+ 0x0BE9: "Shindengen Electric Manufacturing Co., Ltd.",
+ 0x0BEA: "Twenty Five Seven, prodaja in storitve, d.o.o.",
+ 0x0BEB: "Luna Health, Inc.",
+ 0x0BEC: "Miracle-Ear, Inc.",
+ 0x0BED: "CORAL-TAIYI Co. Ltd.",
+ 0x0BEE: "LINKSYS USA, INC.",
+ 0x0BEF: "Safetytest GmbH",
+ 0x0BF0: "KIDO SPORTS CO., LTD.",
+ 0x0BF1: "Site IQ LLC",
+ 0x0BF2: "Angel Medical Systems, Inc.",
+ 0x0BF3: "PONE BIOMETRICS AS",
+ 0x0BF4: "ER Lab LLC",
+ 0x0BF5: "T5 tek, Inc.",
+ 0x0BF6: "greenTEG AG",
+ 0x0BF7: "Wacker Neuson SE",
+ 0x0BF8: "Innovacionnye Resheniya",
+ 0x0BF9: "Alio, Inc",
+ 0x0BFA: "CleanBands Systems Ltd.",
+ 0x0BFB: "Dodam Enersys Co., Ltd",
+ 0x0BFC: "T+A elektroakustik GmbH & Co.KG",
+ 0x0BFD: "Esmé Solutions",
+ 0x0BFE: "Media-Cartec GmbH",
+ 0x0BFF: "Ratio Electric BV",
+ 0x0C00: "MQA Limited",
+ 0x0C01: "NEOWRK SISTEMAS INTELIGENTES S.A.",
+ 0x0C02: "Loomanet, Inc.",
+ 0x0C03: "Puff Corp",
+ 0x0C04: "Happy Health, Inc.",
+ 0x0C05: "Montage Connect, Inc.",
+ 0x0C06: "LED Smart Inc.",
+ 0x0C07: "CONSTRUKTS, INC.",
+ 0x0C08: "limited liability company \"Red\"",
+ 0x0C09: "Senic Inc.",
+ 0x0C0A: "Automated Pet Care Products, LLC",
+ 0x0C0B: "aconno GmbH",
+ 0x0C0C: "Mendeltron, Inc.",
+ 0x0C0D: "Mereltron bv",
+ 0x0C0E: "ALEX DENKO CO.,LTD.",
+ 0x0C0F: "AETERLINK",
+ 0x0C10: "Cosmed s.r.l.",
+ 0x0C11: "Gordon Murray Design Limited",
+ 0x0C12: "IoSA",
+ 0x0C13: "Scandinavian Health Limited",
+ 0x0C14: "Fasetto, Inc.",
+ 0x0C15: "Geva Sol B.V.",
+ 0x0C16: "TYKEE PTY. LTD.",
+ 0x0C17: "SomnoMed Limited",
+ 0x0C18: "CORROHM",
+ 0x0C19: "Arlo Technologies, Inc.",
+ 0x0C1A: "Catapult Group International Ltd",
+ 0x0C1B: "Rockchip Electronics Co., Ltd.",
+ 0x0C1C: "GEMU",
+ 0x0C1D: "OFF Line Japan Co., Ltd.",
+ 0x0C1E: "EC sense co., Ltd",
+ 0x0C1F: "LVI Co.",
+ 0x0C20: "COMELIT GROUP S.P.A.",
+ 0x0C21: "Foshan Viomi Electrical Technology Co., Ltd",
+ 0x0C22: "Glamo Inc.",
+ 0x0C23: "KEYTEC,Inc.",
+ 0x0C24: "SMARTD TECHNOLOGIES INC.",
+ 0x0C25: "JURA Elektroapparate AG",
+ 0x0C26: "Performance Electronics, Ltd.",
+ 0x0C27: "Pal Electronics",
+ 0x0C28: "Embecta Corp.",
+ 0x0C29: "DENSO AIRCOOL CORPORATION",
+ 0x0C2A: "Caresix Inc.",
+ 0x0C2B: "GigaDevice Semiconductor Inc.",
+ 0x0C2C: "Zeku Technology (Shanghai) Corp., Ltd.",
+ 0x0C2D: "OTF Product Sourcing, LLC",
+ 0x0C2E: "Easee AS",
+ 0x0C2F: "BEEHERO, INC.",
+ 0x0C30: "McIntosh Group Inc",
+ 0x0C31: "KINDOO LLP",
+ 0x0C32: "Xian Yisuobao Electronic Technology Co., Ltd.",
+ 0x0C33: "Exeger Operations AB",
+ 0x0C34: "BYD Company Limited",
+ 0x0C35: "Thermokon-Sensortechnik GmbH",
+ 0x0C36: "Cosmicnode BV",
+ 0x0C37: "SignalQuest, LLC",
+ 0x0C38: "Noritz Corporation.",
+ 0x0C39: "TIGER CORPORATION",
+ 0x0C3A: "Equinosis, LLC",
+ 0x0C3B: "ORB Innovations Ltd",
+ 0x0C3C: "Classified Cycling",
+ 0x0C3D: "Wrmth Corp.",
+ 0x0C3E: "BELLDESIGN Inc.",
+ 0x0C3F: "Stinger Equipment, Inc.",
+ 0x0C40: "HORIBA, Ltd.",
+ 0x0C41: "Control Solutions LLC",
+ 0x0C42: "Heath Consultants Inc.",
+ 0x0C43: "Berlinger & Co. AG",
+ 0x0C44: "ONCELABS LLC",
+ 0x0C45: "Brose Verwaltung SE, Bamberg",
+ 0x0C46: "Granwin IoT Technology (Guangzhou) Co.,Ltd",
+ 0x0C47: "Epsilon Electronics,lnc",
+ 0x0C48: "VALEO MANAGEMENT SERVICES",
+ 0x0C49: "twopounds gmbh",
+ 0x0C4A: "atSpiro ApS",
+ 0x0C4B: "ADTRAN, Inc.",
+ 0x0C4C: "Orpyx Medical Technologies Inc.",
+ 0x0C4D: "Seekwave Technology Co.,ltd.",
+ 0x0C4E: "Tactile Engineering, Inc.",
+ 0x0C4F: "SharkNinja Operating LLC",
+ 0x0C50: "Imostar Technologies Inc.",
+ 0x0C51: "INNOVA S.R.L.",
+ 0x0C52: "ESCEA LIMITED",
+ 0x0C53: "Taco, Inc.",
+ 0x0C54: "HiViz Lighting, Inc.",
+ 0x0C55: "Zintouch B.V.",
+ 0x0C56: "Rheem Sales Company, Inc.",
+ 0x0C57: "UNEEG medical A/S",
+ 0x0C58: "Hykso Inc.",
+ 0x0C59: "CYBERDYNE Inc.",
+ 0x0C5A: "Lockswitch Sdn Bhd",
+ 0x0C5B: "Alban Giacomo S.P.A.",
+ 0x0C5C: "MGM WIRELESSS HOLDINGS PTY LTD",
+ 0x0C5D: "StepUp Solutions ApS",
+ 0x0C5E: "BlueID GmbH",
+ 0x0C5F: "Nanjing Linkpower Microelectronics Co.,Ltd",
+ 0x0C60: "KEBA Energy Automation GmbH",
+ 0x0C61: "NNOXX, Inc",
+ 0x0C62: "Phiaton Corporation",
+ 0x0C63: "phg Peter Hengstler GmbH + Co. KG",
+ 0x0C64: "dormakaba Holding AG",
+ 0x0C65: "WAKO CO,.LTD",
+ 0x0C66: "DEN Smart Home B.V.",
+ 0x0C67: "TRACKTING S.R.L.",
+ 0x0C68: "Emerja Corporation",
+ 0x0C69: "BLITZ electric motors. LTD",
+ 0x0C6A: "CONSORCIO TRUST CONTROL - NETTEL",
+ 0x0C6B: "GILSON SAS",
+ 0x0C6C: "SNIFF LOGIC LTD",
+ 0x0C6D: "Fidure Corp.",
+ 0x0C6E: "Sensa LLC",
+ 0x0C6F: "Parakey AB",
+ 0x0C70: "SCARAB SOLUTIONS LTD",
+ 0x0C71: "BitGreen Technolabz (OPC) Private Limited",
+ 0x0C72: "StreetCar ORV, LLC",
+ 0x0C73: "Truma Gerätetechnik GmbH & Co. KG",
+ 0x0C74: "yupiteru",
+ 0x0C75: "Embedded Engineering Solutions LLC",
+ 0x0C76: "Shenzhen Gwell Times Technology Co. , Ltd",
+ 0x0C77: "TEAC Corporation",
+ 0x0C78: "CHARGTRON IOT PRIVATE LIMITED",
+ 0x0C79: "Zhuhai Smartlink Technology Co., Ltd",
+ 0x0C7A: "Triductor Technology (Suzhou), Inc.",
+ 0x0C7B: "PT SADAMAYA GRAHA TEKNOLOGI",
+ 0x0C7C: "Mopeka Products LLC",
+ 0x0C7D: "3ALogics, Inc.",
+ 0x0C7E: "BOOMING OF THINGS",
+ 0x0C7F: "Rochester Sensors, LLC",
+ 0x0C80: "CARDIOID - TECHNOLOGIES, LDA",
+ 0x0C81: "Carrier Corporation",
+ 0x0C82: "NACON",
+ 0x0C83: "Watchdog Systems LLC",
+ 0x0C84: "MAXON INDUSTRIES, INC.",
+ 0x0C85: "Amlogic, Inc.",
+ 0x0C86: "Qingdao Eastsoft Communication Technology Co.,Ltd",
+ 0x0C87: "Weltek Technologies Company Limited",
+ 0x0C88: "Nextivity Inc.",
+ 0x0C89: "AGZZX OPTOELECTRONICS TECHNOLOGY CO., LTD",
+ 0x0C8A: "ARTISTIC&CO.GLOBAL Ltd.",
+ 0x0C8B: "Heavys Inc",
+ 0x0C8C: "T-Mobile USA",
+ 0x0C8D: "tonies GmbH",
+ 0x0C8E: "Technocon Engineering Ltd.",
+ 0x0C8F: "Radar Automobile Sales(Shandong)Co.,Ltd.",
+ 0x0C90: "WESCO AG",
+ 0x0C91: "Yashu Systems",
+ 0x0C92: "Kesseböhmer Ergonomietechnik GmbH",
+ 0x0C93: "Movesense Oy",
+ 0x0C94: "Baxter Healthcare Corporation",
+ 0x0C95: "Gemstone Lights Canada Ltd.",
+ 0x0C96: "H+B Hightech GmbH",
+ 0x0C97: "Deako",
+ 0x0C98: "MiX Telematics International (PTY) LTD",
+ 0x0C99: "Vire Health Oy",
+ 0x0C9A: "ALF Inc.",
+ 0x0C9B: "NTT sonority, Inc.",
+ 0x0C9C: "Sunstone-RTLS Ipari Szolgaltato Korlatolt Felelossegu Tarsasag",
+ 0x0C9D: "Ribbiot, INC.",
+ 0x0C9E: "ECCEL CORPORATION SAS",
+ 0x0C9F: "Dragonfly Energy Corp.",
+ 0x0CA0: "BIGBEN",
+ 0x0CA1: "YAMAHA MOTOR CO.,LTD.",
+ 0x0CA2: "XSENSE LTD",
+ 0x0CA3: "MAQUET GmbH",
+ 0x0CA4: "MITSUBISHI ELECTRIC LIGHTING CO, LTD",
+ 0x0CA5: "Princess Cruise Lines, Ltd.",
+ 0x0CA6: "Megger Ltd",
+ 0x0CA7: "Verve InfoTec Pty Ltd",
+ 0x0CA8: "Sonas, Inc.",
+ 0x0CA9: "Mievo Technologies Private Limited",
+ 0x0CAA: "Shenzhen Poseidon Network Technology Co., Ltd",
+ 0x0CAB: "HERUTU ELECTRONICS CORPORATION",
+ 0x0CAC: "Shenzhen Shokz Co.,Ltd.",
+ 0x0CAD: "Shenzhen Openhearing Tech CO., LTD .",
+ 0x0CAE: "Evident Corporation",
+ 0x0CAF: "NEURINNOV",
+ 0x0CB0: "SwipeSense, Inc.",
+ 0x0CB1: "RF Creations",
+ 0x0CB2: "SHINKAWA Sensor Technology, Inc.",
+ 0x0CB3: "janova GmbH",
+ 0x0CB4: "Eberspaecher Climate Control Systems GmbH",
+ 0x0CB5: "Racketry, d. o. o.",
+ 0x0CB6: "THE EELECTRIC MACARON LLC",
+ 0x0CB7: "Cucumber Lighting Controls Limited",
+ 0x0CB8: "Shanghai Proxy Network Technology Co., Ltd.",
+ 0x0CB9: "seca GmbH & Co. KG",
+ 0x0CBA: "Ameso Tech (OPC) Private Limited",
+ 0x0CBB: "Emlid Tech Kft.",
+ 0x0CBC: "TROX GmbH",
+ 0x0CBD: "Pricer AB",
+ 0x0CBF: "Forward Thinking Systems LLC.",
+ 0x0CC0: "Garnet Instruments Ltd.",
+ 0x0CC1: "CLEIO Inc.",
+ 0x0CC2: "Anker Innovations Limited",
+ 0x0CC3: "HMD Global Oy",
+ 0x0CC4: "ABUS August Bremicker Soehne Kommanditgesellschaft",
+ 0x0CC5: "Open Road Solutions, Inc.",
+ 0x0CC6: "Serial Technology Corporation",
+ 0x0CC7: "SB C&S Corp.",
+ 0x0CC8: "TrikThom",
+ 0x0CC9: "Innocent Technology Co., Ltd.",
+ 0x0CCA: "Cyclops Marine Ltd",
+ 0x0CCB: "NOTHING TECHNOLOGY LIMITED",
+ 0x0CCC: "Kord Defence Pty Ltd",
+ 0x0CCD: "YanFeng Visteon(Chongqing) Automotive Electronic Co.,Ltd",
+ 0x0CCE: "SENOSPACE LLC",
+ 0x0CCF: "Shenzhen CESI Information Technology Co., Ltd.",
+ 0x0CD0: "MooreSilicon Semiconductor Technology (Shanghai) Co., LTD.",
+ 0x0CD1: "Imagine Marketing Limited",
+ 0x0CD2: "EQOM SSC B.V.",
+ 0x0CD3: "TechSwipe",
+ 0x0CD4: "Shenzhen Zhiduotun IoT Technology Co., Ltd",
+ 0x0CD5: "Numa Products, LLC",
+ 0x0CD6: "HHO (Hangzhou) Digital Technology Co., Ltd.",
+ 0x0CD7: "Maztech Industries, LLC",
+ 0x0CD8: "SIA Mesh Group",
+ 0x0CD9: "Minami acoustics Limited",
+ 0x0CDA: "Wolf Steel ltd",
+ 0x0CDB: "Circus World Displays Limited",
+ 0x0CDC: "Ypsomed AG",
+ 0x0CDD: "Alif Semiconductor, Inc.",
+ 0x0CDE: "RESPONSE TECHNOLOGIES, LTD.",
+ 0x0CDF: "SHENZHEN CHENYUN ELECTRONICS CO., LTD",
+ 0x0CE0: "VODALOGIC PTY LTD",
+ 0x0CE1: "Regal Beloit America, Inc.",
+ 0x0CE2: "CORVENT MEDICAL, INC.",
+ 0x0CE3: "Taiwan Fuhsing",
+ 0x0CE4: "Off-Highway Powertrain Services Germany GmbH",
+ 0x0CE5: "Amina Distribution AS",
+ 0x0CE6: "McWong International, Inc.",
+ 0x0CE7: "TAG HEUER SA",
+ 0x0CE8: "Dongguan Yougo Electronics Co.,Ltd.",
+ 0x0CE9: "PEAG, LLC dba JLab Audio",
+ 0x0CEA: "HAYWARD INDUSTRIES, INC.",
+ 0x0CEB: "Shenzhen Tingting Technology Co. LTD",
+ 0x0CEC: "Pacific Coast Fishery Services (2003) Inc.",
+ 0x0CED: "CV. NURI TEKNIK",
+ 0x0CEE: "MadgeTech, Inc",
+ 0x0CEF: "POGS B.V.",
+ 0x0CF0: "THOTAKA TEKHNOLOGIES INDIA PRIVATE LIMITED",
+ 0x0CF1: "Midmark",
+ 0x0CF2: "BestSens AG",
+ 0x0CF3: "Radio Sound",
+ 0x0CF4: "SOLUX PTY LTD",
+ 0x0CF5: "BOS Balance of Storage Systems AG",
+ 0x0CF6: "OJ Electronics A/S",
+ 0x0CF7: "TVS Motor Company Ltd.",
+ 0x0CF8: "core sensing GmbH",
+ 0x0CF9: "Tamblue Oy",
+ 0x0CFA: "Protect Animals With Satellites LLC",
+ 0x0CFB: "Tyromotion GmbH",
+ 0x0CFC: "ElectronX design",
+ 0x0CFD: "Wuhan Woncan Construction Technologies Co., Ltd.",
+ 0x0CFE: "Thule Group AB",
+ 0x0CFF: "Ergodriven Inc",
}
diff --git a/bumble/device.py b/bumble/device.py
index b01dc58..7f11012 100644
--- a/bumble/device.py
+++ b/bumble/device.py
@@ -33,6 +33,8 @@
Tuple,
Type,
Union,
+ cast,
+ overload,
TYPE_CHECKING,
)
@@ -151,6 +153,7 @@
CompositeEventEmitter,
setup_event_forwarding,
composite_listener,
+ deprecated,
)
from .keys import (
KeyStore,
@@ -670,9 +673,7 @@
def send_l2cap_pdu(self, cid: int, pdu: bytes) -> None:
self.device.send_l2cap_pdu(self.handle, cid, pdu)
- def create_l2cap_connector(self, psm):
- return self.device.create_l2cap_connector(self, psm)
-
+ @deprecated("Please use create_l2cap_channel()")
async def open_l2cap_channel(
self,
psm,
@@ -682,6 +683,23 @@
):
return await self.device.open_l2cap_channel(self, psm, max_credits, mtu, mps)
+ @overload
+ async def create_l2cap_channel(
+ self, spec: l2cap.ClassicChannelSpec
+ ) -> l2cap.ClassicChannel:
+ ...
+
+ @overload
+ async def create_l2cap_channel(
+ self, spec: l2cap.LeCreditBasedChannelSpec
+ ) -> l2cap.LeCreditBasedChannel:
+ ...
+
+ async def create_l2cap_channel(
+ self, spec: Union[l2cap.ClassicChannelSpec, l2cap.LeCreditBasedChannelSpec]
+ ) -> Union[l2cap.ClassicChannel, l2cap.LeCreditBasedChannel]:
+ return await self.device.create_l2cap_channel(connection=self, spec=spec)
+
async def disconnect(
self, reason: int = HCI_REMOTE_USER_TERMINATED_CONNECTION_ERROR
) -> None:
@@ -829,6 +847,9 @@
self.connectable = config.get('connectable', self.connectable)
self.discoverable = config.get('discoverable', self.discoverable)
self.gatt_services = config.get('gatt_services', self.gatt_services)
+ self.address_resolution_offload = config.get(
+ 'address_resolution_offload', self.address_resolution_offload
+ )
# Load or synthesize an IRK
irk = config.get('irk')
@@ -1180,15 +1201,11 @@
return None
- def create_l2cap_connector(self, connection, psm):
- return lambda: self.l2cap_channel_manager.connect(connection, psm)
-
- def create_l2cap_registrar(self, psm):
- return lambda handler: self.register_l2cap_server(psm, handler)
-
+ @deprecated("Please use create_l2cap_server()")
def register_l2cap_server(self, psm, server) -> int:
return self.l2cap_channel_manager.register_server(psm, server)
+ @deprecated("Please use create_l2cap_server()")
def register_l2cap_channel_server(
self,
psm,
@@ -1201,6 +1218,7 @@
psm, server, max_credits, mtu, mps
)
+ @deprecated("Please use create_l2cap_channel()")
async def open_l2cap_channel(
self,
connection,
@@ -1213,6 +1231,74 @@
connection, psm, max_credits, mtu, mps
)
+ @overload
+ async def create_l2cap_channel(
+ self,
+ connection: Connection,
+ spec: l2cap.ClassicChannelSpec,
+ ) -> l2cap.ClassicChannel:
+ ...
+
+ @overload
+ async def create_l2cap_channel(
+ self,
+ connection: Connection,
+ spec: l2cap.LeCreditBasedChannelSpec,
+ ) -> l2cap.LeCreditBasedChannel:
+ ...
+
+ async def create_l2cap_channel(
+ self,
+ connection: Connection,
+ spec: Union[l2cap.ClassicChannelSpec, l2cap.LeCreditBasedChannelSpec],
+ ) -> Union[l2cap.ClassicChannel, l2cap.LeCreditBasedChannel]:
+ if isinstance(spec, l2cap.ClassicChannelSpec):
+ return await self.l2cap_channel_manager.create_classic_channel(
+ connection=connection, spec=spec
+ )
+ if isinstance(spec, l2cap.LeCreditBasedChannelSpec):
+ return await self.l2cap_channel_manager.create_le_credit_based_channel(
+ connection=connection, spec=spec
+ )
+
+ @overload
+ def create_l2cap_server(
+ self,
+ spec: l2cap.ClassicChannelSpec,
+ handler: Optional[Callable[[l2cap.ClassicChannel], Any]] = None,
+ ) -> l2cap.ClassicChannelServer:
+ ...
+
+ @overload
+ def create_l2cap_server(
+ self,
+ spec: l2cap.LeCreditBasedChannelSpec,
+ handler: Optional[Callable[[l2cap.LeCreditBasedChannel], Any]] = None,
+ ) -> l2cap.LeCreditBasedChannelServer:
+ ...
+
+ def create_l2cap_server(
+ self,
+ spec: Union[l2cap.ClassicChannelSpec, l2cap.LeCreditBasedChannelSpec],
+ handler: Union[
+ Callable[[l2cap.ClassicChannel], Any],
+ Callable[[l2cap.LeCreditBasedChannel], Any],
+ None,
+ ] = None,
+ ) -> Union[l2cap.ClassicChannelServer, l2cap.LeCreditBasedChannelServer]:
+ if isinstance(spec, l2cap.ClassicChannelSpec):
+ return self.l2cap_channel_manager.create_classic_server(
+ spec=spec,
+ handler=cast(Callable[[l2cap.ClassicChannel], Any], handler),
+ )
+ elif isinstance(spec, l2cap.LeCreditBasedChannelSpec):
+ return self.l2cap_channel_manager.create_le_credit_based_server(
+ handler=cast(Callable[[l2cap.LeCreditBasedChannel], Any], handler),
+ spec=spec,
+ )
+ else:
+ raise ValueError(f'Unexpected mode {spec}')
+
def send_l2cap_pdu(self, connection_handle: int, cid: int, pdu: bytes) -> None:
self.host.send_l2cap_pdu(connection_handle, cid, pdu)
@@ -1425,10 +1511,10 @@
check_result=True,
)
- self.advertising_own_address_type = own_address_type
- self.auto_restart_advertising = auto_restart
self.advertising_type = advertising_type
+ self.advertising_own_address_type = own_address_type
self.advertising = True
+ self.auto_restart_advertising = auto_restart
async def stop_advertising(self) -> None:
# Disable advertising
@@ -1438,9 +1524,9 @@
check_result=True,
)
+ self.advertising_type = None
self.advertising_own_address_type = None
self.advertising = False
- self.advertising_type = None
self.auto_restart_advertising = False
@property
@@ -2630,7 +2716,6 @@
own_address_type = self.advertising_own_address_type
# We are no longer advertising
- self.advertising_own_address_type = None
self.advertising = False
if own_address_type in (
@@ -2687,7 +2772,6 @@
and self.advertising
and self.advertising_type.is_directed
):
- self.advertising_own_address_type = None
self.advertising = False
# Notify listeners
diff --git a/bumble/hci.py b/bumble/hci.py
index 41deed2..057c04a 100644
--- a/bumble/hci.py
+++ b/bumble/hci.py
@@ -121,6 +121,7 @@
HCI_VERSION_BLUETOOTH_CORE_5_1 = 10
HCI_VERSION_BLUETOOTH_CORE_5_2 = 11
HCI_VERSION_BLUETOOTH_CORE_5_3 = 12
+HCI_VERSION_BLUETOOTH_CORE_5_4 = 13
HCI_VERSION_NAMES = {
HCI_VERSION_BLUETOOTH_CORE_1_0B: 'HCI_VERSION_BLUETOOTH_CORE_1_0B',
@@ -135,7 +136,8 @@
HCI_VERSION_BLUETOOTH_CORE_5_0: 'HCI_VERSION_BLUETOOTH_CORE_5_0',
HCI_VERSION_BLUETOOTH_CORE_5_1: 'HCI_VERSION_BLUETOOTH_CORE_5_1',
HCI_VERSION_BLUETOOTH_CORE_5_2: 'HCI_VERSION_BLUETOOTH_CORE_5_2',
- HCI_VERSION_BLUETOOTH_CORE_5_3: 'HCI_VERSION_BLUETOOTH_CORE_5_3'
+ HCI_VERSION_BLUETOOTH_CORE_5_3: 'HCI_VERSION_BLUETOOTH_CORE_5_3',
+ HCI_VERSION_BLUETOOTH_CORE_5_4: 'HCI_VERSION_BLUETOOTH_CORE_5_4',
}
# LMP Version
diff --git a/bumble/hid.py b/bumble/hid.py
new file mode 100644
index 0000000..e4d6a77
--- /dev/null
+++ b/bumble/hid.py
@@ -0,0 +1,332 @@
+# Copyright 2021-2022 Google LLC
+#
+# 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
+#
+# https://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.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+from __future__ import annotations
+from dataclasses import dataclass
+import logging
+import asyncio
+import enum
+
+from pyee import EventEmitter
+from typing import Optional, Tuple, Callable, Dict, Union, TYPE_CHECKING
+
+from . import core, l2cap # type: ignore
+from .colors import color # type: ignore
+from .core import BT_BR_EDR_TRANSPORT, InvalidStateError, ProtocolError # type: ignore
+
+if TYPE_CHECKING:
+ from bumble.device import Device, Connection
+
+
+# -----------------------------------------------------------------------------
+# Logging
+# -----------------------------------------------------------------------------
+logger = logging.getLogger(__name__)
+
+
+# -----------------------------------------------------------------------------
+# Constants
+# -----------------------------------------------------------------------------
+# fmt: on
+HID_CONTROL_PSM = 0x0011
+HID_INTERRUPT_PSM = 0x0013
+
+
+class Message:
+ message_type: MessageType
+ # Report types
+ class ReportType(enum.IntEnum):
+ OTHER_REPORT = 0x00
+ INPUT_REPORT = 0x01
+ OUTPUT_REPORT = 0x02
+ FEATURE_REPORT = 0x03
+
+ # Handshake parameters
+ class Handshake(enum.IntEnum):
+ SUCCESSFUL = 0x00
+ NOT_READY = 0x01
+ ERR_INVALID_REPORT_ID = 0x02
+ ERR_UNSUPPORTED_REQUEST = 0x03
+ ERR_UNKNOWN = 0x0E
+ ERR_FATAL = 0x0F
+
+ # Message Type
+ class MessageType(enum.IntEnum):
+ HANDSHAKE = 0x00
+ CONTROL = 0x01
+ GET_REPORT = 0x04
+ SET_REPORT = 0x05
+ GET_PROTOCOL = 0x06
+ SET_PROTOCOL = 0x07
+ DATA = 0x0A
+
+ # Protocol modes
+ class ProtocolMode(enum.IntEnum):
+ BOOT_PROTOCOL = 0x00
+ REPORT_PROTOCOL = 0x01
+
+ # Control Operations
+ class ControlCommand(enum.IntEnum):
+ SUSPEND = 0x03
+ EXIT_SUSPEND = 0x04
+ VIRTUAL_CABLE_UNPLUG = 0x05
+
+ # Class Method to derive header
+ @classmethod
+ def header(cls, lower_bits: int = 0x00) -> bytes:
+ return bytes([(cls.message_type << 4) | lower_bits])
+
+
+# HIDP messages
+@dataclass
+class GetReportMessage(Message):
+ report_type: int
+ report_id: int
+ buffer_size: int
+ message_type = Message.MessageType.GET_REPORT
+
+ def __bytes__(self) -> bytes:
+ packet_bytes = bytearray()
+ packet_bytes.append(self.report_id)
+ packet_bytes.extend(
+ [(self.buffer_size & 0xFF), ((self.buffer_size >> 8) & 0xFF)]
+ )
+ if self.report_type == Message.ReportType.OTHER_REPORT:
+ return self.header(self.report_type) + packet_bytes
+ else:
+ return self.header(0x08 | self.report_type) + packet_bytes
+
+
+@dataclass
+class SetReportMessage(Message):
+ report_type: int
+ data: bytes
+ message_type = Message.MessageType.SET_REPORT
+
+ def __bytes__(self) -> bytes:
+ return self.header(self.report_type) + self.data
+
+
+@dataclass
+class GetProtocolMessage(Message):
+ message_type = Message.MessageType.GET_PROTOCOL
+
+ def __bytes__(self) -> bytes:
+ return self.header()
+
+
+@dataclass
+class SetProtocolMessage(Message):
+ protocol_mode: int
+ message_type = Message.MessageType.SET_PROTOCOL
+
+ def __bytes__(self) -> bytes:
+ return self.header(self.protocol_mode)
+
+
+@dataclass
+class Suspend(Message):
+ message_type = Message.MessageType.CONTROL
+
+ def __bytes__(self) -> bytes:
+ return self.header(Message.ControlCommand.SUSPEND)
+
+
+@dataclass
+class ExitSuspend(Message):
+ message_type = Message.MessageType.CONTROL
+
+ def __bytes__(self) -> bytes:
+ return self.header(Message.ControlCommand.EXIT_SUSPEND)
+
+
+@dataclass
+class VirtualCableUnplug(Message):
+ message_type = Message.MessageType.CONTROL
+
+ def __bytes__(self) -> bytes:
+ return self.header(Message.ControlCommand.VIRTUAL_CABLE_UNPLUG)
+
+
+@dataclass
+class SendData(Message):
+ data: bytes
+ message_type = Message.MessageType.DATA
+
+ def __bytes__(self) -> bytes:
+ return self.header(Message.ReportType.OUTPUT_REPORT) + self.data
+
+
+# -----------------------------------------------------------------------------
+class Host(EventEmitter):
+ l2cap_ctrl_channel: Optional[l2cap.ClassicChannel]
+ l2cap_intr_channel: Optional[l2cap.ClassicChannel]
+
+ def __init__(self, device: Device, connection: Connection) -> None:
+ super().__init__()
+ self.device = device
+ self.connection = connection
+
+ self.l2cap_ctrl_channel = None
+ self.l2cap_intr_channel = None
+
+ # Register ourselves with the L2CAP channel manager
+ device.register_l2cap_server(HID_CONTROL_PSM, self.on_connection)
+ device.register_l2cap_server(HID_INTERRUPT_PSM, self.on_connection)
+
+ async def connect_control_channel(self) -> None:
+ # Create a new L2CAP connection - control channel
+ try:
+ self.l2cap_ctrl_channel = await self.device.l2cap_channel_manager.connect(
+ self.connection, HID_CONTROL_PSM
+ )
+ except ProtocolError:
+ logging.exception(f'L2CAP connection failed.')
+ raise
+
+ assert self.l2cap_ctrl_channel is not None
+ # Become a sink for the L2CAP channel
+ self.l2cap_ctrl_channel.sink = self.on_ctrl_pdu
+
+ async def connect_interrupt_channel(self) -> None:
+ # Create a new L2CAP connection - interrupt channel
+ try:
+ self.l2cap_intr_channel = await self.device.l2cap_channel_manager.connect(
+ self.connection, HID_INTERRUPT_PSM
+ )
+ except ProtocolError:
+ logging.exception(f'L2CAP connection failed.')
+ raise
+
+ assert self.l2cap_intr_channel is not None
+ # Become a sink for the L2CAP channel
+ self.l2cap_intr_channel.sink = self.on_intr_pdu
+
+ async def disconnect_interrupt_channel(self) -> None:
+ if self.l2cap_intr_channel is None:
+ raise InvalidStateError('invalid state')
+ channel = self.l2cap_intr_channel
+ self.l2cap_intr_channel = None
+ await channel.disconnect()
+
+ async def disconnect_control_channel(self) -> None:
+ if self.l2cap_ctrl_channel is None:
+ raise InvalidStateError('invalid state')
+ channel = self.l2cap_ctrl_channel
+ self.l2cap_ctrl_channel = None
+ await channel.disconnect()
+
+ def on_connection(self, l2cap_channel: l2cap.ClassicChannel) -> None:
+ logger.debug(f'+++ New L2CAP connection: {l2cap_channel}')
+ l2cap_channel.on('open', lambda: self.on_l2cap_channel_open(l2cap_channel))
+
+ def on_l2cap_channel_open(self, l2cap_channel: l2cap.ClassicChannel) -> None:
+ if l2cap_channel.psm == HID_CONTROL_PSM:
+ self.l2cap_ctrl_channel = l2cap_channel
+ self.l2cap_ctrl_channel.sink = self.on_ctrl_pdu
+ else:
+ self.l2cap_intr_channel = l2cap_channel
+ self.l2cap_intr_channel.sink = self.on_intr_pdu
+ logger.debug(f'$$$ L2CAP channel open: {l2cap_channel}')
+
+ def on_ctrl_pdu(self, pdu: bytes) -> None:
+ logger.debug(f'<<< HID CONTROL PDU: {pdu.hex()}')
+ # Here we will receive all kinds of packets, parse and then call respective callbacks
+ message_type = pdu[0] >> 4
+ param = pdu[0] & 0x0F
+
+ if message_type == Message.MessageType.HANDSHAKE:
+ logger.debug(f'<<< HID HANDSHAKE: {Message.Handshake(param).name}')
+ self.emit('handshake', Message.Handshake(param))
+ elif message_type == Message.MessageType.DATA:
+ logger.debug('<<< HID CONTROL DATA')
+ self.emit('data', pdu)
+ elif message_type == Message.MessageType.CONTROL:
+ if param == Message.ControlCommand.SUSPEND:
+ logger.debug('<<< HID SUSPEND')
+ self.emit('suspend', pdu)
+ elif param == Message.ControlCommand.EXIT_SUSPEND:
+ logger.debug('<<< HID EXIT SUSPEND')
+ self.emit('exit_suspend', pdu)
+ elif param == Message.ControlCommand.VIRTUAL_CABLE_UNPLUG:
+ logger.debug('<<< HID VIRTUAL CABLE UNPLUG')
+ self.emit('virtual_cable_unplug')
+ else:
+ logger.debug('<<< HID CONTROL OPERATION UNSUPPORTED')
+ else:
+ logger.debug('<<< HID CONTROL DATA')
+ self.emit('data', pdu)
+
+ def on_intr_pdu(self, pdu: bytes) -> None:
+ logger.debug(f'<<< HID INTERRUPT PDU: {pdu.hex()}')
+ self.emit("data", pdu)
+
+ def get_report(self, report_type: int, report_id: int, buffer_size: int) -> None:
+ msg = GetReportMessage(
+ report_type=report_type, report_id=report_id, buffer_size=buffer_size
+ )
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID CONTROL GET REPORT, PDU: {hid_message.hex()}')
+ self.send_pdu_on_ctrl(hid_message)
+
+ def set_report(self, report_type: int, data: bytes):
+ msg = SetReportMessage(report_type=report_type, data=data)
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID CONTROL SET REPORT, PDU:{hid_message.hex()}')
+ self.send_pdu_on_ctrl(hid_message)
+
+ def get_protocol(self):
+ msg = GetProtocolMessage()
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID CONTROL GET PROTOCOL, PDU: {hid_message.hex()}')
+ self.send_pdu_on_ctrl(hid_message)
+
+ def set_protocol(self, protocol_mode: int):
+ msg = SetProtocolMessage(protocol_mode=protocol_mode)
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID CONTROL SET PROTOCOL, PDU: {hid_message.hex()}')
+ self.send_pdu_on_ctrl(hid_message)
+
+ def send_pdu_on_ctrl(self, msg: bytes) -> None:
+ self.l2cap_ctrl_channel.send_pdu(msg) # type: ignore
+
+ def send_pdu_on_intr(self, msg: bytes) -> None:
+ self.l2cap_intr_channel.send_pdu(msg) # type: ignore
+
+ def send_data(self, data):
+ msg = SendData(data)
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID INTERRUPT SEND DATA, PDU: {hid_message.hex()}')
+ self.send_pdu_on_intr(hid_message)
+
+ def suspend(self):
+ msg = Suspend()
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID CONTROL SUSPEND, PDU:{hid_message.hex()}')
+ self.send_pdu_on_ctrl(msg)
+
+ def exit_suspend(self):
+ msg = ExitSuspend()
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID CONTROL EXIT SUSPEND, PDU:{hid_message.hex()}')
+ self.send_pdu_on_ctrl(msg)
+
+ def virtual_cable_unplug(self):
+ msg = VirtualCableUnplug()
+ hid_message = bytes(msg)
+ logger.debug(f'>>> HID CONTROL VIRTUAL CABLE UNPLUG, PDU: {hid_message.hex()}')
+ self.send_pdu_on_ctrl(msg)
diff --git a/bumble/l2cap.py b/bumble/l2cap.py
index cccb172..7a2f0ed 100644
--- a/bumble/l2cap.py
+++ b/bumble/l2cap.py
@@ -17,6 +17,7 @@
# -----------------------------------------------------------------------------
from __future__ import annotations
import asyncio
+import dataclasses
import enum
import logging
import struct
@@ -38,6 +39,7 @@
TYPE_CHECKING,
)
+from .utils import deprecated
from .colors import color
from .core import BT_CENTRAL_ROLE, InvalidStateError, ProtocolError
from .hci import (
@@ -167,6 +169,34 @@
# pylint: disable=invalid-name
[email protected]
+class ClassicChannelSpec:
+ psm: Optional[int] = None
+ mtu: int = L2CAP_MIN_BR_EDR_MTU
+
+
[email protected]
+class LeCreditBasedChannelSpec:
+ psm: Optional[int] = None
+ mtu: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU
+ mps: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS
+ max_credits: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS
+
+ def __post_init__(self):
+ if (
+ self.max_credits < 1
+ or self.max_credits > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_CREDITS
+ ):
+ raise ValueError('max credits out of range')
+ if self.mtu < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MTU:
+ raise ValueError('MTU too small')
+ if (
+ self.mps < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MPS
+ or self.mps > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MPS
+ ):
+ raise ValueError('MPS out of range')
+
+
class L2CAP_PDU:
'''
See Bluetooth spec @ Vol 3, Part A - 3 DATA PACKET FORMAT
@@ -676,7 +706,7 @@
# -----------------------------------------------------------------------------
-class Channel(EventEmitter):
+class ClassicChannel(EventEmitter):
class State(enum.IntEnum):
# States
CLOSED = 0x00
@@ -990,7 +1020,7 @@
# -----------------------------------------------------------------------------
-class LeConnectionOrientedChannel(EventEmitter):
+class LeCreditBasedChannel(EventEmitter):
"""
LE Credit-based Connection Oriented Channel
"""
@@ -1004,11 +1034,13 @@
CONNECTION_ERROR = 5
out_queue: Deque[bytes]
- connection_result: Optional[asyncio.Future[LeConnectionOrientedChannel]]
+ connection_result: Optional[asyncio.Future[LeCreditBasedChannel]]
disconnection_result: Optional[asyncio.Future[None]]
+ in_sdu: Optional[bytes]
out_sdu: Optional[bytes]
state: State
connection: Connection
+ sink: Optional[Callable[[bytes], Any]]
def __init__(
self,
@@ -1071,7 +1103,7 @@
def send_control_frame(self, frame: L2CAP_Control_Frame) -> None:
self.manager.send_control_frame(self.connection, L2CAP_LE_SIGNALING_CID, frame)
- async def connect(self) -> LeConnectionOrientedChannel:
+ async def connect(self) -> LeCreditBasedChannel:
# Check that we're in the right state
if self.state != self.State.INIT:
raise InvalidStateError('not in a connectable state')
@@ -1343,14 +1375,66 @@
# -----------------------------------------------------------------------------
+class ClassicChannelServer(EventEmitter):
+ def __init__(
+ self,
+ manager: ChannelManager,
+ psm: int,
+ handler: Optional[Callable[[ClassicChannel], Any]],
+ mtu: int,
+ ) -> None:
+ super().__init__()
+ self.manager = manager
+ self.handler = handler
+ self.psm = psm
+ self.mtu = mtu
+
+ def on_connection(self, channel: ClassicChannel) -> None:
+ self.emit('connection', channel)
+ if self.handler:
+ self.handler(channel)
+
+ def close(self) -> None:
+ if self.psm in self.manager.servers:
+ del self.manager.servers[self.psm]
+
+
+# -----------------------------------------------------------------------------
+class LeCreditBasedChannelServer(EventEmitter):
+ def __init__(
+ self,
+ manager: ChannelManager,
+ psm: int,
+ handler: Optional[Callable[[LeCreditBasedChannel], Any]],
+ max_credits: int,
+ mtu: int,
+ mps: int,
+ ) -> None:
+ super().__init__()
+ self.manager = manager
+ self.handler = handler
+ self.psm = psm
+ self.max_credits = max_credits
+ self.mtu = mtu
+ self.mps = mps
+
+ def on_connection(self, channel: LeCreditBasedChannel) -> None:
+ self.emit('connection', channel)
+ if self.handler:
+ self.handler(channel)
+
+ def close(self) -> None:
+ if self.psm in self.manager.le_coc_servers:
+ del self.manager.le_coc_servers[self.psm]
+
+
+# -----------------------------------------------------------------------------
class ChannelManager:
identifiers: Dict[int, int]
- channels: Dict[int, Dict[int, Union[Channel, LeConnectionOrientedChannel]]]
- servers: Dict[int, Callable[[Channel], Any]]
- le_coc_channels: Dict[int, Dict[int, LeConnectionOrientedChannel]]
- le_coc_servers: Dict[
- int, Tuple[Callable[[LeConnectionOrientedChannel], Any], int, int, int]
- ]
+ channels: Dict[int, Dict[int, Union[ClassicChannel, LeCreditBasedChannel]]]
+ servers: Dict[int, ClassicChannelServer]
+ le_coc_channels: Dict[int, Dict[int, LeCreditBasedChannel]]
+ le_coc_servers: Dict[int, LeCreditBasedChannelServer]
le_coc_requests: Dict[int, L2CAP_LE_Credit_Based_Connection_Request]
fixed_channels: Dict[int, Optional[Callable[[int, bytes], Any]]]
_host: Optional[Host]
@@ -1429,21 +1513,6 @@
raise RuntimeError('no free CID')
- @staticmethod
- def check_le_coc_parameters(max_credits: int, mtu: int, mps: int) -> None:
- if (
- max_credits < 1
- or max_credits > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_CREDITS
- ):
- raise ValueError('max credits out of range')
- if mtu < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MTU:
- raise ValueError('MTU too small')
- if (
- mps < L2CAP_LE_CREDIT_BASED_CONNECTION_MIN_MPS
- or mps > L2CAP_LE_CREDIT_BASED_CONNECTION_MAX_MPS
- ):
- raise ValueError('MPS out of range')
-
def next_identifier(self, connection: Connection) -> int:
identifier = (self.identifiers.setdefault(connection.handle, 0) + 1) % 256
self.identifiers[connection.handle] = identifier
@@ -1458,8 +1527,22 @@
if cid in self.fixed_channels:
del self.fixed_channels[cid]
- def register_server(self, psm: int, server: Callable[[Channel], Any]) -> int:
- if psm == 0:
+ @deprecated("Please use create_classic_server")
+ def register_server(
+ self,
+ psm: int,
+ server: Callable[[ClassicChannel], Any],
+ ) -> int:
+ return self.create_classic_server(
+ handler=server, spec=ClassicChannelSpec(psm=psm)
+ ).psm
+
+ def create_classic_server(
+ self,
+ spec: ClassicChannelSpec,
+ handler: Optional[Callable[[ClassicChannel], Any]] = None,
+ ) -> ClassicChannelServer:
+ if not spec.psm:
# Find a free PSM
for candidate in range(
L2CAP_PSM_DYNAMIC_RANGE_START, L2CAP_PSM_DYNAMIC_RANGE_END + 1, 2
@@ -1468,62 +1551,75 @@
continue
if candidate in self.servers:
continue
- psm = candidate
+ spec.psm = candidate
break
else:
raise InvalidStateError('no free PSM')
else:
# Check that the PSM isn't already in use
- if psm in self.servers:
+ if spec.psm in self.servers:
raise ValueError('PSM already in use')
# Check that the PSM is valid
- if psm % 2 == 0:
+ if spec.psm % 2 == 0:
raise ValueError('invalid PSM (not odd)')
- check = psm >> 8
+ check = spec.psm >> 8
while check:
if check % 2 != 0:
raise ValueError('invalid PSM')
check >>= 8
- self.servers[psm] = server
+ self.servers[spec.psm] = ClassicChannelServer(self, spec.psm, handler, spec.mtu)
- return psm
+ return self.servers[spec.psm]
+ @deprecated("Please use create_le_credit_based_server()")
def register_le_coc_server(
self,
psm: int,
- server: Callable[[LeConnectionOrientedChannel], Any],
- max_credits: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS,
- mtu: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU,
- mps: int = L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS,
+ server: Callable[[LeCreditBasedChannel], Any],
+ max_credits: int,
+ mtu: int,
+ mps: int,
) -> int:
- self.check_le_coc_parameters(max_credits, mtu, mps)
+ return self.create_le_credit_based_server(
+ spec=LeCreditBasedChannelSpec(
+ psm=None if psm == 0 else psm, mtu=mtu, mps=mps, max_credits=max_credits
+ ),
+ handler=server,
+ ).psm
- if psm == 0:
+ def create_le_credit_based_server(
+ self,
+ spec: LeCreditBasedChannelSpec,
+ handler: Optional[Callable[[LeCreditBasedChannel], Any]] = None,
+ ) -> LeCreditBasedChannelServer:
+ if not spec.psm:
# Find a free PSM
for candidate in range(
L2CAP_LE_PSM_DYNAMIC_RANGE_START, L2CAP_LE_PSM_DYNAMIC_RANGE_END + 1
):
if candidate in self.le_coc_servers:
continue
- psm = candidate
+ spec.psm = candidate
break
else:
raise InvalidStateError('no free PSM')
else:
# Check that the PSM isn't already in use
- if psm in self.le_coc_servers:
+ if spec.psm in self.le_coc_servers:
raise ValueError('PSM already in use')
- self.le_coc_servers[psm] = (
- server,
- max_credits or L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_INITIAL_CREDITS,
- mtu or L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MTU,
- mps or L2CAP_LE_CREDIT_BASED_CONNECTION_DEFAULT_MPS,
+ self.le_coc_servers[spec.psm] = LeCreditBasedChannelServer(
+ self,
+ spec.psm,
+ handler,
+ max_credits=spec.max_credits,
+ mtu=spec.mtu,
+ mps=spec.mps,
)
- return psm
+ return self.le_coc_servers[spec.psm]
def on_disconnection(self, connection_handle: int, _reason: int) -> None:
logger.debug(f'disconnection from {connection_handle}, cleaning up channels')
@@ -1650,13 +1746,13 @@
logger.debug(
f'creating server channel with cid={source_cid} for psm {request.psm}'
)
- channel = Channel(
- self, connection, cid, request.psm, source_cid, L2CAP_MIN_BR_EDR_MTU
+ channel = ClassicChannel(
+ self, connection, cid, request.psm, source_cid, server.mtu
)
connection_channels[source_cid] = channel
# Notify
- server(channel)
+ server.on_connection(channel)
channel.on_connection_request(request)
else:
logger.warning(
@@ -1878,7 +1974,7 @@
self, connection: Connection, cid: int, request
) -> None:
if request.le_psm in self.le_coc_servers:
- (server, max_credits, mtu, mps) = self.le_coc_servers[request.le_psm]
+ server = self.le_coc_servers[request.le_psm]
# Check that the CID isn't already used
le_connection_channels = self.le_coc_channels.setdefault(
@@ -1892,8 +1988,8 @@
L2CAP_LE_Credit_Based_Connection_Response(
identifier=request.identifier,
destination_cid=0,
- mtu=mtu,
- mps=mps,
+ mtu=server.mtu,
+ mps=server.mps,
initial_credits=0,
# pylint: disable=line-too-long
result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_SOURCE_CID_ALREADY_ALLOCATED,
@@ -1911,8 +2007,8 @@
L2CAP_LE_Credit_Based_Connection_Response(
identifier=request.identifier,
destination_cid=0,
- mtu=mtu,
- mps=mps,
+ mtu=server.mtu,
+ mps=server.mps,
initial_credits=0,
# pylint: disable=line-too-long
result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_REFUSED_NO_RESOURCES_AVAILABLE,
@@ -1925,18 +2021,18 @@
f'creating LE CoC server channel with cid={source_cid} for psm '
f'{request.le_psm}'
)
- channel = LeConnectionOrientedChannel(
+ channel = LeCreditBasedChannel(
self,
connection,
request.le_psm,
source_cid,
request.source_cid,
- mtu,
- mps,
+ server.mtu,
+ server.mps,
request.initial_credits,
request.mtu,
request.mps,
- max_credits,
+ server.max_credits,
True,
)
connection_channels[source_cid] = channel
@@ -1949,16 +2045,16 @@
L2CAP_LE_Credit_Based_Connection_Response(
identifier=request.identifier,
destination_cid=source_cid,
- mtu=mtu,
- mps=mps,
- initial_credits=max_credits,
+ mtu=server.mtu,
+ mps=server.mps,
+ initial_credits=server.max_credits,
# pylint: disable=line-too-long
result=L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_SUCCESSFUL,
),
)
# Notify
- server(channel)
+ server.on_connection(channel)
else:
logger.info(
f'No LE server for connection 0x{connection.handle:04X} '
@@ -2013,37 +2109,51 @@
channel.on_credits(credit.credits)
- def on_channel_closed(self, channel: Channel) -> None:
+ def on_channel_closed(self, channel: ClassicChannel) -> None:
connection_channels = self.channels.get(channel.connection.handle)
if connection_channels:
if channel.source_cid in connection_channels:
del connection_channels[channel.source_cid]
+ @deprecated("Please use create_le_credit_based_channel()")
async def open_le_coc(
self, connection: Connection, psm: int, max_credits: int, mtu: int, mps: int
- ) -> LeConnectionOrientedChannel:
- self.check_le_coc_parameters(max_credits, mtu, mps)
+ ) -> LeCreditBasedChannel:
+ return await self.create_le_credit_based_channel(
+ connection=connection,
+ spec=LeCreditBasedChannelSpec(
+ psm=psm, max_credits=max_credits, mtu=mtu, mps=mps
+ ),
+ )
+ async def create_le_credit_based_channel(
+ self,
+ connection: Connection,
+ spec: LeCreditBasedChannelSpec,
+ ) -> LeCreditBasedChannel:
# Find a free CID for the new channel
connection_channels = self.channels.setdefault(connection.handle, {})
source_cid = self.find_free_le_cid(connection_channels)
if source_cid is None: # Should never happen!
raise RuntimeError('all CIDs already in use')
+ if spec.psm is None:
+ raise ValueError('PSM cannot be None')
+
# Create the channel
- logger.debug(f'creating coc channel with cid={source_cid} for psm {psm}')
- channel = LeConnectionOrientedChannel(
+ logger.debug(f'creating coc channel with cid={source_cid} for psm {spec.psm}')
+ channel = LeCreditBasedChannel(
manager=self,
connection=connection,
- le_psm=psm,
+ le_psm=spec.psm,
source_cid=source_cid,
destination_cid=0,
- mtu=mtu,
- mps=mps,
+ mtu=spec.mtu,
+ mps=spec.mps,
credits=0,
peer_mtu=0,
peer_mps=0,
- peer_credits=max_credits,
+ peer_credits=spec.max_credits,
connected=False,
)
connection_channels[source_cid] = channel
@@ -2062,7 +2172,15 @@
return channel
- async def connect(self, connection: Connection, psm: int) -> Channel:
+ @deprecated("Please use create_classic_channel()")
+ async def connect(self, connection: Connection, psm: int) -> ClassicChannel:
+ return await self.create_classic_channel(
+ connection=connection, spec=ClassicChannelSpec(psm=psm)
+ )
+
+ async def create_classic_channel(
+ self, connection: Connection, spec: ClassicChannelSpec
+ ) -> ClassicChannel:
# NOTE: this implementation hard-codes BR/EDR
# Find a free CID for a new channel
@@ -2071,10 +2189,20 @@
if source_cid is None: # Should never happen!
raise RuntimeError('all CIDs already in use')
+ if spec.psm is None:
+ raise ValueError('PSM cannot be None')
+
# Create the channel
- logger.debug(f'creating client channel with cid={source_cid} for psm {psm}')
- channel = Channel(
- self, connection, L2CAP_SIGNALING_CID, psm, source_cid, L2CAP_MIN_BR_EDR_MTU
+ logger.debug(
+ f'creating client channel with cid={source_cid} for psm {spec.psm}'
+ )
+ channel = ClassicChannel(
+ self,
+ connection,
+ L2CAP_SIGNALING_CID,
+ spec.psm,
+ source_cid,
+ spec.mtu,
)
connection_channels[source_cid] = channel
@@ -2086,3 +2214,20 @@
raise e
return channel
+
+
+# -----------------------------------------------------------------------------
+# Deprecated Classes
+# -----------------------------------------------------------------------------
+
+
+class Channel(ClassicChannel):
+ @deprecated("Please use ClassicChannel")
+ def __init__(self, *args, **kwargs) -> None:
+ super().__init__(*args, **kwargs)
+
+
+class LeConnectionOrientedChannel(LeCreditBasedChannel):
+ @deprecated("Please use LeCreditBasedChannel")
+ def __init__(self, *args, **kwargs) -> None:
+ super().__init__(*args, **kwargs)
diff --git a/bumble/pandora/security.py b/bumble/pandora/security.py
index 0f31512..85365e6 100644
--- a/bumble/pandora/security.py
+++ b/bumble/pandora/security.py
@@ -450,21 +450,18 @@
'security_request': pair,
}
- # register event handlers
- for event, listener in listeners.items():
- connection.on(event, listener)
+ with contextlib.closing(EventWatcher()) as watcher:
+ # register event handlers
+ for event, listener in listeners.items():
+ watcher.on(connection, event, listener)
- # security level already reached
- if self.reached_security_level(connection, level):
- return WaitSecurityResponse(success=empty_pb2.Empty())
+ # security level already reached
+ if self.reached_security_level(connection, level):
+ return WaitSecurityResponse(success=empty_pb2.Empty())
- self.log.debug('Wait for security...')
- kwargs = {}
- kwargs[await wait_for_security] = empty_pb2.Empty()
-
- # remove event handlers
- for event, listener in listeners.items():
- connection.remove_listener(event, listener) # type: ignore
+ self.log.debug('Wait for security...')
+ kwargs = {}
+ kwargs[await wait_for_security] = empty_pb2.Empty()
# wait for `authenticate` to finish if any
if authenticate_task is not None:
diff --git a/bumble/profiles/asha_service.py b/bumble/profiles/asha_service.py
index 6898397..412b28a 100644
--- a/bumble/profiles/asha_service.py
+++ b/bumble/profiles/asha_service.py
@@ -19,6 +19,8 @@
import struct
import logging
from typing import List
+
+from bumble import l2cap
from ..core import AdvertisingData
from ..device import Device, Connection
from ..gatt import (
@@ -149,7 +151,10 @@
channel.sink = on_data
# let the server find a free PSM
- self.psm = self.device.register_l2cap_channel_server(self.psm, on_coc, 8)
+ self.psm = device.create_l2cap_server(
+ spec=l2cap.LeCreditBasedChannelSpec(psm=self.psm, max_credits=8),
+ handler=on_coc,
+ ).psm
self.le_psm_out_characteristic = Characteristic(
GATT_ASHA_LE_PSM_OUT_CHARACTERISTIC,
Characteristic.Properties.READ,
diff --git a/bumble/rfcomm.py b/bumble/rfcomm.py
index 02c18fa..53e98e0 100644
--- a/bumble/rfcomm.py
+++ b/bumble/rfcomm.py
@@ -674,7 +674,7 @@
acceptor: Optional[Callable[[int], bool]]
dlcs: Dict[int, DLC]
- def __init__(self, l2cap_channel: l2cap.Channel, role: Role) -> None:
+ def __init__(self, l2cap_channel: l2cap.ClassicChannel, role: Role) -> None:
super().__init__()
self.role = role
self.l2cap_channel = l2cap_channel
@@ -887,7 +887,7 @@
# -----------------------------------------------------------------------------
class Client:
multiplexer: Optional[Multiplexer]
- l2cap_channel: Optional[l2cap.Channel]
+ l2cap_channel: Optional[l2cap.ClassicChannel]
def __init__(self, device: Device, connection: Connection) -> None:
self.device = device
@@ -898,8 +898,8 @@
async def start(self) -> Multiplexer:
# Create a new L2CAP connection
try:
- self.l2cap_channel = await self.device.l2cap_channel_manager.connect(
- self.connection, RFCOMM_PSM
+ self.l2cap_channel = await self.connection.create_l2cap_channel(
+ spec=l2cap.ClassicChannelSpec(RFCOMM_PSM)
)
except ProtocolError as error:
logger.warning(f'L2CAP connection failed: {error}')
@@ -936,7 +936,9 @@
self.acceptors = {}
# Register ourselves with the L2CAP channel manager
- device.register_l2cap_server(RFCOMM_PSM, self.on_connection)
+ device.create_l2cap_server(
+ spec=l2cap.ClassicChannelSpec(psm=RFCOMM_PSM), handler=self.on_connection
+ )
def listen(self, acceptor: Callable[[DLC], None], channel: int = 0) -> int:
if channel:
@@ -960,11 +962,11 @@
self.acceptors[channel] = acceptor
return channel
- def on_connection(self, l2cap_channel: l2cap.Channel) -> None:
+ def on_connection(self, l2cap_channel: l2cap.ClassicChannel) -> None:
logger.debug(f'+++ new L2CAP connection: {l2cap_channel}')
l2cap_channel.on('open', lambda: self.on_l2cap_channel_open(l2cap_channel))
- def on_l2cap_channel_open(self, l2cap_channel: l2cap.Channel) -> None:
+ def on_l2cap_channel_open(self, l2cap_channel: l2cap.ClassicChannel) -> None:
logger.debug(f'$$$ L2CAP channel open: {l2cap_channel}')
# Create a new multiplexer for the channel
diff --git a/bumble/sdp.py b/bumble/sdp.py
index 6428187..bc8303c 100644
--- a/bumble/sdp.py
+++ b/bumble/sdp.py
@@ -167,7 +167,7 @@
UUID: lambda x: DataElement(
DataElement.UUID, core.UUID.from_bytes(bytes(reversed(x)))
),
- TEXT_STRING: lambda x: DataElement(DataElement.TEXT_STRING, x.decode('utf8')),
+ TEXT_STRING: lambda x: DataElement(DataElement.TEXT_STRING, x),
BOOLEAN: lambda x: DataElement(DataElement.BOOLEAN, x[0] == 1),
SEQUENCE: lambda x: DataElement(
DataElement.SEQUENCE, DataElement.list_from_bytes(x)
@@ -229,7 +229,7 @@
return DataElement(DataElement.UUID, value)
@staticmethod
- def text_string(value: str) -> DataElement:
+ def text_string(value: bytes) -> DataElement:
return DataElement(DataElement.TEXT_STRING, value)
@staticmethod
@@ -376,7 +376,7 @@
raise ValueError('invalid value_size')
elif self.type == DataElement.UUID:
data = bytes(reversed(bytes(self.value)))
- elif self.type in (DataElement.TEXT_STRING, DataElement.URL):
+ elif self.type == DataElement.URL:
data = self.value.encode('utf8')
elif self.type == DataElement.BOOLEAN:
data = bytes([1 if self.value else 0])
@@ -758,7 +758,7 @@
# -----------------------------------------------------------------------------
class Client:
- channel: Optional[l2cap.Channel]
+ channel: Optional[l2cap.ClassicChannel]
def __init__(self, device: Device) -> None:
self.device = device
@@ -766,8 +766,9 @@
self.channel = None
async def connect(self, connection: Connection) -> None:
- result = await self.device.l2cap_channel_manager.connect(connection, SDP_PSM)
- self.channel = result
+ self.channel = await connection.create_l2cap_channel(
+ spec=l2cap.ClassicChannelSpec(SDP_PSM)
+ )
async def disconnect(self) -> None:
if self.channel:
@@ -921,7 +922,7 @@
# -----------------------------------------------------------------------------
class Server:
CONTINUATION_STATE = bytes([0x01, 0x43])
- channel: Optional[l2cap.Channel]
+ channel: Optional[l2cap.ClassicChannel]
Service = NewType('Service', List[ServiceAttribute])
service_records: Dict[int, Service]
current_response: Union[None, bytes, Tuple[int, List[int]]]
@@ -933,7 +934,9 @@
self.current_response = None
def register(self, l2cap_channel_manager: l2cap.ChannelManager) -> None:
- l2cap_channel_manager.register_server(SDP_PSM, self.on_connection)
+ l2cap_channel_manager.create_classic_server(
+ spec=l2cap.ClassicChannelSpec(psm=SDP_PSM), handler=self.on_connection
+ )
def send_response(self, response):
logger.debug(f'{color(">>> Sending SDP Response", "blue")}: {response}')
diff --git a/bumble/utils.py b/bumble/utils.py
index dc03725..a562618 100644
--- a/bumble/utils.py
+++ b/bumble/utils.py
@@ -21,6 +21,7 @@
import traceback
import collections
import sys
+import warnings
from typing import (
Awaitable,
Set,
@@ -33,7 +34,7 @@
Union,
overload,
)
-from functools import wraps
+from functools import wraps, partial
from pyee import EventEmitter
from .colors import color
@@ -410,3 +411,36 @@
self.resume_source()
self.check_pump()
+
+
+async def async_call(function, *args, **kwargs):
+ """
+ Immediately calls the function with provided args and kwargs, wrapping it in an async function.
+ Rust's `pyo3_asyncio` library needs functions to be marked async to properly inject a running loop.
+
+ result = await async_call(some_function, ...)
+ """
+ return function(*args, **kwargs)
+
+
+def wrap_async(function):
+ """
+ Wraps the provided function in an async function.
+ """
+ return partial(async_call, function)
+
+
+def deprecated(msg: str):
+ """
+ Throw deprecation warning before execution
+ """
+
+ def wrapper(function):
+ @wraps(function)
+ def inner(*args, **kwargs):
+ warnings.warn(msg, DeprecationWarning)
+ return function(*args, **kwargs)
+
+ return inner
+
+ return wrapper
diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml
index 82a6f41..67bdd7c 100644
--- a/docs/mkdocs/mkdocs.yml
+++ b/docs/mkdocs/mkdocs.yml
@@ -67,6 +67,9 @@
- Zephyr: platforms/zephyr.md
- Examples:
- Overview: examples/index.md
+ - Extras:
+ - Overview: extras/index.md
+ - Android Remote HCI: extras/android_remote_hci.md
copyright: Copyright 2021-2023 Google LLC
diff --git a/docs/mkdocs/src/extras/android_remote_hci.md b/docs/mkdocs/src/extras/android_remote_hci.md
new file mode 100644
index 0000000..4eab132
--- /dev/null
+++ b/docs/mkdocs/src/extras/android_remote_hci.md
@@ -0,0 +1,141 @@
+ANDROID REMOTE HCI APP
+======================
+
+This application allows using an android phone's built-in Bluetooth controller with
+a Bumble host stack running outside the phone (typically a development laptop or desktop).
+The app runs an HCI proxy between a TCP socket on the "outside" and the Bluetooth HCI HAL
+on the "inside". (See [this page](https://source.android.com/docs/core/connect/bluetooth) for a high level
+description of the Android Bluetooth HCI HAL).
+The HCI packets received on the TCP socket are forwarded to the phone's controller, and the
+packets coming from the controller are forwarded to the TCP socket.
+
+
+Building
+--------
+
+You can build the app by running `./gradlew build` (use `gradlew.bat` on Windows) from the `RemoteHCI` top level directory.
+You can also build with Android Studio: open the `RemoteHCI` project. You can build and/or debug from there.
+
+If the build succeeds, you can find the app APKs (debug and release) at:
+
+ * [Release] ``app/build/outputs/apk/release/app-release-unsigned.apk``
+ * [Debug] ``app/build/outputs/apk/debug/app-debug.apk``
+
+
+Running
+-------
+
+### Preconditions
+When the proxy starts (tapping the "Start" button in the app's main activity), it will try to
+bind to the Bluetooth HAL. This requires disabling SELinux temporarily, and being the only HAL client.
+
+#### Disabling SELinux
+Binding to the Bluetooth HCI HAL requires certain SELinux permissions that can't simply be changed
+on a device without rebuilding its system image. To bypass these restrictions, you will need
+to disable SELinux on your phone (please be aware that this is global, not just for the proxy app,
+so proceed with caution).
+In order to disable SELinux, you need to root the phone (it may be advisable to do this on a
+development phone).
+
+!!! tip "Disabling SELinux Temporarily"
+ Restart `adb` as root:
+ ```bash
+ $ adb root
+ ```
+
+ Then disable SELinux
+ ```bash
+ $ adb shell setenforce 0
+ ```
+
+ Once you're done using the proxy, you can restore SELinux, if you need to, with
+ ```bash
+ $ adb shell setenforce 1
+ ```
+
+ This state will also reset to the normal SELinux enforcement when you reboot.
+
+#### Stopping the bluetooth process
+Since the Bluetooth HAL service can only accept one client, and that in normal conditions
+that client is the Android's bluetooth stack, it is required to first shut down the
+Android bluetooth stack process.
+
+!!! tip "Checking if the Bluetooth process is running"
+ ```bash
+ $ adb shell "ps -A | grep com.google.android.bluetooth"
+ ```
+ If the process is running, you will get a line like:
+ ```
+ bluetooth 10759 876 17455796 136620 do_epoll_wait 0 S com.google.android.bluetooth
+ ```
+ If you don't, it means that the process is not running and you are clear to proceed.
+
+Simply turning Bluetooth off from the phone's settings does not ensure that the bluetooth process will exit.
+If the bluetooth process is still running after toggling Bluetooth off from the settings, you may try enabling
+Airplane Mode, then rebooting. The bluetooth process should, in theory, not restart after the reboot.
+
+!!! tip "Stopping the bluetooth process with adb"
+ ```bash
+ $ adb shell cmd bluetooth_manager disable
+ ```
+
+### Starting the app
+You can start the app from the Android launcher, from Android Studio, or with `adb`
+
+#### Launching from the launcher
+Just tap the app icon on the launcher, check the TCP port that is configured, and tap
+the "Start" button.
+
+#### Launching with `adb`
+Using the `am` command, you can start the activity, and pass it arguments so that you can
+automatically start the proxy, and/or set the port number.
+
+!!! tip "Launching from adb with auto-start"
+ ```bash
+ $ adb shell am start -n com.github.google.bumble.remotehci/.MainActivity --ez autostart true
+ ```
+
+!!! tip "Launching from adb with auto-start and a port"
+ In this example, we auto-start the proxy upon launch, with the port set to 9995
+ ```bash
+ $ adb shell am start -n com.github.google.bumble.remotehci/.MainActivity --ez autostart true --ei port 9995
+ ```
+
+#### Selecting a TCP port
+The RemoteHCI app's main activity has a "TCP Port" setting where you can change the port on
+which the proxy is accepting connections. If the default value isn't suitable, you can
+change it there (you can also use the special value 0 to let the OS assign a port number for you).
+
+### Connecting to the proxy
+To connect the Bumble stack to the proxy, you need to be able to reach the phone's network
+stack. This can be done over the phone's WiFi connection, or, alternatively, using an `adb`
+TCP forward (which should be faster than over WiFi).
+
+!!! tip "Forwarding TCP with `adb`"
+ To connect to the proxy via an `adb` TCP forward, use:
+ ```bash
+ $ adb forward tcp:<outside-port> tcp:<inside-port>
+ ```
+ Where ``<outside-port>`` is the port number for a listening socket on your laptop or
+ desktop machine, and <inside-port> is the TCP port selected in the app's user interface.
+ Those two ports may be the same, of course.
+ For example, with the default TCP port 9993:
+ ```bash
+ $ adb forward tcp:9993 tcp:9993
+ ```
+
+Once you've ensured that you can reach the proxy's TCP port on the phone, either directly or
+via an `adb` forward, you can then use it as a Bumble transport, using the transport name:
+``tcp-client:<host>:<port>`` syntax.
+
+!!! example "Connecting a Bumble client"
+ Connecting the `bumble-controller-info` app to the phone's controller.
+ Assuming you have set up an `adb` forward on port 9993:
+ ```bash
+ $ bumble-controller-info tcp-client:localhost:9993
+ ```
+
+ Or over WiFi with, in this example, the IP address of the phone being ```192.168.86.27```
+ ```bash
+ $ bumble-controller-info tcp-client:192.168.86.27:9993
+ ```
diff --git a/docs/mkdocs/src/extras/index.md b/docs/mkdocs/src/extras/index.md
new file mode 100644
index 0000000..ae906c1
--- /dev/null
+++ b/docs/mkdocs/src/extras/index.md
@@ -0,0 +1,11 @@
+EXTRAS
+======
+
+A collection of add-ons, apps and tools, to the Bumble project.
+
+Android Remote HCI
+------------------
+
+Allows using an Android phone's built-in Bluetooth controller with a Bumble
+stack running on a development machine.
+See [Android Remote HCI](android_remote_hci.md) for details.
\ No newline at end of file
diff --git a/docs/mkdocs/src/hardware/index.md b/docs/mkdocs/src/hardware/index.md
index 986532b..9eabab0 100644
--- a/docs/mkdocs/src/hardware/index.md
+++ b/docs/mkdocs/src/hardware/index.md
@@ -3,7 +3,7 @@
The Bumble Host connects to a controller over an [HCI Transport](../transports/index.md).
To use a hardware controller attached to the host on which the host application is running, the transport is typically either [HCI over UART](../transports/serial.md) or [HCI over USB](../transports/usb.md).
-On Linux, the [VHCI Transport](../transports/vhci.md) can be used to communicate with any controller hardware managed by the operating system. Alternatively, a remote controller (a phyiscal controller attached to a remote host) can be used by connecting one of the networked transports (such as the [TCP Client transport](../transports/tcp_client.md), the [TCP Server transport](../transports/tcp_server.md) or the [UDP Transport](../transports/udp.md)) to an [HCI Bridge](../apps_and_tools/hci_bridge) bridging the network transport to a physical controller on a remote host.
+On Linux, the [VHCI Transport](../transports/vhci.md) can be used to communicate with any controller hardware managed by the operating system. Alternatively, a remote controller (a phyiscal controller attached to a remote host) can be used by connecting one of the networked transports (such as the [TCP Client transport](../transports/tcp_client.md), the [TCP Server transport](../transports/tcp_server.md) or the [UDP Transport](../transports/udp.md)) to an [HCI Bridge](../apps_and_tools/hci_bridge.md) bridging the network transport to a physical controller on a remote host.
In theory, any controller that is compliant with the HCI over UART or HCI over USB protocols can be used.
diff --git a/examples/hid_key_map.py b/examples/hid_key_map.py
new file mode 100644
index 0000000..5f61e7f
--- /dev/null
+++ b/examples/hid_key_map.py
@@ -0,0 +1,248 @@
+# shift map
+
+# letters
+shift_map = {
+ 'a': 'A',
+ 'b': 'B',
+ 'c': 'C',
+ 'd': 'D',
+ 'e': 'E',
+ 'f': 'F',
+ 'g': 'G',
+ 'h': 'H',
+ 'i': 'I',
+ 'j': 'J',
+ 'k': 'K',
+ 'l': 'L',
+ 'm': 'M',
+ 'n': 'N',
+ 'o': 'O',
+ 'p': 'P',
+ 'q': 'Q',
+ 'r': 'R',
+ 's': 'S',
+ 't': 'T',
+ 'u': 'U',
+ 'v': 'V',
+ 'w': 'W',
+ 'x': 'X',
+ 'y': 'Y',
+ 'z': 'Z',
+ # numbers
+ '1': '!',
+ '2': '@',
+ '3': '#',
+ '4': '$',
+ '5': '%',
+ '6': '^',
+ '7': '&',
+ '8': '*',
+ '9': '(',
+ '0': ')',
+ # symbols
+ '-': '_',
+ '=': '+',
+ '[': '{',
+ ']': '}',
+ '\\': '|',
+ ';': ':',
+ '\'': '"',
+ ',': '<',
+ '.': '>',
+ '/': '?',
+ '`': '~',
+}
+
+# hex map
+
+# modifier keys
+mod_keys = {
+ '00': '',
+ '01': 'left_ctrl',
+ '02': 'left_shift',
+ '04': 'left_alt',
+ '08': 'left_meta',
+ '10': 'right_ctrl',
+ '20': 'right_shift',
+ '40': 'right_alt',
+ '80': 'right_meta',
+}
+
+# base keys
+
+base_keys = {
+ # meta
+ '00': '', # none
+ '01': 'error_ovf',
+ # letters
+ '04': 'a',
+ '05': 'b',
+ '06': 'c',
+ '07': 'd',
+ '08': 'e',
+ '09': 'f',
+ '0a': 'g',
+ '0b': 'h',
+ '0c': 'i',
+ '0d': 'j',
+ '0e': 'k',
+ '0f': 'l',
+ '10': 'm',
+ '11': 'n',
+ '12': 'o',
+ '13': 'p',
+ '14': 'q',
+ '15': 'r',
+ '16': 's',
+ '17': 't',
+ '18': 'u',
+ '19': 'v',
+ '1a': 'w',
+ '1b': 'x',
+ '1c': 'y',
+ '1d': 'z',
+ # numbers
+ '1e': '1',
+ '1f': '2',
+ '20': '3',
+ '21': '4',
+ '22': '5',
+ '23': '6',
+ '24': '7',
+ '25': '8',
+ '26': '9',
+ '27': '0',
+ # misc
+ '28': 'enter', # enter \n
+ '29': 'esc',
+ '2a': 'backspace',
+ '2b': 'tab',
+ '2c': 'spacebar', # space
+ '2d': '-',
+ '2e': '=',
+ '2f': '[',
+ '30': ']',
+ '31': '\\',
+ '32': '=',
+ '33': '_SEMICOLON',
+ '34': 'KEY_APOSTROPHE',
+ '35': 'KEY_GRAVE',
+ '36': 'KEY_COMMA',
+ '37': 'KEY_DOT',
+ '38': 'KEY_SLASH',
+ '39': 'KEY_CAPSLOCK',
+ '3a': 'KEY_F1',
+ '3b': 'KEY_F2',
+ '3c': 'KEY_F3',
+ '3d': 'KEY_F4',
+ '3e': 'KEY_F5',
+ '3f': 'KEY_F6',
+ '40': 'KEY_F7',
+ '41': 'KEY_F8',
+ '42': 'KEY_F9',
+ '43': 'KEY_F10',
+ '44': 'KEY_F11',
+ '45': 'KEY_F12',
+ '46': 'KEY_SYSRQ',
+ '47': 'KEY_SCROLLLOCK',
+ '48': 'KEY_PAUSE',
+ '49': 'KEY_INSERT',
+ '4a': 'KEY_HOME',
+ '4b': 'KEY_PAGEUP',
+ '4c': 'KEY_DELETE',
+ '4d': 'KEY_END',
+ '4e': 'KEY_PAGEDOWN',
+ '4f': 'KEY_RIGHT',
+ '50': 'KEY_LEFT',
+ '51': 'KEY_DOWN',
+ '52': 'KEY_UP',
+ '53': 'KEY_NUMLOCK',
+ '54': 'KEY_KPSLASH',
+ '55': 'KEY_KPASTERISK',
+ '56': 'KEY_KPMINUS',
+ '57': 'KEY_KPPLUS',
+ '58': 'KEY_KPENTER',
+ '59': 'KEY_KP1',
+ '5a': 'KEY_KP2',
+ '5b': 'KEY_KP3',
+ '5c': 'KEY_KP4',
+ '5d': 'KEY_KP5',
+ '5e': 'KEY_KP6',
+ '5f': 'KEY_KP7',
+ '60': 'KEY_KP8',
+ '61': 'KEY_KP9',
+ '62': 'KEY_KP0',
+ '63': 'KEY_KPDOT',
+ '64': 'KEY_102ND',
+ '65': 'KEY_COMPOSE',
+ '66': 'KEY_POWER',
+ '67': 'KEY_KPEQUAL',
+ '68': 'KEY_F13',
+ '69': 'KEY_F14',
+ '6a': 'KEY_F15',
+ '6b': 'KEY_F16',
+ '6c': 'KEY_F17',
+ '6d': 'KEY_F18',
+ '6e': 'KEY_F19',
+ '6f': 'KEY_F20',
+ '70': 'KEY_F21',
+ '71': 'KEY_F22',
+ '72': 'KEY_F23',
+ '73': 'KEY_F24',
+ '74': 'KEY_OPEN',
+ '75': 'KEY_HELP',
+ '76': 'KEY_PROPS',
+ '77': 'KEY_FRONT',
+ '78': 'KEY_STOP',
+ '79': 'KEY_AGAIN',
+ '7a': 'KEY_UNDO',
+ '7b': 'KEY_CUT',
+ '7c': 'KEY_COPY',
+ '7d': 'KEY_PASTE',
+ '7e': 'KEY_FIND',
+ '7f': 'KEY_MUTE',
+ '80': 'KEY_VOLUMEUP',
+ '81': 'KEY_VOLUMEDOWN',
+ '85': 'KEY_KPCOMMA',
+ '87': 'KEY_RO',
+ '88': 'KEY_KATAKANAHIRAGANA',
+ '89': 'KEY_YEN',
+ '8a': 'KEY_HENKAN',
+ '8b': 'KEY_MUHENKAN',
+ '8c': 'KEY_KPJPCOMMA',
+ '90': 'KEY_HANGEUL',
+ '91': 'KEY_HANJA',
+ '92': 'KEY_KATAKANA',
+ '93': 'KEY_HIRAGANA',
+ '94': 'KEY_ZENKAKUHANKAKU',
+ 'b6': 'KEY_KPLEFTPAREN',
+ 'b7': 'KEY_KPRIGHTPAREN',
+ 'e0': 'KEY_LEFTCTRL',
+ 'e1': 'KEY_LEFTSHIFT',
+ 'e2': 'KEY_LEFTALT',
+ 'e3': 'KEY_LEFTMETA',
+ 'e4': 'KEY_RIGHTCTRL',
+ 'e5': 'KEY_RIGHTSHIFT',
+ 'e6': 'KEY_RIGHTALT',
+ 'e7': 'KEY_RIGHTMETA',
+ 'e8': 'KEY_MEDIA_PLAYPAUSE',
+ 'e9': 'KEY_MEDIA_STOPCD',
+ 'ea': 'KEY_MEDIA_PREVIOUSSONG',
+ 'eb': 'KEY_MEDIA_NEXTSONG',
+ 'ec': 'KEY_MEDIA_EJECTCD',
+ 'ed': 'KEY_MEDIA_VOLUMEUP',
+ 'ee': 'KEY_MEDIA_VOLUMEDOWN',
+ 'ef': 'KEY_MEDIA_MUTE',
+ 'f0': 'KEY_MEDIA_WWW',
+ 'f1': 'KEY_MEDIA_BACK',
+ 'f2': 'KEY_MEDIA_FORWARD',
+ 'f3': 'KEY_MEDIA_STOP',
+ 'f4': 'KEY_MEDIA_FIND',
+ 'f5': 'KEY_MEDIA_SCROLLUP',
+ 'f6': 'KEY_MEDIA_SCROLLDOWN',
+ 'f7': 'KEY_MEDIA_EDIT',
+ 'f8': 'KEY_MEDIA_SLEEP',
+ 'f9': 'KEY_MEDIA_COFFEE',
+ 'fa': 'KEY_MEDIA_REFRESH',
+ 'fb': 'KEY_MEDIA_CALC',
+}
diff --git a/examples/hid_report_parser.py b/examples/hid_report_parser.py
new file mode 100644
index 0000000..61561b3
--- /dev/null
+++ b/examples/hid_report_parser.py
@@ -0,0 +1,159 @@
+from bumble.colors import color
+from hid_key_map import base_keys, mod_keys, shift_map
+
+
+# ------------------------------------------------------------------------------
+def get_key(modifier: str, key: str) -> str:
+ if modifier == '22':
+ modifier = '02'
+ if modifier in mod_keys:
+ modifier = mod_keys[modifier]
+ else:
+ return ''
+ if key in base_keys:
+ key = base_keys[key]
+ else:
+ return ''
+ if (modifier == 'left_shift' or modifier == 'right_shift') and key in shift_map:
+ key = shift_map[key]
+
+ return key
+
+
+class Keyboard:
+ def __init__(self): # type: ignore
+ self.report = [
+ [ # Bit array for Modifier keys
+ 0, # Right GUI - (usually the Windows key)
+ 0, # Right ALT
+ 0, # Right Shift
+ 0, # Right Control
+ 0, # Left GUI - (usually the Windows key)
+ 0, # Left ALT
+ 0, # Left Shift
+ 0, # Left Control
+ ],
+ 0x00, # Vendor reserved
+ '', # Rest is space for 6 keys
+ '',
+ '',
+ '',
+ '',
+ '',
+ ]
+
+ def decode_keyboard_report(self, input_report: bytes, report_length: int) -> None:
+ if report_length >= 8:
+ modifier = input_report[1]
+ self.report[0] = [int(x) for x in '{0:08b}'.format(modifier)]
+ self.report[0].reverse() # type: ignore
+
+ modifier_key = str((modifier & 0x22).to_bytes(1, "big").hex())
+ keycodes = []
+ for k in range(3, report_length):
+ keycodes.append(str(input_report[k].to_bytes(1, "big").hex()))
+ self.report[k - 1] = get_key(modifier_key, keycodes[k - 3])
+ else:
+ print(color('Warning: Not able to parse report', 'yellow'))
+
+ def print_keyboard_report(self) -> None:
+ print(color('\tKeyboard Input Received', 'green', None, 'bold'))
+ print(color(f'Keys:', 'white', None, 'bold'))
+ for i in range(1, 7):
+ print(
+ color(f' Key{i}{" ":>8s}= ', 'cyan', None, 'bold'), self.report[i + 1]
+ )
+ print(color(f'\nModifier Keys:', 'white', None, 'bold'))
+ print(
+ color(f' Left Ctrl : ', 'cyan'),
+ f'{self.report[0][0] == 1!s:<5}', # type: ignore
+ color(f' Left Shift : ', 'cyan'),
+ f'{self.report[0][1] == 1!s:<5}', # type: ignore
+ color(f' Left ALT : ', 'cyan'),
+ f'{self.report[0][2] == 1!s:<5}', # type: ignore
+ color(f' Left GUI : ', 'cyan'),
+ f'{self.report[0][3] == 1!s:<5}\n', # type: ignore
+ color(f' Right Ctrl : ', 'cyan'),
+ f'{self.report[0][4] == 1!s:<5}', # type: ignore
+ color(f' Right Shift : ', 'cyan'),
+ f'{self.report[0][5] == 1!s:<5}', # type: ignore
+ color(f' Right ALT : ', 'cyan'),
+ f'{self.report[0][6] == 1!s:<5}', # type: ignore
+ color(f' Right GUI : ', 'cyan'),
+ f'{self.report[0][7] == 1!s:<5}', # type: ignore
+ )
+
+
+# ------------------------------------------------------------------------------
+class Mouse:
+ def __init__(self): # type: ignore
+ self.report = [
+ [ # Bit array for Buttons
+ 0, # Button 1 (primary/trigger
+ 0, # Button 2 (secondary)
+ 0, # Button 3 (tertiary)
+ 0, # Button 4
+ 0, # Button 5
+ 0, # unused padding bits
+ 0, # unused padding bits
+ 0, # unused padding bits
+ ],
+ 0, # X
+ 0, # Y
+ 0, # Wheel
+ 0, # AC Pan
+ ]
+
+ def decode_mouse_report(self, input_report: bytes, report_length: int) -> None:
+ self.report[0] = [int(x) for x in '{0:08b}'.format(input_report[1])]
+ self.report[0].reverse() # type: ignore
+ self.report[1] = input_report[2]
+ self.report[2] = input_report[3]
+ if report_length in [5, 6]:
+ self.report[3] = input_report[4]
+ self.report[4] = input_report[5] if report_length == 6 else 0
+
+ def print_mouse_report(self) -> None:
+ print(color('\tMouse Input Received', 'green', None, 'bold'))
+ print(
+ color(f' Button 1 (primary/trigger) = ', 'cyan'),
+ self.report[0][0] == 1, # type: ignore
+ color(f'\n Button 2 (secondary) = ', 'cyan'),
+ self.report[0][1] == 1, # type: ignore
+ color(f'\n Button 3 (tertiary) = ', 'cyan'),
+ self.report[0][2] == 1, # type: ignore
+ color(f'\n Button4 = ', 'cyan'),
+ self.report[0][3] == 1, # type: ignore
+ color(f'\n Button5 = ', 'cyan'),
+ self.report[0][4] == 1, # type: ignore
+ color(f'\n X (X-axis displacement) = ', 'cyan'),
+ self.report[1],
+ color(f'\n Y (Y-axis displacement) = ', 'cyan'),
+ self.report[2],
+ color(f'\n Wheel = ', 'cyan'),
+ self.report[3],
+ color(f'\n AC PAN = ', 'cyan'),
+ self.report[4],
+ )
+
+
+# ------------------------------------------------------------------------------
+class ReportParser:
+ @staticmethod
+ def parse_input_report(input_report: bytes) -> None:
+
+ report_id = input_report[0] # pylint: disable=unsubscriptable-object
+ report_length = len(input_report)
+
+ # Keyboard input report (report id = 1)
+ if report_id == 1 and report_length >= 8:
+ keyboard = Keyboard() # type: ignore
+ keyboard.decode_keyboard_report(input_report, report_length)
+ keyboard.print_keyboard_report()
+ # Mouse input report (report id = 2)
+ elif report_id == 2 and report_length in [4, 5, 6]:
+ mouse = Mouse() # type: ignore
+ mouse.decode_mouse_report(input_report, report_length)
+ mouse.print_mouse_report()
+ else:
+ print(color(f'Warning: Parse Error Report ID {report_id}', 'yellow'))
diff --git a/examples/run_a2dp_sink.py b/examples/run_a2dp_sink.py
index b20f0d6..61bdce3 100644
--- a/examples/run_a2dp_sink.py
+++ b/examples/run_a2dp_sink.py
@@ -131,7 +131,7 @@
await device.power_on()
# Create a listener to wait for AVDTP connections
- listener = Listener(Listener.create_registrar(device))
+ listener = Listener.for_device(device)
listener.on('connection', on_avdtp_connection)
if len(sys.argv) >= 5:
diff --git a/examples/run_a2dp_source.py b/examples/run_a2dp_source.py
index 2443518..69dc2d0 100644
--- a/examples/run_a2dp_source.py
+++ b/examples/run_a2dp_source.py
@@ -179,7 +179,7 @@
await stream_packets(read, protocol)
else:
# Create a listener to wait for AVDTP connections
- listener = Listener(Listener.create_registrar(device), version=(1, 2))
+ listener = Listener.for_device(device=device, version=(1, 2))
listener.on(
'connection', lambda protocol: on_avdtp_connection(read, protocol)
)
diff --git a/examples/run_asha_sink.py b/examples/run_asha_sink.py
index 3e4955d..2d6f0d5 100644
--- a/examples/run_asha_sink.py
+++ b/examples/run_asha_sink.py
@@ -21,6 +21,7 @@
import os
import logging
+from bumble import l2cap
from bumble.core import AdvertisingData
from bumble.device import Device
from bumble.transport import open_transport_or_link
@@ -95,8 +96,10 @@
channel.sink = on_data
- psm = device.register_l2cap_channel_server(0, on_coc, 8)
- print(f'### LE_PSM_OUT = {psm}')
+ server = device.create_l2cap_server(
+ spec=l2cap.LeCreditBasedChannelSpec(max_credits=8), handler=on_coc
+ )
+ print(f'### LE_PSM_OUT = {server.psm}')
# Add the ASHA service to the GATT server
read_only_properties_characteristic = Characteristic(
@@ -147,7 +150,7 @@
ASHA_LE_PSM_OUT_CHARACTERISTIC,
Characteristic.Properties.READ,
Characteristic.READABLE,
- struct.pack('<H', psm),
+ struct.pack('<H', server.psm),
)
device.add_service(
Service(
diff --git a/examples/run_hid_host.py b/examples/run_hid_host.py
new file mode 100644
index 0000000..a174444
--- /dev/null
+++ b/examples/run_hid_host.py
@@ -0,0 +1,540 @@
+# Copyright 2021-2022 Google LLC
+#
+# 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
+#
+# https://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.
+
+# -----------------------------------------------------------------------------
+# Imports
+# -----------------------------------------------------------------------------
+import asyncio
+import sys
+import os
+import logging
+
+from bumble.colors import color
+
+import bumble.core
+from bumble.device import Device
+from bumble.transport import open_transport_or_link
+from bumble.core import (
+ BT_L2CAP_PROTOCOL_ID,
+ BT_HIDP_PROTOCOL_ID,
+ BT_HUMAN_INTERFACE_DEVICE_SERVICE,
+ BT_BR_EDR_TRANSPORT,
+)
+from bumble.hci import Address
+from bumble.hid import Host, Message
+from bumble.sdp import (
+ Client as SDP_Client,
+ DataElement,
+ ServiceAttribute,
+ SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+ SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID,
+ SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+ SDP_ALL_ATTRIBUTES_RANGE,
+ SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID,
+ SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID,
+ SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID,
+ SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID,
+)
+from hid_report_parser import ReportParser
+
+# -----------------------------------------------------------------------------
+# SDP attributes for Bluetooth HID devices
+SDP_HID_SERVICE_NAME_ATTRIBUTE_ID = 0x0100
+SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID = 0x0101
+SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID = 0x0102
+SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID = 0x0200 # [DEPRECATED]
+SDP_HID_PARSER_VERSION_ATTRIBUTE_ID = 0x0201
+SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID = 0x0202
+SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID = 0x0203
+SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID = 0x0204
+SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID = 0x0205
+SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID = 0x0206
+SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID = 0x0207
+SDP_HID_SDP_DISABLE_ATTRIBUTE_ID = 0x0208 # [DEPRECATED]
+SDP_HID_BATTERY_POWER_ATTRIBUTE_ID = 0x0209
+SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID = 0x020A
+SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID = 0x020B # DEPRECATED]
+SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID = 0x020C
+SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID = 0x020D
+SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID = 0x020E
+SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID = 0x020F
+SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID = 0x0210
+
+
+# -----------------------------------------------------------------------------
+
+
+async def get_hid_device_sdp_record(device, connection):
+
+ # Connect to the SDP Server
+ sdp_client = SDP_Client(device)
+ await sdp_client.connect(connection)
+ if sdp_client:
+ print(color('Connected to SDP Server', 'blue'))
+ else:
+ print(color('Failed to connect to SDP Server', 'red'))
+
+ # List BT HID Device service in the root browse group
+ service_record_handles = await sdp_client.search_services(
+ [BT_HUMAN_INTERFACE_DEVICE_SERVICE]
+ )
+
+ if len(service_record_handles) < 1:
+ await sdp_client.disconnect()
+ raise Exception(
+ color(f'BT HID Device service not found on peer device!!!!', 'red')
+ )
+
+ # For BT_HUMAN_INTERFACE_DEVICE_SERVICE service, get all its attributes
+ for service_record_handle in service_record_handles:
+ attributes = await sdp_client.get_attributes(
+ service_record_handle, [SDP_ALL_ATTRIBUTES_RANGE]
+ )
+ print(color(f'SERVICE {service_record_handle:04X} attributes:', 'yellow'))
+ print(color(f'SDP attributes for HID device', 'magenta'))
+ for attribute in attributes:
+ if attribute.id == SDP_SERVICE_RECORD_HANDLE_ATTRIBUTE_ID:
+ print(
+ color(' Service Record Handle : ', 'cyan'),
+ hex(attribute.value.value),
+ )
+
+ elif attribute.id == SDP_SERVICE_CLASS_ID_LIST_ATTRIBUTE_ID:
+ print(
+ color(' Service Class : ', 'cyan'), attribute.value.value[0].value
+ )
+
+ elif attribute.id == SDP_BROWSE_GROUP_LIST_ATTRIBUTE_ID:
+ print(
+ color(' SDP Browse Group List : ', 'cyan'),
+ attribute.value.value[0].value,
+ )
+
+ elif attribute.id == SDP_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
+ print(
+ color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'),
+ attribute.value.value[0].value[0].value,
+ )
+ print(
+ color(' PSM for Bluetooth HID Control channel : ', 'cyan'),
+ hex(attribute.value.value[0].value[1].value),
+ )
+ print(
+ color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'),
+ attribute.value.value[1].value[0].value,
+ )
+
+ elif attribute.id == SDP_LANGUAGE_BASE_ATTRIBUTE_ID_LIST_ATTRIBUTE_ID:
+ print(
+ color(' Lanugage : ', 'cyan'), hex(attribute.value.value[0].value)
+ )
+ print(
+ color(' Encoding : ', 'cyan'), hex(attribute.value.value[1].value)
+ )
+ print(
+ color(' PrimaryLanguageBaseID : ', 'cyan'),
+ hex(attribute.value.value[2].value),
+ )
+
+ elif attribute.id == SDP_BLUETOOTH_PROFILE_DESCRIPTOR_LIST_ATTRIBUTE_ID:
+ print(
+ color(' BT_HUMAN_INTERFACE_DEVICE_SERVICE ', 'cyan'),
+ attribute.value.value[0].value[0].value,
+ )
+ print(
+ color(' HID Profileversion number : ', 'cyan'),
+ hex(attribute.value.value[0].value[1].value),
+ )
+
+ elif attribute.id == SDP_ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST_ATTRIBUTE_ID:
+ print(
+ color(' BT_L2CAP_PROTOCOL_ID : ', 'cyan'),
+ attribute.value.value[0].value[0].value[0].value,
+ )
+ print(
+ color(' PSM for Bluetooth HID Interrupt channel : ', 'cyan'),
+ hex(attribute.value.value[0].value[0].value[1].value),
+ )
+ print(
+ color(' BT_HIDP_PROTOCOL_ID : ', 'cyan'),
+ attribute.value.value[0].value[1].value[0].value,
+ )
+
+ elif attribute.id == SDP_HID_SERVICE_NAME_ATTRIBUTE_ID:
+ print(color(' Service Name: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_SERVICE_DESCRIPTION_ATTRIBUTE_ID:
+ print(color(' Service Description: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_PROVIDER_NAME_ATTRIBUTE_ID:
+ print(color(' Provider Name: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_DEVICE_RELEASE_NUMBER_ATTRIBUTE_ID:
+ print(color(' Release Number: ', 'cyan'), hex(attribute.value.value))
+
+ elif attribute.id == SDP_HID_PARSER_VERSION_ATTRIBUTE_ID:
+ print(
+ color(' HID Parser Version: ', 'cyan'), hex(attribute.value.value)
+ )
+
+ elif attribute.id == SDP_HID_DEVICE_SUBCLASS_ATTRIBUTE_ID:
+ print(
+ color(' HIDDeviceSubclass: ', 'cyan'), hex(attribute.value.value)
+ )
+
+ elif attribute.id == SDP_HID_COUNTRY_CODE_ATTRIBUTE_ID:
+ print(color(' HIDCountryCode: ', 'cyan'), hex(attribute.value.value))
+
+ elif attribute.id == SDP_HID_VIRTUAL_CABLE_ATTRIBUTE_ID:
+ print(color(' HIDVirtualCable: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_RECONNECT_INITIATE_ATTRIBUTE_ID:
+ print(color(' HIDReconnectInitiate: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_DESCRIPTOR_LIST_ATTRIBUTE_ID:
+ print(
+ color(' HID Report Descriptor type: ', 'cyan'),
+ hex(attribute.value.value[0].value[0].value),
+ )
+ print(
+ color(' HID Report DescriptorList: ', 'cyan'),
+ attribute.value.value[0].value[1].value,
+ )
+
+ elif attribute.id == SDP_HID_LANGID_BASE_LIST_ATTRIBUTE_ID:
+ print(
+ color(' HID LANGID Base Language: ', 'cyan'),
+ hex(attribute.value.value[0].value[0].value),
+ )
+ print(
+ color(' HID LANGID Base Bluetooth String Offset: ', 'cyan'),
+ hex(attribute.value.value[0].value[1].value),
+ )
+
+ elif attribute.id == SDP_HID_BATTERY_POWER_ATTRIBUTE_ID:
+ print(color(' HIDBatteryPower: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_REMOTE_WAKE_ATTRIBUTE_ID:
+ print(color(' HIDRemoteWake: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_PROFILE_VERSION_ATTRIBUTE_ID:
+ print(
+ color(' HIDProfileVersion : ', 'cyan'), hex(attribute.value.value)
+ )
+
+ elif attribute.id == SDP_HID_SUPERVISION_TIMEOUT_ATTRIBUTE_ID:
+ print(
+ color(' HIDSupervisionTimeout: ', 'cyan'),
+ hex(attribute.value.value),
+ )
+
+ elif attribute.id == SDP_HID_NORMALLY_CONNECTABLE_ATTRIBUTE_ID:
+ print(
+ color(' HIDNormallyConnectable: ', 'cyan'), attribute.value.value
+ )
+
+ elif attribute.id == SDP_HID_BOOT_DEVICE_ATTRIBUTE_ID:
+ print(color(' HIDBootDevice: ', 'cyan'), attribute.value.value)
+
+ elif attribute.id == SDP_HID_SSR_HOST_MAX_LATENCY_ATTRIBUTE_ID:
+ print(
+ color(' HIDSSRHostMaxLatency: ', 'cyan'),
+ hex(attribute.value.value),
+ )
+
+ elif attribute.id == SDP_HID_SSR_HOST_MIN_TIMEOUT_ATTRIBUTE_ID:
+ print(
+ color(' HIDSSRHostMinTimeout: ', 'cyan'),
+ hex(attribute.value.value),
+ )
+
+ else:
+ print(
+ color(
+ f' Warning: Attribute ID: {attribute.id} match not found.\n Attribute Info: {attribute}',
+ 'yellow',
+ )
+ )
+
+ await sdp_client.disconnect()
+
+
+# -----------------------------------------------------------------------------
+async def get_stream_reader(pipe) -> asyncio.StreamReader:
+ loop = asyncio.get_event_loop()
+ reader = asyncio.StreamReader(loop=loop)
+ protocol = asyncio.StreamReaderProtocol(reader)
+ await loop.connect_read_pipe(lambda: protocol, pipe)
+ return reader
+
+
+# -----------------------------------------------------------------------------
+async def main():
+ if len(sys.argv) < 4:
+ print(
+ 'Usage: run_hid_host.py <device-config> <transport-spec> '
+ '<bluetooth-address> [test-mode]'
+ )
+
+ print('example: run_hid_host.py classic1.json usb:0 E1:CA:72:48:C4:E8/P')
+ return
+
+ def on_hid_data_cb(pdu):
+ report_type = pdu[0] & 0x0F
+ if len(pdu) == 1:
+ print(color(f'Warning: No report received', 'yellow'))
+ return
+ report_length = len(pdu[1:])
+ report_id = pdu[1]
+
+ if report_type != Message.ReportType.OTHER_REPORT:
+ print(
+ color(
+ f' Report type = {report_type}, Report length = {report_length}, Report id = {report_id}',
+ 'blue',
+ None,
+ 'bold',
+ )
+ )
+
+ if (report_length <= 1) or (report_id == 0):
+ return
+
+ if report_type == Message.ReportType.INPUT_REPORT:
+ ReportParser.parse_input_report(pdu[1:]) # type: ignore
+
+ async def handle_virtual_cable_unplug():
+ await hid_host.disconnect_interrupt_channel()
+ await hid_host.disconnect_control_channel()
+ await device.keystore.delete(target_address) # type: ignore
+ await connection.disconnect()
+
+ def on_hid_virtual_cable_unplug_cb():
+ asyncio.create_task(handle_virtual_cable_unplug())
+
+ print('<<< connecting to HCI...')
+ async with await open_transport_or_link(sys.argv[2]) as (hci_source, hci_sink):
+ print('<<< CONNECTED')
+
+ # Create a device
+ device = Device.from_config_file_with_hci(sys.argv[1], hci_source, hci_sink)
+ device.classic_enabled = True
+ await device.power_on()
+
+ # Connect to a peer
+ target_address = sys.argv[3]
+ print(f'=== Connecting to {target_address}...')
+ connection = await device.connect(target_address, transport=BT_BR_EDR_TRANSPORT)
+ print(f'=== Connected to {connection.peer_address}!')
+
+ # Request authentication
+ print('*** Authenticating...')
+ await connection.authenticate()
+ print('*** Authenticated...')
+
+ # Enable encryption
+ print('*** Enabling encryption...')
+ await connection.encrypt()
+ print('*** Encryption on')
+
+ await get_hid_device_sdp_record(device, connection)
+
+ # Create HID host and start it
+ print('@@@ Starting HID Host...')
+ hid_host = Host(device, connection)
+
+ # Register for HID data call back
+ hid_host.on('data', on_hid_data_cb)
+
+ # Register for virtual cable unplug call back
+ hid_host.on('virtual_cable_unplug', on_hid_virtual_cable_unplug_cb)
+
+ async def menu():
+ reader = await get_stream_reader(sys.stdin)
+ while True:
+ print(
+ "\n************************ HID Host Menu *****************************\n"
+ )
+ print(" 1. Connect Control Channel")
+ print(" 2. Connect Interrupt Channel")
+ print(" 3. Disconnect Control Channel")
+ print(" 4. Disconnect Interrupt Channel")
+ print(" 5. Get Report")
+ print(" 6. Set Report")
+ print(" 7. Set Protocol Mode")
+ print(" 8. Get Protocol Mode")
+ print(" 9. Send Report")
+ print("10. Suspend")
+ print("11. Exit Suspend")
+ print("12. Virtual Cable Unplug")
+ print("13. Disconnect device")
+ print("14. Delete Bonding")
+ print("15. Re-connect to device")
+ print("\nEnter your choice : \n")
+
+ choice = await reader.readline()
+ choice = choice.decode('utf-8').strip()
+
+ if choice == '1':
+ await hid_host.connect_control_channel()
+
+ elif choice == '2':
+ await hid_host.connect_interrupt_channel()
+
+ elif choice == '3':
+ await hid_host.disconnect_control_channel()
+
+ elif choice == '4':
+ await hid_host.disconnect_interrupt_channel()
+
+ elif choice == '5':
+ print(" 1. Report ID 0x02")
+ print(" 2. Report ID 0x03")
+ print(" 3. Report ID 0x05")
+ choice1 = await reader.readline()
+ choice1 = choice1.decode('utf-8').strip()
+
+ if choice1 == '1':
+ hid_host.get_report(1, 2, 3)
+
+ elif choice1 == '2':
+ hid_host.get_report(2, 3, 2)
+
+ elif choice1 == '3':
+ hid_host.get_report(3, 5, 3)
+
+ else:
+ print('Incorrect option selected')
+
+ elif choice == '6':
+ print(" 1. Report type 1 and Report id 0x01")
+ print(" 2. Report type 2 and Report id 0x03")
+ print(" 3. Report type 3 and Report id 0x05")
+ choice1 = await reader.readline()
+ choice1 = choice1.decode('utf-8').strip()
+
+ if choice1 == '1':
+ # data includes first octet as report id
+ data = bytearray(
+ [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01]
+ )
+ hid_host.set_report(1, data)
+
+ elif choice1 == '2':
+ data = bytearray([0x03, 0x01, 0x01])
+ hid_host.set_report(2, data)
+
+ elif choice1 == '3':
+ data = bytearray([0x05, 0x01, 0x01, 0x01])
+ hid_host.set_report(3, data)
+
+ else:
+ print('Incorrect option selected')
+
+ elif choice == '7':
+ print(" 0. Boot")
+ print(" 1. Report")
+ choice1 = await reader.readline()
+ choice1 = choice1.decode('utf-8').strip()
+
+ if choice1 == '0':
+ hid_host.set_protocol(Message.ProtocolMode.BOOT_PROTOCOL)
+
+ elif choice1 == '1':
+ hid_host.set_protocol(Message.ProtocolMode.REPORT_PROTOCOL)
+
+ else:
+ print('Incorrect option selected')
+
+ elif choice == '8':
+ hid_host.get_protocol()
+
+ elif choice == '9':
+ print(" 1. Report ID 0x01")
+ print(" 2. Report ID 0x03")
+ choice1 = await reader.readline()
+ choice1 = choice1.decode('utf-8').strip()
+
+ if choice1 == '1':
+ data = bytearray(
+ [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
+ )
+ hid_host.send_data(data)
+
+ elif choice1 == '2':
+ data = bytearray([0x03, 0x00, 0x0D, 0xFD, 0x00, 0x00])
+ hid_host.send_data(data)
+
+ else:
+ print('Incorrect option selected')
+
+ elif choice == '10':
+ hid_host.suspend()
+
+ elif choice == '11':
+ hid_host.exit_suspend()
+
+ elif choice == '12':
+ hid_host.virtual_cable_unplug()
+ try:
+ await device.keystore.delete(target_address)
+ except KeyError:
+ print('Device not found or Device already unpaired.')
+
+ elif choice == '13':
+ peer_address = Address.from_string_for_transport(
+ target_address, transport=BT_BR_EDR_TRANSPORT
+ )
+ connection = device.find_connection_by_bd_addr(
+ peer_address, transport=BT_BR_EDR_TRANSPORT
+ )
+ if connection is not None:
+ await connection.disconnect()
+ else:
+ print("Already disconnected from device")
+
+ elif choice == '14':
+ try:
+ await device.keystore.delete(target_address)
+ print("Unpair successful")
+ except KeyError:
+ print('Device not found or Device already unpaired.')
+
+ elif choice == '15':
+ connection = await device.connect(
+ target_address, transport=BT_BR_EDR_TRANSPORT
+ )
+ await connection.authenticate()
+ await connection.encrypt()
+
+ else:
+ print("Invalid option selected.")
+
+ if (len(sys.argv) > 4) and (sys.argv[4] == 'test-mode'):
+ # Enabling menu for testing
+ await menu()
+ else:
+ # HID Connection
+ # Control channel
+ await hid_host.connect_control_channel()
+ # Interrupt Channel
+ await hid_host.connect_interrupt_channel()
+
+ await hci_source.wait_for_termination()
+
+
+# -----------------------------------------------------------------------------
+
+logging.basicConfig(level=os.environ.get('BUMBLE_LOGLEVEL', 'DEBUG').upper())
+asyncio.run(main())
diff --git a/extras/android/RemoteHCI/.gitignore b/extras/android/RemoteHCI/.gitignore
new file mode 100644
index 0000000..10cfdbf
--- /dev/null
+++ b/extras/android/RemoteHCI/.gitignore
@@ -0,0 +1,10 @@
+*.iml
+.gradle
+/local.properties
+/.idea
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/extras/android/RemoteHCI/app/.gitignore b/extras/android/RemoteHCI/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/extras/android/RemoteHCI/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/build.gradle.kts b/extras/android/RemoteHCI/app/build.gradle.kts
new file mode 100644
index 0000000..2e2df38
--- /dev/null
+++ b/extras/android/RemoteHCI/app/build.gradle.kts
@@ -0,0 +1,73 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+ alias(libs.plugins.androidApplication)
+ alias(libs.plugins.kotlinAndroid)
+}
+
+android {
+ namespace = "com.github.google.bumble.remotehci"
+ compileSdk = 33
+
+ defaultConfig {
+ applicationId = "com.github.google.bumble.remotehci"
+ minSdk = 26
+ targetSdk = 33
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables {
+ useSupportLibrary = true
+ }
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+ buildFeatures {
+ compose = true
+ aidl = false
+ }
+ composeOptions {
+ kotlinCompilerExtensionVersion = "1.4.3"
+ }
+ packaging {
+ resources {
+ excludes += "/META-INF/{AL2.0,LGPL2.1}"
+ }
+ }
+}
+
+dependencies {
+ implementation(kotlin("reflect"))
+ implementation(libs.core.ktx)
+ implementation(libs.lifecycle.runtime.ktx)
+ implementation(libs.activity.compose)
+ implementation(platform(libs.compose.bom))
+ implementation(libs.ui)
+ implementation(libs.ui.graphics)
+ implementation(libs.ui.tooling.preview)
+ implementation(libs.material3)
+ compileOnly(project(":lib"))
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.ext.junit)
+ androidTestImplementation(libs.espresso.core)
+ androidTestImplementation(platform(libs.compose.bom))
+ androidTestImplementation(libs.ui.test.junit4)
+ debugImplementation(libs.ui.tooling)
+ debugImplementation(libs.ui.test.manifest)
+ //compileOnly(files("${project.rootDir.absolutePath}/sdk/framework.jar"))
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/proguard-rules.pro b/extras/android/RemoteHCI/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/extras/android/RemoteHCI/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/AndroidManifest.xml b/extras/android/RemoteHCI/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2cca4ad
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools">
+
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.RemoteHCI"
+ tools:targetApi="31">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:label="@string/app_name"
+ android:theme="@style/Theme.RemoteHCI">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/IBluetoothHci.aidl b/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/IBluetoothHci.aidl
new file mode 100644
index 0000000..92feaa5
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/IBluetoothHci.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.bluetooth;
+@VintfStability
+interface IBluetoothHci {
+ void close();
+ void initialize(in android.hardware.bluetooth.IBluetoothHciCallbacks callback);
+ void sendAclData(in byte[] data);
+ void sendHciCommand(in byte[] command);
+ void sendIsoData(in byte[] data);
+ void sendScoData(in byte[] data);
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl b/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl
new file mode 100644
index 0000000..f0d8c29
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2022 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.bluetooth;
+@VintfStability
+interface IBluetoothHciCallbacks {
+ void aclDataReceived(in byte[] data);
+ void hciEventReceived(in byte[] event);
+ void initializationComplete(in android.hardware.bluetooth.Status status);
+ void isoDataReceived(in byte[] data);
+ void scoDataReceived(in byte[] data);
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/Status.aidl b/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/Status.aidl
new file mode 100644
index 0000000..f3ba792
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/aidl/android/hardware/bluetooth/Status.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.bluetooth;
+@Backing(type="int")
+@VintfStability
+enum Status {
+ SUCCESS = 0,
+ ALREADY_INITIALIZED = 1,
+ UNABLE_TO_OPEN_INTERFACE = 2,
+ HARDWARE_INITIALIZATION_ERROR = 3,
+ UNKNOWN = 4,
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/hidl/bluetooth/1.1/IBluetoothHci.hal b/extras/android/RemoteHCI/app/src/main/hidl/bluetooth/1.1/IBluetoothHci.hal
new file mode 100644
index 0000000..b7845d5
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/hidl/bluetooth/1.1/IBluetoothHci.hal
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package [email protected];
+
+import @1.0::HciPacket;
+import @1.0::IBluetoothHci;
+import IBluetoothHciCallbacks;
+
+/**
+ * The Host Controller Interface (HCI) is the layer defined by the Bluetooth
+ * specification between the software that runs on the host and the Bluetooth
+ * controller chip. This boundary is the natural choice for a Hardware
+ * Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
+ * the stack and abstracts away power management, initialization, and other
+ * implementation-specific details related to the hardware.
+ */
+interface IBluetoothHci extends @1.0::IBluetoothHci {
+ /**
+ * Same as @1.0, but uses 1.1 Callbacks version
+ */
+ initialize_1_1(@1.1::IBluetoothHciCallbacks callback);
+
+ /**
+ * Send an ISO data packet (as specified in the Bluetooth Core
+ * Specification v5.2) to the Bluetooth controller.
+ * Packets must be processed in order.
+ * @param data HCI data packet to be sent
+ */
+ sendIsoData(HciPacket data);
+};
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/hidl/bluetooth/1.1/IBluetoothHciCallbacks.hal b/extras/android/RemoteHCI/app/src/main/hidl/bluetooth/1.1/IBluetoothHciCallbacks.hal
new file mode 100644
index 0000000..b8d0b8a
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/hidl/bluetooth/1.1/IBluetoothHciCallbacks.hal
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package [email protected];
+
+import @1.0::HciPacket;
+import @1.0::IBluetoothHciCallbacks;
+
+/**
+ * The interface from the Bluetooth Controller to the stack.
+ */
+interface IBluetoothHciCallbacks extends @1.0::IBluetoothHciCallbacks {
+ /**
+ * Send a ISO data packet form the controller to the host.
+ * @param data the ISO HCI packet to be passed to the host stack
+ */
+ isoDataReceived(HciPacket data);
+};
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/ic_launcher-playstore.png b/extras/android/RemoteHCI/app/src/main/ic_launcher-playstore.png
new file mode 100644
index 0000000..b0a2f40
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/ic_launcher-playstore.png
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/IBluetoothHci.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/IBluetoothHci.java
new file mode 100644
index 0000000..c235137
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/IBluetoothHci.java
@@ -0,0 +1,259 @@
+/*
+ * This file is auto-generated. DO NOT MODIFY.
+ */
+package android.hardware.bluetooth;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public interface IBluetoothHci extends android.os.IInterface
+{
+ /** Default implementation for IBluetoothHci. */
+ public static class Default implements android.hardware.bluetooth.IBluetoothHci
+ {
+ @Override public void close() throws android.os.RemoteException
+ {
+ }
+ @Override public void initialize(android.hardware.bluetooth.IBluetoothHciCallbacks callback) throws android.os.RemoteException
+ {
+ }
+ @Override public void sendAclData(byte[] data) throws android.os.RemoteException
+ {
+ }
+ @Override public void sendHciCommand(byte[] command) throws android.os.RemoteException
+ {
+ }
+ @Override public void sendIsoData(byte[] data) throws android.os.RemoteException
+ {
+ }
+ @Override public void sendScoData(byte[] data) throws android.os.RemoteException
+ {
+ }
+ @Override
+ public android.os.IBinder asBinder() {
+ return null;
+ }
+ }
+ /** Local-side IPC implementation stub class. */
+ public static abstract class Stub extends android.os.Binder implements android.hardware.bluetooth.IBluetoothHci
+ {
+ /** Construct the stub at attach it to the interface. */
+ public Stub()
+ {
+ //this.markVintfStability();
+ try {
+ Method method = this.getClass().getMethod("markVintfStability", (Class<?>[])null);
+ method.invoke(this);
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ this.attachInterface(this, DESCRIPTOR);
+ }
+ /**
+ * Cast an IBinder object into an android.hardware.bluetooth.IBluetoothHci interface,
+ * generating a proxy if needed.
+ */
+ public static android.hardware.bluetooth.IBluetoothHci asInterface(android.os.IBinder obj)
+ {
+ if ((obj==null)) {
+ return null;
+ }
+ android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+ if (((iin!=null)&&(iin instanceof android.hardware.bluetooth.IBluetoothHci))) {
+ return ((android.hardware.bluetooth.IBluetoothHci)iin);
+ }
+ return new android.hardware.bluetooth.IBluetoothHci.Stub.Proxy(obj);
+ }
+ @Override public android.os.IBinder asBinder()
+ {
+ return this;
+ }
+ @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
+ {
+ java.lang.String descriptor = DESCRIPTOR;
+ if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
+ data.enforceInterface(descriptor);
+ }
+ switch (code)
+ {
+ case INTERFACE_TRANSACTION:
+ {
+ reply.writeString(descriptor);
+ return true;
+ }
+ }
+ switch (code)
+ {
+ case TRANSACTION_close:
+ {
+ this.close();
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_initialize:
+ {
+ android.hardware.bluetooth.IBluetoothHciCallbacks _arg0;
+ _arg0 = android.hardware.bluetooth.IBluetoothHciCallbacks.Stub.asInterface(data.readStrongBinder());
+ this.initialize(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_sendAclData:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.sendAclData(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_sendHciCommand:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.sendHciCommand(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_sendIsoData:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.sendIsoData(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_sendScoData:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.sendScoData(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ default:
+ {
+ return super.onTransact(code, data, reply, flags);
+ }
+ }
+ return true;
+ }
+ private static class Proxy implements android.hardware.bluetooth.IBluetoothHci
+ {
+ private android.os.IBinder mRemote;
+ Proxy(android.os.IBinder remote)
+ {
+ mRemote = remote;
+ }
+ @Override public android.os.IBinder asBinder()
+ {
+ return mRemote;
+ }
+ public java.lang.String getInterfaceDescriptor()
+ {
+ return DESCRIPTOR;
+ }
+ @Override public void close() throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_close, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void initialize(android.hardware.bluetooth.IBluetoothHciCallbacks callback) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeStrongInterface(callback);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_initialize, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void sendAclData(byte[] data) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(data);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_sendAclData, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void sendHciCommand(byte[] command) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(command);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_sendHciCommand, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void sendIsoData(byte[] data) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(data);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_sendIsoData, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void sendScoData(byte[] data) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(data);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_sendScoData, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ }
+ static final int TRANSACTION_close = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
+ static final int TRANSACTION_initialize = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
+ static final int TRANSACTION_sendAclData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
+ static final int TRANSACTION_sendHciCommand = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
+ static final int TRANSACTION_sendIsoData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
+ static final int TRANSACTION_sendScoData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
+ }
+ public static final java.lang.String DESCRIPTOR = "android$hardware$bluetooth$IBluetoothHci".replace('$', '.');
+ public void close() throws android.os.RemoteException;
+ public void initialize(android.hardware.bluetooth.IBluetoothHciCallbacks callback) throws android.os.RemoteException;
+ public void sendAclData(byte[] data) throws android.os.RemoteException;
+ public void sendHciCommand(byte[] command) throws android.os.RemoteException;
+ public void sendIsoData(byte[] data) throws android.os.RemoteException;
+ public void sendScoData(byte[] data) throws android.os.RemoteException;
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/IBluetoothHciCallbacks.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/IBluetoothHciCallbacks.java
new file mode 100644
index 0000000..61a70ad
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/IBluetoothHciCallbacks.java
@@ -0,0 +1,234 @@
+/*
+ * This file is auto-generated. DO NOT MODIFY.
+ */
+package android.hardware.bluetooth;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public interface IBluetoothHciCallbacks extends android.os.IInterface
+{
+ /** Default implementation for IBluetoothHciCallbacks. */
+ public static class Default implements android.hardware.bluetooth.IBluetoothHciCallbacks
+ {
+ @Override public void aclDataReceived(byte[] data) throws android.os.RemoteException
+ {
+ }
+ @Override public void hciEventReceived(byte[] event) throws android.os.RemoteException
+ {
+ }
+ @Override public void initializationComplete(int status) throws android.os.RemoteException
+ {
+ }
+ @Override public void isoDataReceived(byte[] data) throws android.os.RemoteException
+ {
+ }
+ @Override public void scoDataReceived(byte[] data) throws android.os.RemoteException
+ {
+ }
+ @Override
+ public android.os.IBinder asBinder() {
+ return null;
+ }
+ }
+ /** Local-side IPC implementation stub class. */
+ public static abstract class Stub extends android.os.Binder implements android.hardware.bluetooth.IBluetoothHciCallbacks
+ {
+ /** Construct the stub at attach it to the interface. */
+ public Stub()
+ {
+ //this.markVintfStability();
+ try {
+ Method method = this.getClass().getMethod("markVintfStability", (Class<?>[])null);
+ method.invoke(this);
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ this.attachInterface(this, DESCRIPTOR);
+ }
+ /**
+ * Cast an IBinder object into an android.hardware.bluetooth.IBluetoothHciCallbacks interface,
+ * generating a proxy if needed.
+ */
+ public static android.hardware.bluetooth.IBluetoothHciCallbacks asInterface(android.os.IBinder obj)
+ {
+ if ((obj==null)) {
+ return null;
+ }
+ android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+ if (((iin!=null)&&(iin instanceof android.hardware.bluetooth.IBluetoothHciCallbacks))) {
+ return ((android.hardware.bluetooth.IBluetoothHciCallbacks)iin);
+ }
+ return new android.hardware.bluetooth.IBluetoothHciCallbacks.Stub.Proxy(obj);
+ }
+ @Override public android.os.IBinder asBinder()
+ {
+ return this;
+ }
+ @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
+ {
+ java.lang.String descriptor = DESCRIPTOR;
+ if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
+ data.enforceInterface(descriptor);
+ }
+ switch (code)
+ {
+ case INTERFACE_TRANSACTION:
+ {
+ reply.writeString(descriptor);
+ return true;
+ }
+ }
+ switch (code)
+ {
+ case TRANSACTION_aclDataReceived:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.aclDataReceived(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_hciEventReceived:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.hciEventReceived(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_initializationComplete:
+ {
+ int _arg0;
+ _arg0 = data.readInt();
+ this.initializationComplete(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_isoDataReceived:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.isoDataReceived(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ case TRANSACTION_scoDataReceived:
+ {
+ byte[] _arg0;
+ _arg0 = data.createByteArray();
+ this.scoDataReceived(_arg0);
+ reply.writeNoException();
+ break;
+ }
+ default:
+ {
+ return super.onTransact(code, data, reply, flags);
+ }
+ }
+ return true;
+ }
+ private static class Proxy implements android.hardware.bluetooth.IBluetoothHciCallbacks
+ {
+ private android.os.IBinder mRemote;
+ Proxy(android.os.IBinder remote)
+ {
+ mRemote = remote;
+ }
+ @Override public android.os.IBinder asBinder()
+ {
+ return mRemote;
+ }
+ public java.lang.String getInterfaceDescriptor()
+ {
+ return DESCRIPTOR;
+ }
+ @Override public void aclDataReceived(byte[] data) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(data);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_aclDataReceived, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void hciEventReceived(byte[] event) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(event);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_hciEventReceived, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void initializationComplete(int status) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(status);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_initializationComplete, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void isoDataReceived(byte[] data) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(data);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_isoDataReceived, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ @Override public void scoDataReceived(byte[] data) throws android.os.RemoteException
+ {
+ android.os.Parcel _data = android.os.Parcel.obtain();
+ android.os.Parcel _reply = android.os.Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeByteArray(data);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_scoDataReceived, _data, _reply, 0);
+ _reply.readException();
+ }
+ finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+ }
+ static final int TRANSACTION_aclDataReceived = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
+ static final int TRANSACTION_hciEventReceived = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
+ static final int TRANSACTION_initializationComplete = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
+ static final int TRANSACTION_isoDataReceived = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
+ static final int TRANSACTION_scoDataReceived = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
+ }
+ public static final java.lang.String DESCRIPTOR = "android$hardware$bluetooth$IBluetoothHciCallbacks".replace('$', '.');
+ public void aclDataReceived(byte[] data) throws android.os.RemoteException;
+ public void hciEventReceived(byte[] event) throws android.os.RemoteException;
+ public void initializationComplete(int status) throws android.os.RemoteException;
+ public void isoDataReceived(byte[] data) throws android.os.RemoteException;
+ public void scoDataReceived(byte[] data) throws android.os.RemoteException;
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/Status.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/Status.java
new file mode 100644
index 0000000..710d6c9
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/Status.java
@@ -0,0 +1,11 @@
+/*
+ * This file is auto-generated. DO NOT MODIFY.
+ */
+package android.hardware.bluetooth;
+public @interface Status {
+ public static final int SUCCESS = 0;
+ public static final int ALREADY_INITIALIZED = 1;
+ public static final int UNABLE_TO_OPEN_INTERFACE = 2;
+ public static final int HARDWARE_INITIALIZATION_ERROR = 3;
+ public static final int UNKNOWN = 4;
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/IBluetoothHci.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/IBluetoothHci.java
new file mode 100644
index 0000000..e7cd577
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/IBluetoothHci.java
@@ -0,0 +1,816 @@
+package android.hardware.bluetooth.V1_0;
+
+import android.os.HidlSupport;
+import android.os.HwBinder;
+import android.os.IHwBinder;
+import android.os.HwBlob;
+import android.os.HwParcel;
+import android.os.IHwInterface;
+import android.os.NativeHandle;
+
+/**
+ * The Host Controller Interface (HCI) is the layer defined by the Bluetooth
+ * specification between the software that runs on the host and the Bluetooth
+ * controller chip. This boundary is the natural choice for a Hardware
+ * Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
+ * the stack and abstracts away power management, initialization, and other
+ * implementation-specific details related to the hardware.
+ */
+public interface IBluetoothHci extends android.internal.hidl.base.V1_0.IBase {
+ /**
+ * Fully-qualified interface name for this interface.
+ */
+ public static final String kInterfaceName = "[email protected]::IBluetoothHci";
+
+ /**
+ * Does a checked conversion from a binder to this class.
+ */
+ /* package private */ static IBluetoothHci asInterface(IHwBinder binder) {
+ if (binder == null) {
+ return null;
+ }
+
+ IHwInterface iface =
+ binder.queryLocalInterface(kInterfaceName);
+
+ if ((iface != null) && (iface instanceof IBluetoothHci)) {
+ return (IBluetoothHci)iface;
+ }
+
+ IBluetoothHci proxy = new IBluetoothHci.Proxy(binder);
+
+ try {
+ for (String descriptor : proxy.interfaceChain()) {
+ if (descriptor.equals(kInterfaceName)) {
+ return proxy;
+ }
+ }
+ } catch (android.os.RemoteException e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Does a checked conversion from any interface to this class.
+ */
+ public static IBluetoothHci castFrom(IHwInterface iface) {
+ return (iface == null) ? null : IBluetoothHci.asInterface(iface.asBinder());
+ }
+
+ @Override
+ public IHwBinder asBinder();
+
+ /**
+ * This will invoke the equivalent of the C++ getService(std::string) if retry is
+ * true or tryGetService(std::string) if retry is false. If the service is
+ * available on the device and retry is true, this will wait for the service to
+ * start.
+ *
+ */
+ public static IBluetoothHci getService(String serviceName, boolean retry) throws android.os.RemoteException {
+ return IBluetoothHci.asInterface(HwBinder.getService("[email protected]::IBluetoothHci", serviceName, retry));
+ }
+
+ /**
+ * Calls getService("default",retry).
+ */
+ public static IBluetoothHci getService(boolean retry) throws android.os.RemoteException {
+ return getService("default", retry);
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(String,boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHci getService(String serviceName) throws android.os.RemoteException {
+ return IBluetoothHci.asInterface(HwBinder.getService("[email protected]::IBluetoothHci", serviceName));
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHci getService() throws android.os.RemoteException {
+ return getService("default");
+ }
+
+ /**
+ * Initialize the underlying HCI interface.
+ *
+ * This method should be used to initialize any hardware interfaces
+ * required to communicate with the Bluetooth hardware in the
+ * device.
+ *
+ * The |oninitializationComplete| callback must be invoked in response
+ * to this function to indicate success before any other function
+ * (sendHciCommand, sendAclData, * sendScoData) is invoked on this
+ * interface.
+ *
+ * @param callback implements IBluetoothHciCallbacks which will
+ * receive callbacks when incoming HCI packets are received
+ * from the controller to be sent to the host.
+ */
+ void initialize(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks callback)
+ throws android.os.RemoteException;
+ /**
+ * Send an HCI command (as specified in the Bluetooth Specification
+ * V4.2, Vol 2, Part 5, Section 5.4.1) to the Bluetooth controller.
+ * Commands must be executed in order.
+ *
+ * @param command is the HCI command to be sent
+ */
+ void sendHciCommand(java.util.ArrayList<Byte> command)
+ throws android.os.RemoteException;
+ /**
+ * Send an HCI ACL data packet (as specified in the Bluetooth Specification
+ * V4.2, Vol 2, Part 5, Section 5.4.2) to the Bluetooth controller.
+ * Packets must be processed in order.
+ * @param data HCI data packet to be sent
+ */
+ void sendAclData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException;
+ /**
+ * Send an SCO data packet (as specified in the Bluetooth Specification
+ * V4.2, Vol 2, Part 5, Section 5.4.3) to the Bluetooth controller.
+ * Packets must be processed in order.
+ * @param data HCI data packet to be sent
+ */
+ void sendScoData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException;
+ /**
+ * Close the HCI interface
+ */
+ void close()
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * ["[email protected]::IChild",
+ * "[email protected]::IParent"
+ * "[email protected]::IBase"]
+ *
+ * @return descriptors a vector of descriptors of the run-time type of the
+ * object.
+ */
+ java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException;
+ /*
+ * Emit diagnostic information to the given file.
+ *
+ * Optionally overriden.
+ *
+ * @param fd File descriptor to dump data to.
+ * Must only be used for the duration of this call.
+ * @param options Arguments for debugging.
+ * Must support empty for default debug information.
+ */
+ void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceDescriptor on an IChild object must yield
+ * "[email protected]::IChild"
+ *
+ * @return descriptor a descriptor of the run-time type of the
+ * object (the first element of the vector returned by
+ * interfaceChain())
+ */
+ String interfaceDescriptor()
+ throws android.os.RemoteException;
+ /*
+ * Returns hashes of the source HAL files that define the interfaces of the
+ * runtime type information on the object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * [(hash of IChild.hal),
+ * (hash of IParent.hal)
+ * (hash of IBase.hal)].
+ *
+ * SHA-256 is used as the hashing algorithm. Each hash has 32 bytes
+ * according to SHA-256 standard.
+ *
+ * @return hashchain a vector of SHA-1 digests
+ */
+ java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException;
+ /*
+ * This method trigger the interface to enable/disable instrumentation based
+ * on system property hal.instrumentation.enable.
+ */
+ void setHALInstrumentation()
+ throws android.os.RemoteException;
+ /*
+ * Registers a death recipient, to be called when the process hosting this
+ * interface dies.
+ *
+ * @param recipient a hidl_death_recipient callback object
+ * @param cookie a cookie that must be returned with the callback
+ * @return success whether the death recipient was registered successfully.
+ */
+ boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException;
+ /*
+ * Provides way to determine if interface is running without requesting
+ * any functionality.
+ */
+ void ping()
+ throws android.os.RemoteException;
+ /*
+ * Get debug information on references on this interface.
+ * @return info debugging information. See comments of DebugInfo.
+ */
+ android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException;
+ /*
+ * This method notifies the interface that one or more system properties
+ * have changed. The default implementation calls
+ * (C++) report_sysprop_change() in libcutils or
+ * (Java) android.os.SystemProperties.reportSyspropChanged,
+ * which in turn calls a set of registered callbacks (eg to update trace
+ * tags).
+ */
+ void notifySyspropsChanged()
+ throws android.os.RemoteException;
+ /*
+ * Unregisters the registered death recipient. If this service was registered
+ * multiple times with the same exact death recipient, this unlinks the most
+ * recently registered one.
+ *
+ * @param recipient a previously registered hidl_death_recipient callback
+ * @return success whether the death recipient was unregistered successfully.
+ */
+ boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException;
+
+ public static final class Proxy implements IBluetoothHci {
+ private IHwBinder mRemote;
+
+ public Proxy(IHwBinder remote) {
+ mRemote = java.util.Objects.requireNonNull(remote);
+ }
+
+ @Override
+ public IHwBinder asBinder() {
+ return mRemote;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return this.interfaceDescriptor() + "@Proxy";
+ } catch (android.os.RemoteException ex) {
+ /* ignored; handled below. */
+ }
+ return "[class or subclass of " + IBluetoothHci.kInterfaceName + "]@Proxy";
+ }
+
+ @Override
+ public final boolean equals(java.lang.Object other) {
+ return HidlSupport.interfacesEqual(this, other);
+ }
+
+ @Override
+ public final int hashCode() {
+ return this.asBinder().hashCode();
+ }
+
+ // Methods from ::android::hardware::bluetooth::V1_0::IBluetoothHci follow.
+ @Override
+ public void initialize(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks callback)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeStrongBinder(callback == null ? null : callback.asBinder());
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(1 /* initialize */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void sendHciCommand(java.util.ArrayList<Byte> command)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeInt8Vector(command);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(2 /* sendHciCommand */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void sendAclData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(3 /* sendAclData */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void sendScoData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(4 /* sendScoData */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void close()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(5 /* close */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ @Override
+ public java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256067662 /* interfaceChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<String> _hidl_out_descriptors = _hidl_reply.readStringVector();
+ return _hidl_out_descriptors;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+ _hidl_request.writeNativeHandle(fd);
+ _hidl_request.writeStringVector(options);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256131655 /* debug */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public String interfaceDescriptor()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256136003 /* interfaceDescriptor */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ String _hidl_out_descriptor = _hidl_reply.readString();
+ return _hidl_out_descriptor;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256398152 /* getHashChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = new java.util.ArrayList<byte[/* 32 */]>();
+ {
+ HwBlob _hidl_blob = _hidl_reply.readBuffer(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_blob.getInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */);
+ HwBlob childBlob = _hidl_reply.readEmbeddedBuffer(
+ _hidl_vec_size * 32,_hidl_blob.handle(),
+ 0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */,true /* nullable */);
+
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).clear();
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ byte[/* 32 */] _hidl_vec_element = new byte[32];
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ childBlob.copyToInt8Array(_hidl_array_offset_1, (byte[/* 32 */]) _hidl_vec_element, 32 /* size */);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).add(_hidl_vec_element);
+ }
+ }
+ }
+ return _hidl_out_hashchain;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void setHALInstrumentation()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256462420 /* setHALInstrumentation */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException {
+ return mRemote.linkToDeath(recipient, cookie);
+ }
+ @Override
+ public void ping()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256921159 /* ping */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257049926 /* getDebugInfo */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = new android.internal.hidl.base.V1_0.DebugInfo();
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).readFromParcel(_hidl_reply);
+ return _hidl_out_info;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void notifySyspropsChanged()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257120595 /* notifySyspropsChanged */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException {
+ return mRemote.unlinkToDeath(recipient);
+ }
+ }
+
+ public static abstract class Stub extends HwBinder implements IBluetoothHci {
+ @Override
+ public IHwBinder asBinder() {
+ return this;
+ }
+
+ @Override
+ public final java.util.ArrayList<String> interfaceChain() {
+ return new java.util.ArrayList<String>(java.util.Arrays.asList(
+ android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName,
+ android.internal.hidl.base.V1_0.IBase.kInterfaceName));
+
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options) {
+ return;
+
+ }
+
+ @Override
+ public final String interfaceDescriptor() {
+ return android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName;
+
+ }
+
+ @Override
+ public final java.util.ArrayList<byte[/* 32 */]> getHashChain() {
+ return new java.util.ArrayList<byte[/* 32 */]>(java.util.Arrays.asList(
+ new byte[/* 32 */]{52,124,-25,70,-127,86,7,86,127,95,59,83,-28,-128,9,-104,-54,90,-71,53,81,65,-16,-120,15,-64,-49,12,31,-59,-61,85} /* 347ce746815607567f5f3b53e4800998ca5ab9355141f0880fc0cf0c1fc5c355 */,
+ new byte[/* 32 */]{-20,127,-41,-98,-48,45,-6,-123,-68,73,-108,38,-83,-82,62,-66,35,-17,5,36,-13,-51,105,87,19,-109,36,-72,59,24,-54,76} /* ec7fd79ed02dfa85bc499426adae3ebe23ef0524f3cd6957139324b83b18ca4c */));
+
+ }
+
+ @Override
+ public final void setHALInstrumentation() {
+
+ }
+
+ @Override
+ public final boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
+ return true;
+
+ }
+
+ @Override
+ public final void ping() {
+ return;
+
+ }
+
+ @Override
+ public final android.internal.hidl.base.V1_0.DebugInfo getDebugInfo() {
+ android.internal.hidl.base.V1_0.DebugInfo info = new android.internal.hidl.base.V1_0.DebugInfo();
+ info.pid = HidlSupport.getPidIfSharable();
+ info.ptr = 0;
+ info.arch = android.internal.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;
+ return info;
+
+ }
+
+ @Override
+ public final void notifySyspropsChanged() {
+ HwBinder.enableInstrumentation();
+
+ }
+
+ @Override
+ public final boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
+ return true;
+
+ }
+
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ if (kInterfaceName.equals(descriptor)) {
+ return this;
+ }
+ return null;
+ }
+
+ public void registerAsService(String serviceName) throws android.os.RemoteException {
+ registerService(serviceName);
+ }
+
+ @Override
+ public String toString() {
+ return this.interfaceDescriptor() + "@Stub";
+ }
+
+ //@Override
+ public void onTransact(int _hidl_code, HwParcel _hidl_request, final HwParcel _hidl_reply, int _hidl_flags)
+ throws android.os.RemoteException {
+ switch (_hidl_code) {
+ case 1 /* initialize */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks callback = android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.asInterface(_hidl_request.readStrongBinder());
+ initialize(callback);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 2 /* sendHciCommand */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ java.util.ArrayList<Byte> command = _hidl_request.readInt8Vector();
+ sendHciCommand(command);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 3 /* sendAclData */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ sendAclData(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 4 /* sendScoData */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ sendScoData(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 5 /* close */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ close();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256067662 /* interfaceChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<String> _hidl_out_descriptors = interfaceChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeStringVector(_hidl_out_descriptors);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256131655 /* debug */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ NativeHandle fd = _hidl_request.readNativeHandle();
+ java.util.ArrayList<String> options = _hidl_request.readStringVector();
+ debug(fd, options);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256136003 /* interfaceDescriptor */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ String _hidl_out_descriptor = interfaceDescriptor();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeString(_hidl_out_descriptor);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256398152 /* getHashChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = getHashChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ {
+ HwBlob _hidl_blob = new HwBlob(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_out_hashchain.size();
+ _hidl_blob.putInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */, _hidl_vec_size);
+ _hidl_blob.putBool(0 /* offset */ + 12 /* offsetof(hidl_vec<T>, mOwnsBuffer) */, false);
+ HwBlob childBlob = new HwBlob((int)(_hidl_vec_size * 32));
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ byte[] _hidl_array_item_1 = (byte[/* 32 */]) _hidl_out_hashchain.get(_hidl_index_0);
+
+ if (_hidl_array_item_1 == null || _hidl_array_item_1.length != 32) {
+ throw new IllegalArgumentException("Array element is not of the expected length");
+ }
+
+ childBlob.putInt8Array(_hidl_array_offset_1, _hidl_array_item_1);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ }
+ _hidl_blob.putBlob(0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */, childBlob);
+ }
+ _hidl_reply.writeBuffer(_hidl_blob);
+ }
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256462420 /* setHALInstrumentation */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ setHALInstrumentation();
+ break;
+ }
+
+ case 256660548 /* linkToDeath */:
+ {
+ break;
+ }
+
+ case 256921159 /* ping */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ ping();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257049926 /* getDebugInfo */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = getDebugInfo();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).writeToParcel(_hidl_reply);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257120595 /* notifySyspropsChanged */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ notifySyspropsChanged();
+ break;
+ }
+
+ case 257250372 /* unlinkToDeath */:
+ {
+ break;
+ }
+
+ }
+ }
+ }
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/IBluetoothHciCallbacks.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/IBluetoothHciCallbacks.java
new file mode 100644
index 0000000..694fb72
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/IBluetoothHciCallbacks.java
@@ -0,0 +1,762 @@
+package android.hardware.bluetooth.V1_0;
+
+import android.os.HidlSupport;
+import android.os.HwBinder;
+import android.os.IHwBinder;
+import android.os.HwBlob;
+import android.os.HwParcel;
+import android.os.IHwInterface;
+import android.os.NativeHandle;
+
+/**
+ * The interface from the Bluetooth Controller to the stack.
+ */
+public interface IBluetoothHciCallbacks extends android.internal.hidl.base.V1_0.IBase {
+ /**
+ * Fully-qualified interface name for this interface.
+ */
+ public static final String kInterfaceName = "[email protected]::IBluetoothHciCallbacks";
+
+ /**
+ * Does a checked conversion from a binder to this class.
+ */
+ /* package private */ static IBluetoothHciCallbacks asInterface(IHwBinder binder) {
+ if (binder == null) {
+ return null;
+ }
+
+ IHwInterface iface =
+ binder.queryLocalInterface(kInterfaceName);
+
+ if ((iface != null) && (iface instanceof IBluetoothHciCallbacks)) {
+ return (IBluetoothHciCallbacks)iface;
+ }
+
+ IBluetoothHciCallbacks proxy = new IBluetoothHciCallbacks.Proxy(binder);
+
+ try {
+ for (String descriptor : proxy.interfaceChain()) {
+ if (descriptor.equals(kInterfaceName)) {
+ return proxy;
+ }
+ }
+ } catch (android.os.RemoteException e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Does a checked conversion from any interface to this class.
+ */
+ public static IBluetoothHciCallbacks castFrom(IHwInterface iface) {
+ return (iface == null) ? null : IBluetoothHciCallbacks.asInterface(iface.asBinder());
+ }
+
+ @Override
+ public IHwBinder asBinder();
+
+ /**
+ * This will invoke the equivalent of the C++ getService(std::string) if retry is
+ * true or tryGetService(std::string) if retry is false. If the service is
+ * available on the device and retry is true, this will wait for the service to
+ * start.
+ *
+ */
+ public static IBluetoothHciCallbacks getService(String serviceName, boolean retry) throws android.os.RemoteException {
+ return IBluetoothHciCallbacks.asInterface(HwBinder.getService("[email protected]::IBluetoothHciCallbacks", serviceName, retry));
+ }
+
+ /**
+ * Calls getService("default",retry).
+ */
+ public static IBluetoothHciCallbacks getService(boolean retry) throws android.os.RemoteException {
+ return getService("default", retry);
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(String,boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHciCallbacks getService(String serviceName) throws android.os.RemoteException {
+ return IBluetoothHciCallbacks.asInterface(HwBinder.getService("[email protected]::IBluetoothHciCallbacks", serviceName));
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHciCallbacks getService() throws android.os.RemoteException {
+ return getService("default");
+ }
+
+ /**
+ * Invoked when the Bluetooth controller initialization has been
+ * completed.
+ */
+ void initializationComplete(int status)
+ throws android.os.RemoteException;
+ /**
+ * This function is invoked when an HCI event is received from the
+ * Bluetooth controller to be forwarded to the Bluetooth stack.
+ * @param event is the HCI event to be sent to the Bluetooth stack.
+ */
+ void hciEventReceived(java.util.ArrayList<Byte> event)
+ throws android.os.RemoteException;
+ /**
+ * Send an ACL data packet form the controller to the host.
+ * @param data the ACL HCI packet to be passed to the host stack
+ */
+ void aclDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException;
+ /**
+ * Send a SCO data packet form the controller to the host.
+ * @param data the SCO HCI packet to be passed to the host stack
+ */
+ void scoDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * ["[email protected]::IChild",
+ * "[email protected]::IParent"
+ * "[email protected]::IBase"]
+ *
+ * @return descriptors a vector of descriptors of the run-time type of the
+ * object.
+ */
+ java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException;
+ /*
+ * Emit diagnostic information to the given file.
+ *
+ * Optionally overriden.
+ *
+ * @param fd File descriptor to dump data to.
+ * Must only be used for the duration of this call.
+ * @param options Arguments for debugging.
+ * Must support empty for default debug information.
+ */
+ void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceDescriptor on an IChild object must yield
+ * "[email protected]::IChild"
+ *
+ * @return descriptor a descriptor of the run-time type of the
+ * object (the first element of the vector returned by
+ * interfaceChain())
+ */
+ String interfaceDescriptor()
+ throws android.os.RemoteException;
+ /*
+ * Returns hashes of the source HAL files that define the interfaces of the
+ * runtime type information on the object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * [(hash of IChild.hal),
+ * (hash of IParent.hal)
+ * (hash of IBase.hal)].
+ *
+ * SHA-256 is used as the hashing algorithm. Each hash has 32 bytes
+ * according to SHA-256 standard.
+ *
+ * @return hashchain a vector of SHA-1 digests
+ */
+ java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException;
+ /*
+ * This method trigger the interface to enable/disable instrumentation based
+ * on system property hal.instrumentation.enable.
+ */
+ void setHALInstrumentation()
+ throws android.os.RemoteException;
+ /*
+ * Registers a death recipient, to be called when the process hosting this
+ * interface dies.
+ *
+ * @param recipient a hidl_death_recipient callback object
+ * @param cookie a cookie that must be returned with the callback
+ * @return success whether the death recipient was registered successfully.
+ */
+ boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException;
+ /*
+ * Provides way to determine if interface is running without requesting
+ * any functionality.
+ */
+ void ping()
+ throws android.os.RemoteException;
+ /*
+ * Get debug information on references on this interface.
+ * @return info debugging information. See comments of DebugInfo.
+ */
+ android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException;
+ /*
+ * This method notifies the interface that one or more system properties
+ * have changed. The default implementation calls
+ * (C++) report_sysprop_change() in libcutils or
+ * (Java) android.os.SystemProperties.reportSyspropChanged,
+ * which in turn calls a set of registered callbacks (eg to update trace
+ * tags).
+ */
+ void notifySyspropsChanged()
+ throws android.os.RemoteException;
+ /*
+ * Unregisters the registered death recipient. If this service was registered
+ * multiple times with the same exact death recipient, this unlinks the most
+ * recently registered one.
+ *
+ * @param recipient a previously registered hidl_death_recipient callback
+ * @return success whether the death recipient was unregistered successfully.
+ */
+ boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException;
+
+ public static final class Proxy implements IBluetoothHciCallbacks {
+ private IHwBinder mRemote;
+
+ public Proxy(IHwBinder remote) {
+ mRemote = java.util.Objects.requireNonNull(remote);
+ }
+
+ @Override
+ public IHwBinder asBinder() {
+ return mRemote;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return this.interfaceDescriptor() + "@Proxy";
+ } catch (android.os.RemoteException ex) {
+ /* ignored; handled below. */
+ }
+ return "[class or subclass of " + IBluetoothHciCallbacks.kInterfaceName + "]@Proxy";
+ }
+
+ @Override
+ public final boolean equals(java.lang.Object other) {
+ return HidlSupport.interfacesEqual(this, other);
+ }
+
+ @Override
+ public final int hashCode() {
+ return this.asBinder().hashCode();
+ }
+
+ // Methods from ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks follow.
+ @Override
+ public void initializationComplete(int status)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt32(status);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(1 /* initializationComplete */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void hciEventReceived(java.util.ArrayList<Byte> event)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt8Vector(event);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(2 /* hciEventReceived */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void aclDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(3 /* aclDataReceived */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void scoDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(4 /* scoDataReceived */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ @Override
+ public java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256067662 /* interfaceChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<String> _hidl_out_descriptors = _hidl_reply.readStringVector();
+ return _hidl_out_descriptors;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+ _hidl_request.writeNativeHandle(fd);
+ _hidl_request.writeStringVector(options);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256131655 /* debug */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public String interfaceDescriptor()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256136003 /* interfaceDescriptor */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ String _hidl_out_descriptor = _hidl_reply.readString();
+ return _hidl_out_descriptor;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256398152 /* getHashChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = new java.util.ArrayList<byte[/* 32 */]>();
+ {
+ HwBlob _hidl_blob = _hidl_reply.readBuffer(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_blob.getInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */);
+ HwBlob childBlob = _hidl_reply.readEmbeddedBuffer(
+ _hidl_vec_size * 32,_hidl_blob.handle(),
+ 0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */,true /* nullable */);
+
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).clear();
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ byte[/* 32 */] _hidl_vec_element = new byte[32];
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ childBlob.copyToInt8Array(_hidl_array_offset_1, (byte[/* 32 */]) _hidl_vec_element, 32 /* size */);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).add(_hidl_vec_element);
+ }
+ }
+ }
+ return _hidl_out_hashchain;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void setHALInstrumentation()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256462420 /* setHALInstrumentation */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException {
+ return mRemote.linkToDeath(recipient, cookie);
+ }
+ @Override
+ public void ping()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256921159 /* ping */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257049926 /* getDebugInfo */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = new android.internal.hidl.base.V1_0.DebugInfo();
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).readFromParcel(_hidl_reply);
+ return _hidl_out_info;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void notifySyspropsChanged()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257120595 /* notifySyspropsChanged */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException {
+ return mRemote.unlinkToDeath(recipient);
+ }
+ }
+
+ public static abstract class Stub extends HwBinder implements IBluetoothHciCallbacks {
+ @Override
+ public IHwBinder asBinder() {
+ return this;
+ }
+
+ @Override
+ public final java.util.ArrayList<String> interfaceChain() {
+ return new java.util.ArrayList<String>(java.util.Arrays.asList(
+ android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName,
+ android.internal.hidl.base.V1_0.IBase.kInterfaceName));
+
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options) {
+ return;
+
+ }
+
+ @Override
+ public final String interfaceDescriptor() {
+ return android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName;
+
+ }
+
+ @Override
+ public final java.util.ArrayList<byte[/* 32 */]> getHashChain() {
+ return new java.util.ArrayList<byte[/* 32 */]>(java.util.Arrays.asList(
+ new byte[/* 32 */]{-125,95,65,-66,34,-127,-65,-78,47,62,51,-58,-6,-121,11,-34,123,-62,30,55,-27,-49,-70,-7,-93,111,-1,23,6,50,-9,84} /* 835f41be2281bfb22f3e33c6fa870bde7bc21e37e5cfbaf9a36fff170632f754 */,
+ new byte[/* 32 */]{-20,127,-41,-98,-48,45,-6,-123,-68,73,-108,38,-83,-82,62,-66,35,-17,5,36,-13,-51,105,87,19,-109,36,-72,59,24,-54,76} /* ec7fd79ed02dfa85bc499426adae3ebe23ef0524f3cd6957139324b83b18ca4c */));
+
+ }
+
+ @Override
+ public final void setHALInstrumentation() {
+
+ }
+
+ @Override
+ public final boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
+ return true;
+
+ }
+
+ @Override
+ public final void ping() {
+ return;
+
+ }
+
+ @Override
+ public final android.internal.hidl.base.V1_0.DebugInfo getDebugInfo() {
+ android.internal.hidl.base.V1_0.DebugInfo info = new android.internal.hidl.base.V1_0.DebugInfo();
+ info.pid = HidlSupport.getPidIfSharable();
+ info.ptr = 0;
+ info.arch = android.internal.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;
+ return info;
+
+ }
+
+ @Override
+ public final void notifySyspropsChanged() {
+ HwBinder.enableInstrumentation();
+
+ }
+
+ @Override
+ public final boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
+ return true;
+
+ }
+
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ if (kInterfaceName.equals(descriptor)) {
+ return this;
+ }
+ return null;
+ }
+
+ public void registerAsService(String serviceName) throws android.os.RemoteException {
+ registerService(serviceName);
+ }
+
+ @Override
+ public String toString() {
+ return this.interfaceDescriptor() + "@Stub";
+ }
+
+ //@Override
+ public void onTransact(int _hidl_code, HwParcel _hidl_request, final HwParcel _hidl_reply, int _hidl_flags)
+ throws android.os.RemoteException {
+ switch (_hidl_code) {
+ case 1 /* initializationComplete */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ int status = _hidl_request.readInt32();
+ initializationComplete(status);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 2 /* hciEventReceived */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ java.util.ArrayList<Byte> event = _hidl_request.readInt8Vector();
+ hciEventReceived(event);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 3 /* aclDataReceived */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ aclDataReceived(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 4 /* scoDataReceived */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ scoDataReceived(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256067662 /* interfaceChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<String> _hidl_out_descriptors = interfaceChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeStringVector(_hidl_out_descriptors);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256131655 /* debug */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ NativeHandle fd = _hidl_request.readNativeHandle();
+ java.util.ArrayList<String> options = _hidl_request.readStringVector();
+ debug(fd, options);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256136003 /* interfaceDescriptor */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ String _hidl_out_descriptor = interfaceDescriptor();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeString(_hidl_out_descriptor);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256398152 /* getHashChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = getHashChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ {
+ HwBlob _hidl_blob = new HwBlob(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_out_hashchain.size();
+ _hidl_blob.putInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */, _hidl_vec_size);
+ _hidl_blob.putBool(0 /* offset */ + 12 /* offsetof(hidl_vec<T>, mOwnsBuffer) */, false);
+ HwBlob childBlob = new HwBlob((int)(_hidl_vec_size * 32));
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ byte[] _hidl_array_item_1 = (byte[/* 32 */]) _hidl_out_hashchain.get(_hidl_index_0);
+
+ if (_hidl_array_item_1 == null || _hidl_array_item_1.length != 32) {
+ throw new IllegalArgumentException("Array element is not of the expected length");
+ }
+
+ childBlob.putInt8Array(_hidl_array_offset_1, _hidl_array_item_1);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ }
+ _hidl_blob.putBlob(0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */, childBlob);
+ }
+ _hidl_reply.writeBuffer(_hidl_blob);
+ }
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256462420 /* setHALInstrumentation */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ setHALInstrumentation();
+ break;
+ }
+
+ case 256660548 /* linkToDeath */:
+ {
+ break;
+ }
+
+ case 256921159 /* ping */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ ping();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257049926 /* getDebugInfo */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = getDebugInfo();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).writeToParcel(_hidl_reply);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257120595 /* notifySyspropsChanged */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ notifySyspropsChanged();
+ break;
+ }
+
+ case 257250372 /* unlinkToDeath */:
+ {
+ break;
+ }
+
+ }
+ }
+ }
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/Status.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/Status.java
new file mode 100644
index 0000000..9235005
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_0/Status.java
@@ -0,0 +1,48 @@
+package android.hardware.bluetooth.V1_0;
+
+
+public final class Status {
+ public static final int SUCCESS = 0;
+ public static final int TRANSPORT_ERROR = 1 /* ::android::hardware::bluetooth::V1_0::Status.SUCCESS implicitly + 1 */;
+ public static final int INITIALIZATION_ERROR = 2 /* ::android::hardware::bluetooth::V1_0::Status.TRANSPORT_ERROR implicitly + 1 */;
+ public static final int UNKNOWN = 3 /* ::android::hardware::bluetooth::V1_0::Status.INITIALIZATION_ERROR implicitly + 1 */;
+ public static final String toString(int o) {
+ if (o == SUCCESS) {
+ return "SUCCESS";
+ }
+ if (o == TRANSPORT_ERROR) {
+ return "TRANSPORT_ERROR";
+ }
+ if (o == INITIALIZATION_ERROR) {
+ return "INITIALIZATION_ERROR";
+ }
+ if (o == UNKNOWN) {
+ return "UNKNOWN";
+ }
+ return "0x" + Integer.toHexString(o);
+ }
+
+ public static final String dumpBitfield(int o) {
+ java.util.ArrayList<String> list = new java.util.ArrayList<>();
+ int flipped = 0;
+ list.add("SUCCESS"); // SUCCESS == 0
+ if ((o & TRANSPORT_ERROR) == TRANSPORT_ERROR) {
+ list.add("TRANSPORT_ERROR");
+ flipped |= TRANSPORT_ERROR;
+ }
+ if ((o & INITIALIZATION_ERROR) == INITIALIZATION_ERROR) {
+ list.add("INITIALIZATION_ERROR");
+ flipped |= INITIALIZATION_ERROR;
+ }
+ if ((o & UNKNOWN) == UNKNOWN) {
+ list.add("UNKNOWN");
+ flipped |= UNKNOWN;
+ }
+ if (o != flipped) {
+ list.add("0x" + Integer.toHexString(o & (~flipped)));
+ }
+ return String.join(" | ", list);
+ }
+
+};
+
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_1/IBluetoothHci.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_1/IBluetoothHci.java
new file mode 100644
index 0000000..4439451
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_1/IBluetoothHci.java
@@ -0,0 +1,840 @@
+package android.hardware.bluetooth.V1_1;
+
+import android.os.HidlSupport;
+import android.os.HwBinder;
+import android.os.IHwBinder;
+import android.os.HwBlob;
+import android.os.HwParcel;
+import android.os.IHwInterface;
+import android.os.NativeHandle;
+
+/**
+ * The Host Controller Interface (HCI) is the layer defined by the Bluetooth
+ * specification between the software that runs on the host and the Bluetooth
+ * controller chip. This boundary is the natural choice for a Hardware
+ * Abstraction Layer (HAL). Dealing only in HCI packets and events simplifies
+ * the stack and abstracts away power management, initialization, and other
+ * implementation-specific details related to the hardware.
+ */
+public interface IBluetoothHci extends android.hardware.bluetooth.V1_0.IBluetoothHci {
+ /**
+ * Fully-qualified interface name for this interface.
+ */
+ public static final String kInterfaceName = "[email protected]::IBluetoothHci";
+
+ /**
+ * Does a checked conversion from a binder to this class.
+ */
+ /* package private */ static IBluetoothHci asInterface(IHwBinder binder) {
+ if (binder == null) {
+ return null;
+ }
+
+ IHwInterface iface =
+ binder.queryLocalInterface(kInterfaceName);
+
+ if ((iface != null) && (iface instanceof IBluetoothHci)) {
+ return (IBluetoothHci)iface;
+ }
+
+ IBluetoothHci proxy = new IBluetoothHci.Proxy(binder);
+
+ try {
+ for (String descriptor : proxy.interfaceChain()) {
+ if (descriptor.equals(kInterfaceName)) {
+ return proxy;
+ }
+ }
+ } catch (android.os.RemoteException e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Does a checked conversion from any interface to this class.
+ */
+ public static IBluetoothHci castFrom(IHwInterface iface) {
+ return (iface == null) ? null : IBluetoothHci.asInterface(iface.asBinder());
+ }
+
+ @Override
+ public IHwBinder asBinder();
+
+ /**
+ * This will invoke the equivalent of the C++ getService(std::string) if retry is
+ * true or tryGetService(std::string) if retry is false. If the service is
+ * available on the device and retry is true, this will wait for the service to
+ * start.
+ *
+ */
+ public static IBluetoothHci getService(String serviceName, boolean retry) throws android.os.RemoteException {
+ return IBluetoothHci.asInterface(HwBinder.getService("[email protected]::IBluetoothHci", serviceName, retry));
+ }
+
+ /**
+ * Calls getService("default",retry).
+ */
+ public static IBluetoothHci getService(boolean retry) throws android.os.RemoteException {
+ return getService("default", retry);
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(String,boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHci getService(String serviceName) throws android.os.RemoteException {
+ return IBluetoothHci.asInterface(HwBinder.getService("[email protected]::IBluetoothHci", serviceName));
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHci getService() throws android.os.RemoteException {
+ return getService("default");
+ }
+
+ /**
+ * Same as @1.0, but uses 1.1 Callbacks version
+ */
+ void initialize_1_1(android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks callback)
+ throws android.os.RemoteException;
+ /**
+ * Send an ISO data packet (as specified in the Bluetooth Core
+ * Specification v5.2) to the Bluetooth controller.
+ * Packets must be processed in order.
+ * @param data HCI data packet to be sent
+ */
+ void sendIsoData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * ["[email protected]::IChild",
+ * "[email protected]::IParent"
+ * "[email protected]::IBase"]
+ *
+ * @return descriptors a vector of descriptors of the run-time type of the
+ * object.
+ */
+ java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException;
+ /*
+ * Emit diagnostic information to the given file.
+ *
+ * Optionally overriden.
+ *
+ * @param fd File descriptor to dump data to.
+ * Must only be used for the duration of this call.
+ * @param options Arguments for debugging.
+ * Must support empty for default debug information.
+ */
+ void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceDescriptor on an IChild object must yield
+ * "[email protected]::IChild"
+ *
+ * @return descriptor a descriptor of the run-time type of the
+ * object (the first element of the vector returned by
+ * interfaceChain())
+ */
+ String interfaceDescriptor()
+ throws android.os.RemoteException;
+ /*
+ * Returns hashes of the source HAL files that define the interfaces of the
+ * runtime type information on the object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * [(hash of IChild.hal),
+ * (hash of IParent.hal)
+ * (hash of IBase.hal)].
+ *
+ * SHA-256 is used as the hashing algorithm. Each hash has 32 bytes
+ * according to SHA-256 standard.
+ *
+ * @return hashchain a vector of SHA-1 digests
+ */
+ java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException;
+ /*
+ * This method trigger the interface to enable/disable instrumentation based
+ * on system property hal.instrumentation.enable.
+ */
+ void setHALInstrumentation()
+ throws android.os.RemoteException;
+ /*
+ * Registers a death recipient, to be called when the process hosting this
+ * interface dies.
+ *
+ * @param recipient a hidl_death_recipient callback object
+ * @param cookie a cookie that must be returned with the callback
+ * @return success whether the death recipient was registered successfully.
+ */
+ boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException;
+ /*
+ * Provides way to determine if interface is running without requesting
+ * any functionality.
+ */
+ void ping()
+ throws android.os.RemoteException;
+ /*
+ * Get debug information on references on this interface.
+ * @return info debugging information. See comments of DebugInfo.
+ */
+ android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException;
+ /*
+ * This method notifies the interface that one or more system properties
+ * have changed. The default implementation calls
+ * (C++) report_sysprop_change() in libcutils or
+ * (Java) android.os.SystemProperties.reportSyspropChanged,
+ * which in turn calls a set of registered callbacks (eg to update trace
+ * tags).
+ */
+ void notifySyspropsChanged()
+ throws android.os.RemoteException;
+ /*
+ * Unregisters the registered death recipient. If this service was registered
+ * multiple times with the same exact death recipient, this unlinks the most
+ * recently registered one.
+ *
+ * @param recipient a previously registered hidl_death_recipient callback
+ * @return success whether the death recipient was unregistered successfully.
+ */
+ boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException;
+
+ public static final class Proxy implements IBluetoothHci {
+ private IHwBinder mRemote;
+
+ public Proxy(IHwBinder remote) {
+ mRemote = java.util.Objects.requireNonNull(remote);
+ }
+
+ @Override
+ public IHwBinder asBinder() {
+ return mRemote;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return this.interfaceDescriptor() + "@Proxy";
+ } catch (android.os.RemoteException ex) {
+ /* ignored; handled below. */
+ }
+ return "[class or subclass of " + IBluetoothHci.kInterfaceName + "]@Proxy";
+ }
+
+ @Override
+ public final boolean equals(java.lang.Object other) {
+ return HidlSupport.interfacesEqual(this, other);
+ }
+
+ @Override
+ public final int hashCode() {
+ return this.asBinder().hashCode();
+ }
+
+ // Methods from ::android::hardware::bluetooth::V1_0::IBluetoothHci follow.
+ @Override
+ public void initialize(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks callback)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeStrongBinder(callback == null ? null : callback.asBinder());
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(1 /* initialize */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void sendHciCommand(java.util.ArrayList<Byte> command)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeInt8Vector(command);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(2 /* sendHciCommand */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void sendAclData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(3 /* sendAclData */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void sendScoData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(4 /* sendScoData */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void close()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(5 /* close */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ // Methods from ::android::hardware::bluetooth::V1_1::IBluetoothHci follow.
+ @Override
+ public void initialize_1_1(android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks callback)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_1.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeStrongBinder(callback == null ? null : callback.asBinder());
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(6 /* initialize_1_1 */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void sendIsoData(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_1.IBluetoothHci.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(7 /* sendIsoData */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ @Override
+ public java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256067662 /* interfaceChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<String> _hidl_out_descriptors = _hidl_reply.readStringVector();
+ return _hidl_out_descriptors;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+ _hidl_request.writeNativeHandle(fd);
+ _hidl_request.writeStringVector(options);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256131655 /* debug */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public String interfaceDescriptor()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256136003 /* interfaceDescriptor */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ String _hidl_out_descriptor = _hidl_reply.readString();
+ return _hidl_out_descriptor;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256398152 /* getHashChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = new java.util.ArrayList<byte[/* 32 */]>();
+ {
+ HwBlob _hidl_blob = _hidl_reply.readBuffer(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_blob.getInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */);
+ HwBlob childBlob = _hidl_reply.readEmbeddedBuffer(
+ _hidl_vec_size * 32,_hidl_blob.handle(),
+ 0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */,true /* nullable */);
+
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).clear();
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ byte[/* 32 */] _hidl_vec_element = new byte[32];
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ childBlob.copyToInt8Array(_hidl_array_offset_1, (byte[/* 32 */]) _hidl_vec_element, 32 /* size */);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).add(_hidl_vec_element);
+ }
+ }
+ }
+ return _hidl_out_hashchain;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void setHALInstrumentation()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256462420 /* setHALInstrumentation */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException {
+ return mRemote.linkToDeath(recipient, cookie);
+ }
+ @Override
+ public void ping()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256921159 /* ping */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257049926 /* getDebugInfo */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = new android.internal.hidl.base.V1_0.DebugInfo();
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).readFromParcel(_hidl_reply);
+ return _hidl_out_info;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void notifySyspropsChanged()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257120595 /* notifySyspropsChanged */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException {
+ return mRemote.unlinkToDeath(recipient);
+ }
+ }
+
+ public static abstract class Stub extends HwBinder implements IBluetoothHci {
+ @Override
+ public IHwBinder asBinder() {
+ return this;
+ }
+
+ @Override
+ public final java.util.ArrayList<String> interfaceChain() {
+ return new java.util.ArrayList<String>(java.util.Arrays.asList(
+ android.hardware.bluetooth.V1_1.IBluetoothHci.kInterfaceName,
+ android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName,
+ android.internal.hidl.base.V1_0.IBase.kInterfaceName));
+
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options) {
+ return;
+
+ }
+
+ @Override
+ public final String interfaceDescriptor() {
+ return android.hardware.bluetooth.V1_1.IBluetoothHci.kInterfaceName;
+
+ }
+
+ @Override
+ public final java.util.ArrayList<byte[/* 32 */]> getHashChain() {
+ return new java.util.ArrayList<byte[/* 32 */]>(java.util.Arrays.asList(
+ new byte[/* 32 */]{54,47,-47,-62,22,65,-62,34,79,59,-128,-61,13,-105,-105,-71,-120,-6,63,52,66,67,-43,49,-70,115,-59,83,119,-102,87,99} /* 362fd1c21641c2224f3b80c30d9797b988fa3f344243d531ba73c553779a5763 */,
+ new byte[/* 32 */]{52,124,-25,70,-127,86,7,86,127,95,59,83,-28,-128,9,-104,-54,90,-71,53,81,65,-16,-120,15,-64,-49,12,31,-59,-61,85} /* 347ce746815607567f5f3b53e4800998ca5ab9355141f0880fc0cf0c1fc5c355 */,
+ new byte[/* 32 */]{-20,127,-41,-98,-48,45,-6,-123,-68,73,-108,38,-83,-82,62,-66,35,-17,5,36,-13,-51,105,87,19,-109,36,-72,59,24,-54,76} /* ec7fd79ed02dfa85bc499426adae3ebe23ef0524f3cd6957139324b83b18ca4c */));
+
+ }
+
+ @Override
+ public final void setHALInstrumentation() {
+
+ }
+
+ @Override
+ public final boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
+ return true;
+
+ }
+
+ @Override
+ public final void ping() {
+ return;
+
+ }
+
+ @Override
+ public final android.internal.hidl.base.V1_0.DebugInfo getDebugInfo() {
+ android.internal.hidl.base.V1_0.DebugInfo info = new android.internal.hidl.base.V1_0.DebugInfo();
+ info.pid = HidlSupport.getPidIfSharable();
+ info.ptr = 0;
+ info.arch = android.internal.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;
+ return info;
+
+ }
+
+ @Override
+ public final void notifySyspropsChanged() {
+ HwBinder.enableInstrumentation();
+
+ }
+
+ @Override
+ public final boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
+ return true;
+
+ }
+
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ if (kInterfaceName.equals(descriptor)) {
+ return this;
+ }
+ return null;
+ }
+
+ public void registerAsService(String serviceName) throws android.os.RemoteException {
+ registerService(serviceName);
+ }
+
+ @Override
+ public String toString() {
+ return this.interfaceDescriptor() + "@Stub";
+ }
+
+ //@Override
+ public void onTransact(int _hidl_code, HwParcel _hidl_request, final HwParcel _hidl_reply, int _hidl_flags)
+ throws android.os.RemoteException {
+ switch (_hidl_code) {
+ case 1 /* initialize */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks callback = android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.asInterface(_hidl_request.readStrongBinder());
+ initialize(callback);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 2 /* sendHciCommand */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ java.util.ArrayList<Byte> command = _hidl_request.readInt8Vector();
+ sendHciCommand(command);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 3 /* sendAclData */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ sendAclData(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 4 /* sendScoData */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ sendScoData(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 5 /* close */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHci.kInterfaceName);
+
+ close();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 6 /* initialize_1_1 */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_1.IBluetoothHci.kInterfaceName);
+
+ android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks callback = android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks.asInterface(_hidl_request.readStrongBinder());
+ initialize_1_1(callback);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 7 /* sendIsoData */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_1.IBluetoothHci.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ sendIsoData(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256067662 /* interfaceChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<String> _hidl_out_descriptors = interfaceChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeStringVector(_hidl_out_descriptors);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256131655 /* debug */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ NativeHandle fd = _hidl_request.readNativeHandle();
+ java.util.ArrayList<String> options = _hidl_request.readStringVector();
+ debug(fd, options);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256136003 /* interfaceDescriptor */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ String _hidl_out_descriptor = interfaceDescriptor();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeString(_hidl_out_descriptor);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256398152 /* getHashChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = getHashChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ {
+ HwBlob _hidl_blob = new HwBlob(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_out_hashchain.size();
+ _hidl_blob.putInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */, _hidl_vec_size);
+ _hidl_blob.putBool(0 /* offset */ + 12 /* offsetof(hidl_vec<T>, mOwnsBuffer) */, false);
+ HwBlob childBlob = new HwBlob((int)(_hidl_vec_size * 32));
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ byte[] _hidl_array_item_1 = (byte[/* 32 */]) _hidl_out_hashchain.get(_hidl_index_0);
+
+ if (_hidl_array_item_1 == null || _hidl_array_item_1.length != 32) {
+ throw new IllegalArgumentException("Array element is not of the expected length");
+ }
+
+ childBlob.putInt8Array(_hidl_array_offset_1, _hidl_array_item_1);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ }
+ _hidl_blob.putBlob(0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */, childBlob);
+ }
+ _hidl_reply.writeBuffer(_hidl_blob);
+ }
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256462420 /* setHALInstrumentation */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ setHALInstrumentation();
+ break;
+ }
+
+ case 256660548 /* linkToDeath */:
+ {
+ break;
+ }
+
+ case 256921159 /* ping */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ ping();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257049926 /* getDebugInfo */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = getDebugInfo();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).writeToParcel(_hidl_reply);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257120595 /* notifySyspropsChanged */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ notifySyspropsChanged();
+ break;
+ }
+
+ case 257250372 /* unlinkToDeath */:
+ {
+ break;
+ }
+
+ }
+ }
+ }
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_1/IBluetoothHciCallbacks.java b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_1/IBluetoothHciCallbacks.java
new file mode 100644
index 0000000..905e3a0
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/android/hardware/bluetooth/V1_1/IBluetoothHciCallbacks.java
@@ -0,0 +1,774 @@
+package android.hardware.bluetooth.V1_1;
+
+import android.os.HidlSupport;
+import android.os.HwBinder;
+import android.os.IHwBinder;
+import android.os.HwBlob;
+import android.os.HwParcel;
+import android.os.IHwInterface;
+import android.os.NativeHandle;
+
+/**
+ * The interface from the Bluetooth Controller to the stack.
+ */
+public interface IBluetoothHciCallbacks extends android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks {
+ /**
+ * Fully-qualified interface name for this interface.
+ */
+ public static final String kInterfaceName = "[email protected]::IBluetoothHciCallbacks";
+
+ /**
+ * Does a checked conversion from a binder to this class.
+ */
+ /* package private */ static IBluetoothHciCallbacks asInterface(IHwBinder binder) {
+ if (binder == null) {
+ return null;
+ }
+
+ IHwInterface iface =
+ binder.queryLocalInterface(kInterfaceName);
+
+ if ((iface != null) && (iface instanceof IBluetoothHciCallbacks)) {
+ return (IBluetoothHciCallbacks)iface;
+ }
+
+ IBluetoothHciCallbacks proxy = new IBluetoothHciCallbacks.Proxy(binder);
+
+ try {
+ for (String descriptor : proxy.interfaceChain()) {
+ if (descriptor.equals(kInterfaceName)) {
+ return proxy;
+ }
+ }
+ } catch (android.os.RemoteException e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Does a checked conversion from any interface to this class.
+ */
+ public static IBluetoothHciCallbacks castFrom(IHwInterface iface) {
+ return (iface == null) ? null : IBluetoothHciCallbacks.asInterface(iface.asBinder());
+ }
+
+ @Override
+ public IHwBinder asBinder();
+
+ /**
+ * This will invoke the equivalent of the C++ getService(std::string) if retry is
+ * true or tryGetService(std::string) if retry is false. If the service is
+ * available on the device and retry is true, this will wait for the service to
+ * start.
+ *
+ */
+ public static IBluetoothHciCallbacks getService(String serviceName, boolean retry) throws android.os.RemoteException {
+ return IBluetoothHciCallbacks.asInterface(HwBinder.getService("[email protected]::IBluetoothHciCallbacks", serviceName, retry));
+ }
+
+ /**
+ * Calls getService("default",retry).
+ */
+ public static IBluetoothHciCallbacks getService(boolean retry) throws android.os.RemoteException {
+ return getService("default", retry);
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(String,boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHciCallbacks getService(String serviceName) throws android.os.RemoteException {
+ return IBluetoothHciCallbacks.asInterface(HwBinder.getService("[email protected]::IBluetoothHciCallbacks", serviceName));
+ }
+
+ /**
+ * @deprecated this will not wait for the interface to come up if it hasn't yet
+ * started. See getService(boolean) instead.
+ */
+ @Deprecated
+ public static IBluetoothHciCallbacks getService() throws android.os.RemoteException {
+ return getService("default");
+ }
+
+ /**
+ * Send a ISO data packet form the controller to the host.
+ * @param data the ISO HCI packet to be passed to the host stack
+ */
+ void isoDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * ["[email protected]::IChild",
+ * "[email protected]::IParent"
+ * "[email protected]::IBase"]
+ *
+ * @return descriptors a vector of descriptors of the run-time type of the
+ * object.
+ */
+ java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException;
+ /*
+ * Emit diagnostic information to the given file.
+ *
+ * Optionally overriden.
+ *
+ * @param fd File descriptor to dump data to.
+ * Must only be used for the duration of this call.
+ * @param options Arguments for debugging.
+ * Must support empty for default debug information.
+ */
+ void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException;
+ /*
+ * Provides run-time type information for this object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceDescriptor on an IChild object must yield
+ * "[email protected]::IChild"
+ *
+ * @return descriptor a descriptor of the run-time type of the
+ * object (the first element of the vector returned by
+ * interfaceChain())
+ */
+ String interfaceDescriptor()
+ throws android.os.RemoteException;
+ /*
+ * Returns hashes of the source HAL files that define the interfaces of the
+ * runtime type information on the object.
+ * For example, for the following interface definition:
+ * package [email protected];
+ * interface IParent {};
+ * interface IChild extends IParent {};
+ * Calling interfaceChain on an IChild object must yield the following:
+ * [(hash of IChild.hal),
+ * (hash of IParent.hal)
+ * (hash of IBase.hal)].
+ *
+ * SHA-256 is used as the hashing algorithm. Each hash has 32 bytes
+ * according to SHA-256 standard.
+ *
+ * @return hashchain a vector of SHA-1 digests
+ */
+ java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException;
+ /*
+ * This method trigger the interface to enable/disable instrumentation based
+ * on system property hal.instrumentation.enable.
+ */
+ void setHALInstrumentation()
+ throws android.os.RemoteException;
+ /*
+ * Registers a death recipient, to be called when the process hosting this
+ * interface dies.
+ *
+ * @param recipient a hidl_death_recipient callback object
+ * @param cookie a cookie that must be returned with the callback
+ * @return success whether the death recipient was registered successfully.
+ */
+ boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException;
+ /*
+ * Provides way to determine if interface is running without requesting
+ * any functionality.
+ */
+ void ping()
+ throws android.os.RemoteException;
+ /*
+ * Get debug information on references on this interface.
+ * @return info debugging information. See comments of DebugInfo.
+ */
+ android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException;
+ /*
+ * This method notifies the interface that one or more system properties
+ * have changed. The default implementation calls
+ * (C++) report_sysprop_change() in libcutils or
+ * (Java) android.os.SystemProperties.reportSyspropChanged,
+ * which in turn calls a set of registered callbacks (eg to update trace
+ * tags).
+ */
+ void notifySyspropsChanged()
+ throws android.os.RemoteException;
+ /*
+ * Unregisters the registered death recipient. If this service was registered
+ * multiple times with the same exact death recipient, this unlinks the most
+ * recently registered one.
+ *
+ * @param recipient a previously registered hidl_death_recipient callback
+ * @return success whether the death recipient was unregistered successfully.
+ */
+ boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException;
+
+ public static final class Proxy implements IBluetoothHciCallbacks {
+ private IHwBinder mRemote;
+
+ public Proxy(IHwBinder remote) {
+ mRemote = java.util.Objects.requireNonNull(remote);
+ }
+
+ @Override
+ public IHwBinder asBinder() {
+ return mRemote;
+ }
+
+ @Override
+ public String toString() {
+ try {
+ return this.interfaceDescriptor() + "@Proxy";
+ } catch (android.os.RemoteException ex) {
+ /* ignored; handled below. */
+ }
+ return "[class or subclass of " + IBluetoothHciCallbacks.kInterfaceName + "]@Proxy";
+ }
+
+ @Override
+ public final boolean equals(java.lang.Object other) {
+ return HidlSupport.interfacesEqual(this, other);
+ }
+
+ @Override
+ public final int hashCode() {
+ return this.asBinder().hashCode();
+ }
+
+ // Methods from ::android::hardware::bluetooth::V1_0::IBluetoothHciCallbacks follow.
+ @Override
+ public void initializationComplete(int status)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt32(status);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(1 /* initializationComplete */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void hciEventReceived(java.util.ArrayList<Byte> event)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt8Vector(event);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(2 /* hciEventReceived */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void aclDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(3 /* aclDataReceived */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void scoDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(4 /* scoDataReceived */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ // Methods from ::android::hardware::bluetooth::V1_1::IBluetoothHciCallbacks follow.
+ @Override
+ public void isoDataReceived(java.util.ArrayList<Byte> data)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks.kInterfaceName);
+ _hidl_request.writeInt8Vector(data);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(5 /* isoDataReceived */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ @Override
+ public java.util.ArrayList<String> interfaceChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256067662 /* interfaceChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<String> _hidl_out_descriptors = _hidl_reply.readStringVector();
+ return _hidl_out_descriptors;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options)
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+ _hidl_request.writeNativeHandle(fd);
+ _hidl_request.writeStringVector(options);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256131655 /* debug */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public String interfaceDescriptor()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256136003 /* interfaceDescriptor */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ String _hidl_out_descriptor = _hidl_reply.readString();
+ return _hidl_out_descriptor;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public java.util.ArrayList<byte[/* 32 */]> getHashChain()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256398152 /* getHashChain */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = new java.util.ArrayList<byte[/* 32 */]>();
+ {
+ HwBlob _hidl_blob = _hidl_reply.readBuffer(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_blob.getInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */);
+ HwBlob childBlob = _hidl_reply.readEmbeddedBuffer(
+ _hidl_vec_size * 32,_hidl_blob.handle(),
+ 0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */,true /* nullable */);
+
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).clear();
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ byte[/* 32 */] _hidl_vec_element = new byte[32];
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ childBlob.copyToInt8Array(_hidl_array_offset_1, (byte[/* 32 */]) _hidl_vec_element, 32 /* size */);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ ((java.util.ArrayList<byte[/* 32 */]>) _hidl_out_hashchain).add(_hidl_vec_element);
+ }
+ }
+ }
+ return _hidl_out_hashchain;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void setHALInstrumentation()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256462420 /* setHALInstrumentation */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie)
+ throws android.os.RemoteException {
+ return mRemote.linkToDeath(recipient, cookie);
+ }
+ @Override
+ public void ping()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(256921159 /* ping */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public android.internal.hidl.base.V1_0.DebugInfo getDebugInfo()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257049926 /* getDebugInfo */, _hidl_request, _hidl_reply, 0 /* flags */);
+ _hidl_reply.verifySuccess();
+ _hidl_request.releaseTemporaryStorage();
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = new android.internal.hidl.base.V1_0.DebugInfo();
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).readFromParcel(_hidl_reply);
+ return _hidl_out_info;
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public void notifySyspropsChanged()
+ throws android.os.RemoteException {
+ HwParcel _hidl_request = new HwParcel();
+ _hidl_request.writeInterfaceToken(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ HwParcel _hidl_reply = new HwParcel();
+ try {
+ mRemote.transact(257120595 /* notifySyspropsChanged */, _hidl_request, _hidl_reply, 1 /* oneway */);
+ _hidl_request.releaseTemporaryStorage();
+ } finally {
+ _hidl_reply.release();
+ }
+ }
+
+ @Override
+ public boolean unlinkToDeath(IHwBinder.DeathRecipient recipient)
+ throws android.os.RemoteException {
+ return mRemote.unlinkToDeath(recipient);
+ }
+ }
+
+ public static abstract class Stub extends HwBinder implements IBluetoothHciCallbacks {
+ @Override
+ public IHwBinder asBinder() {
+ return this;
+ }
+
+ @Override
+ public final java.util.ArrayList<String> interfaceChain() {
+ return new java.util.ArrayList<String>(java.util.Arrays.asList(
+ android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks.kInterfaceName,
+ android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName,
+ android.internal.hidl.base.V1_0.IBase.kInterfaceName));
+
+ }
+
+ @Override
+ public void debug(NativeHandle fd, java.util.ArrayList<String> options) {
+ return;
+
+ }
+
+ @Override
+ public final String interfaceDescriptor() {
+ return android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks.kInterfaceName;
+
+ }
+
+ @Override
+ public final java.util.ArrayList<byte[/* 32 */]> getHashChain() {
+ return new java.util.ArrayList<byte[/* 32 */]>(java.util.Arrays.asList(
+ new byte[/* 32 */]{64,-85,44,104,102,-63,-115,50,-70,-10,-28,-98,48,83,-108,-98,121,96,31,86,-106,58,121,30,-109,-26,-117,-98,-31,-113,113,-115} /* 40ab2c6866c18d32baf6e49e3053949e79601f56963a791e93e68b9ee18f718d */,
+ new byte[/* 32 */]{-125,95,65,-66,34,-127,-65,-78,47,62,51,-58,-6,-121,11,-34,123,-62,30,55,-27,-49,-70,-7,-93,111,-1,23,6,50,-9,84} /* 835f41be2281bfb22f3e33c6fa870bde7bc21e37e5cfbaf9a36fff170632f754 */,
+ new byte[/* 32 */]{-20,127,-41,-98,-48,45,-6,-123,-68,73,-108,38,-83,-82,62,-66,35,-17,5,36,-13,-51,105,87,19,-109,36,-72,59,24,-54,76} /* ec7fd79ed02dfa85bc499426adae3ebe23ef0524f3cd6957139324b83b18ca4c */));
+
+ }
+
+ @Override
+ public final void setHALInstrumentation() {
+
+ }
+
+ @Override
+ public final boolean linkToDeath(IHwBinder.DeathRecipient recipient, long cookie) {
+ return true;
+
+ }
+
+ @Override
+ public final void ping() {
+ return;
+
+ }
+
+ @Override
+ public final android.internal.hidl.base.V1_0.DebugInfo getDebugInfo() {
+ android.internal.hidl.base.V1_0.DebugInfo info = new android.internal.hidl.base.V1_0.DebugInfo();
+ info.pid = HidlSupport.getPidIfSharable();
+ info.ptr = 0;
+ info.arch = android.internal.hidl.base.V1_0.DebugInfo.Architecture.UNKNOWN;
+ return info;
+
+ }
+
+ @Override
+ public final void notifySyspropsChanged() {
+ HwBinder.enableInstrumentation();
+
+ }
+
+ @Override
+ public final boolean unlinkToDeath(IHwBinder.DeathRecipient recipient) {
+ return true;
+
+ }
+
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ if (kInterfaceName.equals(descriptor)) {
+ return this;
+ }
+ return null;
+ }
+
+ public void registerAsService(String serviceName) throws android.os.RemoteException {
+ registerService(serviceName);
+ }
+
+ @Override
+ public String toString() {
+ return this.interfaceDescriptor() + "@Stub";
+ }
+
+ //@Override
+ public void onTransact(int _hidl_code, HwParcel _hidl_request, final HwParcel _hidl_reply, int _hidl_flags)
+ throws android.os.RemoteException {
+ switch (_hidl_code) {
+ case 1 /* initializationComplete */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ int status = _hidl_request.readInt32();
+ initializationComplete(status);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 2 /* hciEventReceived */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ java.util.ArrayList<Byte> event = _hidl_request.readInt8Vector();
+ hciEventReceived(event);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 3 /* aclDataReceived */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ aclDataReceived(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 4 /* scoDataReceived */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ scoDataReceived(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 5 /* isoDataReceived */:
+ {
+ _hidl_request.enforceInterface(android.hardware.bluetooth.V1_1.IBluetoothHciCallbacks.kInterfaceName);
+
+ java.util.ArrayList<Byte> data = _hidl_request.readInt8Vector();
+ isoDataReceived(data);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256067662 /* interfaceChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<String> _hidl_out_descriptors = interfaceChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeStringVector(_hidl_out_descriptors);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256131655 /* debug */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ NativeHandle fd = _hidl_request.readNativeHandle();
+ java.util.ArrayList<String> options = _hidl_request.readStringVector();
+ debug(fd, options);
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256136003 /* interfaceDescriptor */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ String _hidl_out_descriptor = interfaceDescriptor();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.writeString(_hidl_out_descriptor);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256398152 /* getHashChain */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ java.util.ArrayList<byte[/* 32 */]> _hidl_out_hashchain = getHashChain();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ {
+ HwBlob _hidl_blob = new HwBlob(16 /* size */);
+ {
+ int _hidl_vec_size = _hidl_out_hashchain.size();
+ _hidl_blob.putInt32(0 /* offset */ + 8 /* offsetof(hidl_vec<T>, mSize) */, _hidl_vec_size);
+ _hidl_blob.putBool(0 /* offset */ + 12 /* offsetof(hidl_vec<T>, mOwnsBuffer) */, false);
+ HwBlob childBlob = new HwBlob((int)(_hidl_vec_size * 32));
+ for (int _hidl_index_0 = 0; _hidl_index_0 < _hidl_vec_size; ++_hidl_index_0) {
+ {
+ long _hidl_array_offset_1 = _hidl_index_0 * 32;
+ byte[] _hidl_array_item_1 = (byte[/* 32 */]) _hidl_out_hashchain.get(_hidl_index_0);
+
+ if (_hidl_array_item_1 == null || _hidl_array_item_1.length != 32) {
+ throw new IllegalArgumentException("Array element is not of the expected length");
+ }
+
+ childBlob.putInt8Array(_hidl_array_offset_1, _hidl_array_item_1);
+ _hidl_array_offset_1 += 32 * 1;
+ }
+ }
+ _hidl_blob.putBlob(0 /* offset */ + 0 /* offsetof(hidl_vec<T>, mBuffer) */, childBlob);
+ }
+ _hidl_reply.writeBuffer(_hidl_blob);
+ }
+ _hidl_reply.send();
+ break;
+ }
+
+ case 256462420 /* setHALInstrumentation */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ setHALInstrumentation();
+ break;
+ }
+
+ case 256660548 /* linkToDeath */:
+ {
+ break;
+ }
+
+ case 256921159 /* ping */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ ping();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257049926 /* getDebugInfo */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ android.internal.hidl.base.V1_0.DebugInfo _hidl_out_info = getDebugInfo();
+ _hidl_reply.writeStatus(HwParcel.STATUS_SUCCESS);
+ ((android.internal.hidl.base.V1_0.DebugInfo) _hidl_out_info).writeToParcel(_hidl_reply);
+ _hidl_reply.send();
+ break;
+ }
+
+ case 257120595 /* notifySyspropsChanged */:
+ {
+ _hidl_request.enforceInterface(android.internal.hidl.base.V1_0.IBase.kInterfaceName);
+
+ notifySyspropsChanged();
+ break;
+ }
+
+ case 257250372 /* unlinkToDeath */:
+ {
+ break;
+ }
+
+ }
+ }
+ }
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciHal.java b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciHal.java
new file mode 100644
index 0000000..fd81921
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciHal.java
@@ -0,0 +1,260 @@
+package com.github.google.bumble.remotehci;
+
+import android.hardware.bluetooth.V1_0.Status;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+
+public interface HciHal {
+ public enum Status {
+ SUCCESS("SUCCESS"),
+ ALREADY_INITIALIZED("ALREADY_INITIALIZED"),
+ UNABLE_TO_OPEN_INTERFACE("UNABLE_TO_OPEN_INTERFACE"),
+ INITIALIZATION_ERROR("INITIALIZATION_ERROR"),
+ TRANSPORT_ERROR("TRANSPORT_ERROR"),
+ UNKNOWN("UNKNOWN");
+
+ public final String label;
+
+ private Status(String label) {
+ this.label = label;
+ }
+ }
+ static final String TAG = "HciHal";
+ public static HciHal create(HciHalCallback hciCallbacks) {
+ // First try HIDL
+ HciHal hciHal = HciHidlHal.create(hciCallbacks);
+ if (hciHal != null) {
+ Log.d(TAG, "Found HIDL HAL");
+ return hciHal;
+ }
+
+ // Then try AIDL
+ hciHal = HciAidlHal.create(hciCallbacks);
+ if (hciHal != null) {
+ Log.d(TAG, "Found AIDL HAL");
+ return hciHal;
+ }
+
+ Log.d(TAG, "No HAL found");
+ return null;
+ }
+
+ public Status initialize() throws RemoteException, InterruptedException;
+ public void sendPacket(HciPacket.Type type, byte[] packet);
+}
+
+class HciHidlHal extends android.hardware.bluetooth.V1_0.IBluetoothHciCallbacks.Stub implements HciHal {
+ private static final String TAG = "HciHidlHal";
+ private final android.hardware.bluetooth.V1_0.IBluetoothHci mHciService;
+ private final HciHalCallback mHciCallbacks;
+ private int mInitializationStatus = -1;
+
+
+ public static HciHidlHal create(HciHalCallback hciCallbacks) {
+ // Get the HAL service.
+ android.hardware.bluetooth.V1_0.IBluetoothHci hciService;
+ try {
+ hciService = android.hardware.bluetooth.V1_0.IBluetoothHci.getService(true);
+ } catch (NoSuchElementException e) {
+ Log.d(TAG, "HIDL HAL V1.0 not found");
+ return null;
+ } catch (android.os.RemoteException e) {
+ Log.w(TAG, "Exception from getService: " + e);
+ return null;
+ }
+ Log.d(TAG, "Found HIDL HAL V1.0");
+ return new HciHidlHal(hciService, hciCallbacks);
+ }
+
+ private HciHidlHal(android.hardware.bluetooth.V1_0.IBluetoothHci hciService, HciHalCallback hciCallbacks) {
+ mHciService = hciService;
+ mHciCallbacks = hciCallbacks;
+ }
+
+ public Status initialize() throws RemoteException, InterruptedException {
+ // Trigger the initialization.
+ mHciService.initialize(this);
+
+ // Wait for the initialization to complete.
+ Log.d(TAG, "Waiting for initialization status...");
+ synchronized (this) {
+ while (mInitializationStatus == -1) {
+ wait();
+ }
+ }
+
+ // Map the status code.
+ switch (mInitializationStatus) {
+ case android.hardware.bluetooth.V1_0.Status.SUCCESS:
+ return Status.SUCCESS;
+
+ case android.hardware.bluetooth.V1_0.Status.TRANSPORT_ERROR:
+ return Status.TRANSPORT_ERROR;
+
+ case android.hardware.bluetooth.V1_0.Status.INITIALIZATION_ERROR:
+ return Status.INITIALIZATION_ERROR;
+
+ default:
+ return Status.UNKNOWN;
+ }
+ }
+
+ @Override
+ public void sendPacket(HciPacket.Type type, byte[] packet) {
+ ArrayList<Byte> data = HciPacket.byteArrayToList(packet);
+
+ try {
+ switch (type) {
+ case COMMAND:
+ mHciService.sendHciCommand(data);
+ break;
+
+ case ACL_DATA:
+ mHciService.sendAclData(data);
+ break;
+
+ case SCO_DATA:
+ mHciService.sendScoData(data);
+ break;
+ }
+ } catch (RemoteException error) {
+ Log.w(TAG, "failed to forward packet: " + error);
+ }
+ }
+
+ @Override
+ public synchronized void initializationComplete(int status) throws RemoteException {
+ mInitializationStatus = status;
+ notifyAll();
+ }
+
+ @Override
+ public void hciEventReceived(ArrayList<Byte> event) throws RemoteException {
+ byte[] packet = HciPacket.listToByteArray(event);
+ mHciCallbacks.onPacket(HciPacket.Type.EVENT, packet);
+ }
+
+ @Override
+ public void aclDataReceived(ArrayList<Byte> data) throws RemoteException {
+ byte[] packet = HciPacket.listToByteArray(data);
+ mHciCallbacks.onPacket(HciPacket.Type.ACL_DATA, packet);
+ }
+
+ @Override
+ public void scoDataReceived(ArrayList<Byte> data) throws RemoteException {
+ byte[] packet = HciPacket.listToByteArray(data);
+ mHciCallbacks.onPacket(HciPacket.Type.SCO_DATA, packet);
+ }
+}
+
+class HciAidlHal extends android.hardware.bluetooth.IBluetoothHciCallbacks.Stub implements HciHal {
+ private static final String TAG = "HciAidlHal";
+ private final android.hardware.bluetooth.IBluetoothHci mHciService;
+ private final HciHalCallback mHciCallbacks;
+ private int mInitializationStatus = android.hardware.bluetooth.Status.SUCCESS;
+
+ public static HciAidlHal create(HciHalCallback hciCallbacks) {
+ IBinder binder = ServiceManager.getService("android.hardware.bluetooth.IBluetoothHci/default");
+ if (binder == null) {
+ Log.d(TAG, "AIDL HAL not found");
+ return null;
+ }
+ android.hardware.bluetooth.IBluetoothHci hciService = android.hardware.bluetooth.IBluetoothHci.Stub.asInterface(binder);
+ return new HciAidlHal(hciService, hciCallbacks);
+ }
+
+ private HciAidlHal(android.hardware.bluetooth.IBluetoothHci hciService, HciHalCallback hciCallbacks) {
+ super();
+ mHciService = hciService;
+ mHciCallbacks = hciCallbacks;
+ }
+
+ public Status initialize() throws RemoteException, InterruptedException {
+ // Trigger the initialization.
+ mHciService.initialize(this);
+
+ // Wait for the initialization to complete.
+ Log.d(TAG, "Waiting for initialization status...");
+ synchronized (this) {
+ while (mInitializationStatus == -1) {
+ wait();
+ }
+ }
+
+ // Map the status code.
+ switch (mInitializationStatus) {
+ case android.hardware.bluetooth.Status.SUCCESS:
+ return Status.SUCCESS;
+
+ case android.hardware.bluetooth.Status.ALREADY_INITIALIZED:
+ return Status.ALREADY_INITIALIZED;
+
+ case android.hardware.bluetooth.Status.UNABLE_TO_OPEN_INTERFACE:
+ return Status.UNABLE_TO_OPEN_INTERFACE;
+
+ case android.hardware.bluetooth.Status.HARDWARE_INITIALIZATION_ERROR:
+ return Status.INITIALIZATION_ERROR;
+
+ default:
+ return Status.UNKNOWN;
+ }
+ }
+
+ // HciHal methods.
+ @Override
+ public void sendPacket(HciPacket.Type type, byte[] packet) {
+ try {
+ switch (type) {
+ case COMMAND:
+ mHciService.sendHciCommand(packet);
+ break;
+
+ case ACL_DATA:
+ mHciService.sendAclData(packet);
+ break;
+
+ case SCO_DATA:
+ mHciService.sendScoData(packet);
+ break;
+
+ case ISO_DATA:
+ mHciService.sendIsoData(packet);
+ break;
+ }
+ } catch (RemoteException error) {
+ Log.w(TAG, "failed to forward packet: " + error);
+ }
+ }
+
+ // IBluetoothHciCallbacks methods.
+ @Override
+ public synchronized void initializationComplete(int status) throws RemoteException {
+ mInitializationStatus = status;
+ notifyAll();
+ }
+
+ @Override
+ public void hciEventReceived(byte[] event) throws RemoteException {
+ mHciCallbacks.onPacket(HciPacket.Type.EVENT, event);
+ }
+
+ @Override
+ public void aclDataReceived(byte[] data) throws RemoteException {
+ mHciCallbacks.onPacket(HciPacket.Type.ACL_DATA, data);
+ }
+
+ @Override
+ public void scoDataReceived(byte[] data) throws RemoteException {
+ mHciCallbacks.onPacket(HciPacket.Type.SCO_DATA, data);
+ }
+
+ @Override
+ public void isoDataReceived(byte[] data) throws RemoteException {
+ mHciCallbacks.onPacket(HciPacket.Type.ISO_DATA, data);
+ }
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciHalCallback.java b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciHalCallback.java
new file mode 100644
index 0000000..5878fc1
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciHalCallback.java
@@ -0,0 +1,5 @@
+package com.github.google.bumble.remotehci;
+
+public interface HciHalCallback {
+ public void onPacket(HciPacket.Type type, byte[] packet);
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciPacket.java b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciPacket.java
new file mode 100644
index 0000000..2f3162b
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciPacket.java
@@ -0,0 +1,69 @@
+package com.github.google.bumble.remotehci;
+
+import java.util.ArrayList;
+
+public class HciPacket {
+ public enum Type {
+ COMMAND((byte) 1),
+ ACL_DATA((byte) 2),
+ SCO_DATA((byte) 3),
+ EVENT((byte) 4),
+ ISO_DATA((byte)5);
+
+ final byte value;
+ final int lengthSize;
+ final int lengthOffset;
+
+ Type(byte value) throws IllegalArgumentException {
+ switch (value) {
+ case 1:
+ case 3:
+ lengthSize = 1;
+ lengthOffset = 2;
+ break;
+
+ case 2:
+ case 5:
+ lengthSize = 2;
+ lengthOffset = 2;
+ break;
+
+ case 4:
+ lengthSize = 1;
+ lengthOffset = 1;
+ break;
+
+ default:
+ throw new IllegalArgumentException();
+
+ }
+ this.value = value;
+ }
+
+ static Type fromValue(byte value) {
+ for (Type type : values()) {
+ if (type.value == value) {
+ return type;
+ }
+ }
+ return null;
+ }
+ }
+
+ public static ArrayList<Byte> byteArrayToList(byte[] byteArray) {
+ ArrayList<Byte> list = new ArrayList<>();
+ list.ensureCapacity(byteArray.length);
+ for (byte x : byteArray) {
+ list.add(x);
+ }
+ return list;
+ }
+
+ public static byte[] listToByteArray(ArrayList<Byte> byteList) {
+ byte[] byteArray = new byte[byteList.size()];
+ for (int i = 0; i < byteList.size(); i++) {
+ byteArray[i] = byteList.get(i);
+ }
+ return byteArray;
+ }
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciParser.java b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciParser.java
new file mode 100644
index 0000000..8647468
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciParser.java
@@ -0,0 +1,83 @@
+package com.github.google.bumble.remotehci;
+
+import static java.lang.Integer.min;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+
+class HciParser {
+ Sink sink;
+ State state;
+ int bytesNeeded;
+ ByteArrayOutputStream packet = new ByteArrayOutputStream();
+ HciPacket.Type packetType;
+
+ HciParser(Sink sink) {
+ this.sink = sink;
+ reset();
+ }
+
+ void feedData(byte[] data, int dataSize) {
+ int dataOffset = 0;
+ int dataLeft = dataSize;
+
+ while (dataLeft > 0 && bytesNeeded > 0) {
+ int consumed = min(dataLeft, bytesNeeded);
+ if (state != State.NEED_TYPE) {
+ packet.write(data, dataOffset, consumed);
+ }
+ bytesNeeded -= consumed;
+
+ if (bytesNeeded == 0) {
+ if (state == State.NEED_TYPE) {
+ packetType = HciPacket.Type.fromValue(data[dataOffset]);
+ if (packetType == null) {
+ throw new InvalidFormatException();
+ }
+ bytesNeeded = packetType.lengthOffset + packetType.lengthSize;
+ state = State.NEED_LENGTH;
+ } else if (state == State.NEED_LENGTH) {
+ ByteBuffer lengthBuffer =
+ ByteBuffer.wrap(packet.toByteArray())
+ .order(ByteOrder.LITTLE_ENDIAN);
+ bytesNeeded = packetType.lengthSize == 1 ?
+ lengthBuffer.get(packetType.lengthOffset) & 0xFF :
+ lengthBuffer.getShort(packetType.lengthOffset) & 0xFFFF;
+ state = State.NEED_BODY;
+ }
+
+ // Emit a packet if one is complete.
+ if (state == State.NEED_BODY && bytesNeeded == 0) {
+ if (sink != null) {
+ sink.onPacket(packetType, packet.toByteArray());
+ }
+
+ reset();
+ }
+ }
+
+ dataOffset += consumed;
+ dataLeft -= consumed;
+ }
+ }
+
+ void reset() {
+ state = State.NEED_TYPE;
+ bytesNeeded = 1;
+ packet.reset();
+ packetType = null;
+ }
+
+ enum State {
+ NEED_TYPE, NEED_LENGTH, NEED_BODY
+ }
+
+ interface Sink {
+ void onPacket(HciPacket.Type type, byte[] packet);
+ }
+
+ static class InvalidFormatException extends RuntimeException {
+ }
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciProxy.java b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciProxy.java
new file mode 100644
index 0000000..1949a63
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciProxy.java
@@ -0,0 +1,143 @@
+package com.github.google.bumble.remotehci;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+
+public class HciProxy {
+ private static final String TAG = "HciProxy";
+ private final HciServer mServer;
+ private final Listener mListener;
+ private int mCommandPacketsReceived;
+ private int mAclPacketsReceived;
+ private int mScoPacketsReceived;
+ private int mEventPacketsSent;
+ private int mAclPacketsSent;
+ private int mScoPacketsSent;
+
+ HciProxy(int port, Listener listener) throws HalException {
+ this.mListener = listener;
+
+ // Instantiate a HAL to communicate with the hardware.
+ HciHal hciHal = HciHal.create(new HciHalCallback() {
+ @Override
+ public void onPacket(HciPacket.Type type, byte[] packet) {
+ mServer.sendPacket(type, packet);
+
+ switch (type) {
+ case EVENT:
+ mEventPacketsSent += 1;
+ break;
+
+ case ACL_DATA:
+ mAclPacketsSent += 1;
+ break;
+
+ case SCO_DATA:
+ mScoPacketsSent += 1;
+ break;
+ }
+ updateHciPacketCount();
+ }
+ });
+ if (hciHal == null) {
+ String message = "Could not instantiate a HAL instance";
+ Log.w(TAG, message);
+ throw new HalException(message);
+ }
+
+ // Initialize the HAL.
+ HciHal.Status status = null;
+ try {
+ status = hciHal.initialize();
+ } catch (RemoteException | InterruptedException e) {
+ throw new HalException("Exception while initializing");
+ }
+ if (status != HciHal.Status.SUCCESS) {
+ String message = "HAL initialization failed: " + status.label;
+ Log.w(TAG, message);
+ throw new HalException(message);
+ }
+
+ // Create a server to accept clients.
+ mServer = new HciServer(port, new HciServer.Listener() {
+ @Override
+ public void onHostConnectionState(boolean connected) {
+ mListener.onHostConnectionState(connected);
+ if (connected) {
+ mCommandPacketsReceived = 0;
+ mAclPacketsReceived = 0;
+ mScoPacketsReceived = 0;
+ mEventPacketsSent = 0;
+ mAclPacketsSent = 0;
+ mScoPacketsSent = 0;
+ updateHciPacketCount();
+ }
+ }
+
+ @Override
+ public void onMessage(String message) {
+ listener.onMessage(message);
+ }
+
+ @Override
+ public void onPacket(HciPacket.Type type, byte[] packet) {
+ Log.d(TAG, String.format("onPacket: type=%s, size=%d", type, packet.length));
+ hciHal.sendPacket(type, packet);
+
+ switch (type) {
+ case COMMAND:
+ mCommandPacketsReceived += 1;
+ break;
+
+ case ACL_DATA:
+ mAclPacketsReceived += 1;
+ break;
+
+ case SCO_DATA:
+ mScoPacketsReceived += 1;
+ break;
+ }
+ updateHciPacketCount();
+ }
+ });
+ }
+
+ public void run() throws IOException {
+ mServer.run();
+ }
+
+ private void updateHciPacketCount() {
+ mListener.onHciPacketCountChange(
+ mCommandPacketsReceived,
+ mAclPacketsReceived,
+ mScoPacketsReceived,
+ mEventPacketsSent,
+ mAclPacketsSent,
+ mScoPacketsSent
+ );
+ }
+
+ public interface Listener {
+ void onHostConnectionState(boolean connected);
+
+ void onHciPacketCountChange(
+ int commandPacketsReceived,
+ int aclPacketsReceived,
+ int scoPacketsReceived,
+ int eventPacketsSent,
+ int aclPacketsSent,
+ int scoPacketsSent
+ );
+
+ void onMessage(String message);
+ }
+
+ public static class HalException extends RuntimeException {
+ public final String message;
+ public HalException(String message) {
+ this.message = message;
+ }
+ }
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciServer.java b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciServer.java
new file mode 100644
index 0000000..a78a86a
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/HciServer.java
@@ -0,0 +1,92 @@
+package com.github.google.bumble.remotehci;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+
+public class HciServer {
+ private static final String TAG = "HciServer";
+ private static final int BUFFER_SIZE = 1024;
+ private final int mPort;
+ private final Listener mListener;
+ private OutputStream mOutputStream;
+
+ public interface Listener extends HciParser.Sink {
+ void onHostConnectionState(boolean connected);
+ void onMessage(String message);
+ }
+
+ HciServer(int port, Listener listener) {
+ this.mPort = port;
+ this.mListener = listener;
+ }
+
+ public void run() throws IOException {
+ for (;;) {
+ try {
+ loop();
+ } catch (IOException error) {
+ mListener.onMessage("Cannot listen on port " + mPort);
+ return;
+ }
+ }
+ }
+
+ private void loop() throws IOException {
+ mListener.onHostConnectionState(false);
+ try (ServerSocket serverSocket = new ServerSocket(mPort)) {
+ mListener.onMessage("Waiting for connection on port " + serverSocket.getLocalPort());
+ try (Socket clientSocket = serverSocket.accept()) {
+ mListener.onHostConnectionState(true);
+ mListener.onMessage("Connected");
+ HciParser parser = new HciParser(mListener);
+ InputStream inputStream = clientSocket.getInputStream();
+ synchronized (this) {
+ mOutputStream = clientSocket.getOutputStream();
+ }
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ try {
+ for (; ; ) {
+ int bytesRead = inputStream.read(buffer);
+ if (bytesRead < 0) {
+ Log.d(TAG, "end of stream");
+ break;
+ }
+ parser.feedData(buffer, bytesRead);
+ }
+ } catch (IOException error) {
+ Log.d(TAG, "exception in read loop: " + error);
+ }
+ }
+ } finally {
+ synchronized (this) {
+ mOutputStream = null;
+ }
+ }
+ }
+
+ public void sendPacket(HciPacket.Type type, byte[] packet) {
+ // Create a combined data buffer so we can write it out in a single call.
+ byte[] data = new byte[packet.length + 1];
+ data[0] = type.value;
+ System.arraycopy(packet, 0, data, 1, packet.length);
+
+ synchronized (this) {
+ if (mOutputStream != null) {
+ try {
+ mOutputStream.write(data);
+ } catch (IOException error) {
+ Log.w(TAG, "failed to write packet: " + error);
+ }
+ } else {
+ Log.d(TAG, "no client, dropping packet");
+ }
+ }
+ }
+}
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/MainActivity.kt b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/MainActivity.kt
new file mode 100644
index 0000000..3a2630a
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/MainActivity.kt
@@ -0,0 +1,229 @@
+package com.github.google.bumble.remotehci
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.Button
+import androidx.compose.material3.Divider
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalSoftwareKeyboardController
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.lifecycle.ViewModel
+import com.github.google.bumble.remotehci.HciProxy.HalException
+import com.github.google.bumble.remotehci.ui.theme.RemoteHCITheme
+import java.io.IOException
+import java.util.logging.Logger
+import kotlin.concurrent.thread
+
+const val DEFAULT_TCP_PORT = 9993
+const val TCP_PORT_PREF_KEY = "tcp_port"
+
+class AppViewModel : ViewModel(), HciProxy.Listener {
+ private var preferences: SharedPreferences? = null
+ var tcpPort by mutableStateOf(DEFAULT_TCP_PORT)
+ var canStart by mutableStateOf(true)
+ var message by mutableStateOf("")
+ var hostConnected by mutableStateOf(false)
+ var hciCommandPacketsReceived by mutableStateOf(0)
+ var hciAclPacketsReceived by mutableStateOf(0)
+ var hciScoPacketsReceived by mutableStateOf(0)
+ var hciEventPacketsSent by mutableStateOf(0)
+ var hciAclPacketsSent by mutableStateOf(0)
+ var hciScoPacketsSent by mutableStateOf(0)
+
+ fun loadPreferences(preferences: SharedPreferences) {
+ this.preferences = preferences
+ val savedTcpPortString = preferences.getString(TCP_PORT_PREF_KEY, null)
+ if (savedTcpPortString != null) {
+ val savedTcpPortInt = savedTcpPortString.toIntOrNull()
+ if (savedTcpPortInt != null) {
+ tcpPort = savedTcpPortInt
+ }
+ }
+ }
+
+ fun updateTcpPort(tcpPort: Int) {
+ this.tcpPort = tcpPort
+
+ // Save the port to the preferences
+ with (preferences!!.edit()) {
+ putString(TCP_PORT_PREF_KEY, tcpPort.toString())
+ apply()
+ }
+ }
+
+ override fun onHostConnectionState(connected: Boolean) {
+ hostConnected = connected
+ }
+
+ override fun onHciPacketCountChange(
+ commandPacketsReceived: Int,
+ aclPacketsReceived: Int,
+ scoPacketsReceived: Int,
+ eventPacketsSent: Int,
+ aclPacketsSent: Int,
+ scoPacketsSent: Int
+ ) {
+ hciCommandPacketsReceived = commandPacketsReceived
+ hciAclPacketsReceived = aclPacketsReceived
+ hciScoPacketsReceived = scoPacketsReceived
+ hciEventPacketsSent = eventPacketsSent
+ hciAclPacketsSent = aclPacketsSent
+ hciScoPacketsSent = scoPacketsSent
+
+ }
+
+ override fun onMessage(message: String) {
+ this.message = message
+ }
+}
+
+class MainActivity : ComponentActivity() {
+ private val log = Logger.getLogger(MainActivity::class.java.name)
+ private val appViewModel = AppViewModel()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ appViewModel.loadPreferences(getPreferences(Context.MODE_PRIVATE))
+
+ val tcpPort = intent.getIntExtra("port", -1)
+ if (tcpPort >= 0) {
+ appViewModel.tcpPort = tcpPport
+ }
+
+ setContent {
+ MainView(appViewModel, ::startProxy)
+ }
+
+ if (intent.getBooleanExtra("autostart", false)) {
+ startProxy()
+ }
+ }
+
+ private fun startProxy() {
+ // Run the proxy in a thread.
+ appViewModel.message = ""
+ thread {
+ log.info("HCI Proxy thread starting")
+ appViewModel.canStart = false
+ try {
+ val hciProxy = HciProxy(appViewModel.tcpPort, appViewModel)
+ hciProxy.run()
+ } catch (error: IOException) {
+ log.warning("Exception while running HCI Server: $error")
+ } catch (error: HalException) {
+ log.warning("HAL exception: ${error.message}")
+ appViewModel.message = "Cannot bind to HAL (${error.message}). You may need to use the command 'setenforce 0' in a root adb shell."
+ }
+ log.info("HCI Proxy thread ended")
+ appViewModel.canStart = true
+ }
+ }
+}
+
+@Composable
+fun ActionButton(text: String, onClick: () -> Unit, enabled: Boolean) {
+ Button(onClick = onClick, enabled = enabled) {
+ Text(text = text)
+ }
+}
+
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
+@Composable
+fun MainView(appViewModel: AppViewModel, startProxy: () -> Unit) {
+ RemoteHCITheme {
+ // A surface container using the 'background' color from the theme
+ Surface(
+ modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background
+ ) {
+ Column(modifier = Modifier.padding(horizontal = 16.dp)) {
+ Text(
+ text = "Bumble Remote HCI",
+ fontSize = 24.sp,
+ fontWeight = FontWeight.Bold,
+ textAlign = TextAlign.Center
+ )
+ Divider()
+ Text(
+ text = appViewModel.message
+ )
+ Divider()
+ val keyboardController = LocalSoftwareKeyboardController.current
+ TextField(
+ label = {
+ Text(text = "TCP Port")
+ },
+ value = appViewModel.tcpPort.toString(),
+ modifier = Modifier.fillMaxWidth(),
+ keyboardOptions = KeyboardOptions.Default.copy(keyboardType = KeyboardType.Number, imeAction = ImeAction.Done),
+ onValueChange = {
+ if (it.isNotEmpty()) {
+ val tcpPort = it.toIntOrNull()
+ if (tcpPort != null) {
+ appViewModel.updateTcpPort(tcpPort)
+ }
+ }
+ },
+ keyboardActions = KeyboardActions(
+ onDone = {keyboardController?.hide()}
+ )
+ )
+ Divider()
+ val connectState = if (appViewModel.hostConnected) "CONNECTED" else "DISCONNECTED"
+ Text(
+ text = "HOST: $connectState",
+ modifier = Modifier.background(color = if (appViewModel.hostConnected) Color.Green else Color.Red),
+ color = Color.Black
+ )
+ Divider()
+ Text(
+ text = "Command Packets Received: ${appViewModel.hciCommandPacketsReceived}"
+ )
+ Text(
+ text = "ACL Packets Received: ${appViewModel.hciAclPacketsReceived}"
+ )
+ Text(
+ text = "SCO Packets Received: ${appViewModel.hciScoPacketsReceived}"
+ )
+ Text(
+ text = "Event Packets Sent: ${appViewModel.hciEventPacketsSent}"
+ )
+ Text(
+ text = "ACL Packets Sent: ${appViewModel.hciAclPacketsSent}"
+ )
+ Text(
+ text = "SCO Packets Sent: ${appViewModel.hciScoPacketsSent}"
+ )
+ Divider()
+ ActionButton(
+ text = "Start", onClick = startProxy, enabled = appViewModel.canStart
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Color.kt b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Color.kt
new file mode 100644
index 0000000..420958d
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Color.kt
@@ -0,0 +1,11 @@
+package com.github.google.bumble.remotehci.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Theme.kt b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Theme.kt
new file mode 100644
index 0000000..8d707c9
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Theme.kt
@@ -0,0 +1,70 @@
+package com.github.google.bumble.remotehci.ui.theme
+
+import android.app.Activity
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.SideEffect
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
+import androidx.core.view.WindowCompat
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun RemoteHCITheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+ val view = LocalView.current
+ if (!view.isInEditMode) {
+ SideEffect {
+ val window = (view.context as Activity).window
+ window.statusBarColor = colorScheme.primary.toArgb()
+ WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
+ }
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Type.kt b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Type.kt
new file mode 100644
index 0000000..5017af2
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/java/com/github/google/bumble/remotehci/ui/theme/Type.kt
@@ -0,0 +1,34 @@
+package com.github.google.bumble.remotehci.ui.theme
+
+import androidx.compose.material3.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ bodyLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
+ )
+ /* Other default text styles to override
+ titleLarge = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
+ ),
+ labelSmall = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
+ )
+ */
+)
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/extras/android/RemoteHCI/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/extras/android/RemoteHCI/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..036d09b
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..f01f61e
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..bb2143e
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1d9c6ae
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..2a80e30
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..9764352
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..45f205b
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..c607bc0
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..bb2d0f8
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..54c5d2f
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..9afe565
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..a5f9571
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..142c967
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..c9792ad
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
new file mode 100644
index 0000000..434da3f
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b48797d
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/extras/android/RemoteHCI/app/src/main/res/values/colors.xml b/extras/android/RemoteHCI/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_200">#FFBB86FC</color>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/values/ic_launcher_background.xml b/extras/android/RemoteHCI/app/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 0000000..c5d5899
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="ic_launcher_background">#FFFFFF</color>
+</resources>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/values/strings.xml b/extras/android/RemoteHCI/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..af77941
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Remote HCI</string>
+</resources>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/values/themes.xml b/extras/android/RemoteHCI/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..cd813bf
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/values/themes.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="Theme.RemoteHCI" parent="android:Theme.Material.Light.NoActionBar" />
+</resources>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/xml/backup_rules.xml b/extras/android/RemoteHCI/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+ <!--
+ <include domain="sharedpref" path="."/>
+ <exclude domain="sharedpref" path="device.xml"/>
+-->
+</full-backup-content>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/app/src/main/res/xml/data_extraction_rules.xml b/extras/android/RemoteHCI/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/extras/android/RemoteHCI/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ <!-- TODO: Use <include> and <exclude> to control what is backed up.
+ <include .../>
+ <exclude .../>
+ -->
+ </cloud-backup>
+ <!--
+ <device-transfer>
+ <include .../>
+ <exclude .../>
+ </device-transfer>
+ -->
+</data-extraction-rules>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/build.gradle.kts b/extras/android/RemoteHCI/build.gradle.kts
new file mode 100644
index 0000000..3a03bf6
--- /dev/null
+++ b/extras/android/RemoteHCI/build.gradle.kts
@@ -0,0 +1,8 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+ alias(libs.plugins.androidApplication) apply false
+ alias(libs.plugins.kotlinAndroid) apply false
+ alias(libs.plugins.androidLibrary) apply false
+}
+true // Needed to make the Suppress annotation work for the plugins block
diff --git a/extras/android/RemoteHCI/gradle.properties b/extras/android/RemoteHCI/gradle.properties
new file mode 100644
index 0000000..2cbd6d1
--- /dev/null
+++ b/extras/android/RemoteHCI/gradle.properties
@@ -0,0 +1,23 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
diff --git a/extras/android/RemoteHCI/gradle/libs.versions.toml b/extras/android/RemoteHCI/gradle/libs.versions.toml
new file mode 100644
index 0000000..cc1b0f5
--- /dev/null
+++ b/extras/android/RemoteHCI/gradle/libs.versions.toml
@@ -0,0 +1,36 @@
+[versions]
+agp = "8.3.0-alpha05"
+kotlin = "1.8.10"
+core-ktx = "1.9.0"
+junit = "4.13.2"
+androidx-test-ext-junit = "1.1.5"
+espresso-core = "3.5.1"
+lifecycle-runtime-ktx = "2.6.1"
+activity-compose = "1.7.2"
+compose-bom = "2023.03.00"
+appcompat = "1.6.1"
+material = "1.9.0"
+
+[libraries]
+core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
+espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
+lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-ktx" }
+activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
+compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
+ui = { group = "androidx.compose.ui", name = "ui" }
+ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+material3 = { group = "androidx.compose.material3", name = "material3" }
+appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+
+[plugins]
+androidApplication = { id = "com.android.application", version.ref = "agp" }
+kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+androidLibrary = { id = "com.android.library", version.ref = "agp" }
+
diff --git a/extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.jar b/extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.properties b/extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..d58714b
--- /dev/null
+++ b/extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Aug 06 12:53:26 PDT 2023
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-rc-2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/extras/android/RemoteHCI/gradlew b/extras/android/RemoteHCI/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/extras/android/RemoteHCI/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+# https://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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/extras/android/RemoteHCI/gradlew.bat b/extras/android/RemoteHCI/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/extras/android/RemoteHCI/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/extras/android/RemoteHCI/lib/.gitignore b/extras/android/RemoteHCI/lib/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/lib/build.gradle.kts b/extras/android/RemoteHCI/lib/build.gradle.kts
new file mode 100644
index 0000000..70ab0d4
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/build.gradle.kts
@@ -0,0 +1,39 @@
+@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
+plugins {
+ alias(libs.plugins.androidLibrary)
+}
+
+android {
+ namespace = "com.github.google.bumble"
+ compileSdk = 34
+
+ defaultConfig {
+ minSdk = 26
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+
+ implementation(libs.appcompat)
+ implementation(libs.material)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.test.ext.junit)
+ androidTestImplementation(libs.espresso.core)
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/lib/consumer-rules.pro b/extras/android/RemoteHCI/lib/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/consumer-rules.pro
diff --git a/extras/android/RemoteHCI/lib/proguard-rules.pro b/extras/android/RemoteHCI/lib/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/lib/src/main/AndroidManifest.xml b/extras/android/RemoteHCI/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..a5918e6
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/internal/hidl/base/V1_0/DebugInfo.java b/extras/android/RemoteHCI/lib/src/main/java/android/internal/hidl/base/V1_0/DebugInfo.java
new file mode 100644
index 0000000..cceffde
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/internal/hidl/base/V1_0/DebugInfo.java
@@ -0,0 +1,17 @@
+package android.internal.hidl.base.V1_0;
+
+import android.os.HwParcel;
+
+public class DebugInfo {
+ public static final class Architecture {
+ public static final int UNKNOWN = 0;
+ }
+
+ public int pid = 0;
+ public long ptr = 0L;
+ public int arch = 0;
+ public final void readFromParcel(HwParcel parcel) {
+ }
+ public final void writeToParcel(HwParcel parcel) {
+ }
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/internal/hidl/base/V1_0/IBase.java b/extras/android/RemoteHCI/lib/src/main/java/android/internal/hidl/base/V1_0/IBase.java
new file mode 100644
index 0000000..eb03a26
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/internal/hidl/base/V1_0/IBase.java
@@ -0,0 +1,20 @@
+package android.internal.hidl.base.V1_0;
+
+import android.os.IHwBinder;
+import android.os.IHwInterface;
+
+public interface IBase extends IHwInterface {
+ public static final String kInterfaceName = "[email protected]::IBase";
+
+ public static abstract class Stub extends android.os.HwBinder implements IBase {
+ public void onTransact(int _hidl_code, android.os.HwParcel _hidl_request, final android.os.HwParcel _hidl_reply, int _hidl_flags)
+ throws android.os.RemoteException {}
+ }
+
+ public static final class Proxy implements IBase {
+ @Override
+ public IHwBinder asBinder() {
+ return null;
+ }
+ }
+}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/HidlSupport.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/HidlSupport.java
new file mode 100644
index 0000000..c14e70f
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/HidlSupport.java
@@ -0,0 +1,8 @@
+package android.os;
+
+public class HidlSupport {
+ public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
+ return false; // STUB
+ }
+ public static native int getPidIfSharable();
+}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/HwBinder.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/HwBinder.java
new file mode 100644
index 0000000..101a02d
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/HwBinder.java
@@ -0,0 +1,40 @@
+package android.os;
+
+public class HwBinder implements IHwBinder {
+ public native final void registerService(String serviceName);
+ public static final IHwBinder getService(
+ String iface,
+ String serviceName) {
+ return null; //STUB
+ }
+
+ public static final IHwBinder getService(
+ String iface,
+ String serviceName,
+ boolean retry) {
+ return null; // STUB
+ }
+
+ public static void enableInstrumentation() {
+
+ }
+ @Override
+ public IHwInterface queryLocalInterface(String descriptor) {
+ return null; // STUB
+ }
+
+ @Override
+ public void transact(int code, HwParcel request, HwParcel reply, int flags) {
+
+ }
+
+ @Override
+ public boolean linkToDeath(DeathRecipient recipient, long cookie) {
+ return false;
+ }
+
+ @Override
+ public boolean unlinkToDeath(DeathRecipient recipient) {
+ return false;
+ }
+}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/HwBlob.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/HwBlob.java
new file mode 100644
index 0000000..5639fb9
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/HwBlob.java
@@ -0,0 +1,13 @@
+package android.os;
+
+public class HwBlob {
+ public HwBlob(int size) {}
+ public native final long handle();
+
+ public native final int getInt32(long offset);
+ public native final void putInt32(long offset, int x);
+ public native final void putBool(long offset, boolean x);
+ public native final void putInt8Array(long offset, byte[] x);
+ public native final void putBlob(long offset, HwBlob blob);
+ public native final void copyToInt8Array(long offset, byte[] array, int size);
+}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/HwParcel.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/HwParcel.java
new file mode 100644
index 0000000..eac0e69
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/HwParcel.java
@@ -0,0 +1,107 @@
+package android.os;
+
+import java.util.ArrayList;
+
+public class HwParcel {
+ public static final int STATUS_SUCCESS = 0;
+ public native final void writeInterfaceToken(String interfaceName);
+ public native final void writeBool(boolean val);
+ public native final void writeInt8(byte val);
+ public native final void writeInt16(short val);
+ public native final void writeInt32(int val);
+ public native final void writeInt64(long val);
+ public native final void writeFloat(float val);
+ public native final void writeDouble(double val);
+ public native final void writeString(String val);
+ public native final void writeNativeHandle(NativeHandle val);
+ private native final void writeBoolVector(boolean[] val);
+ private native final void writeInt8Vector(byte[] val);
+ private native final void writeInt16Vector(short[] val);
+ private native final void writeInt32Vector(int[] val);
+ private native final void writeInt64Vector(long[] val);
+ private native final void writeFloatVector(float[] val);
+ private native final void writeDoubleVector(double[] val);
+ private native final void writeStringVector(String[] val);
+ private native final void writeNativeHandleVector(NativeHandle[] val);
+ public final void writeBoolVector(ArrayList<Boolean> val) {
+ }
+ public final void writeInt8Vector(ArrayList<Byte> val) {
+ }
+ public final void writeInt16Vector(ArrayList<Short> val) {
+ }
+ public final void writeInt32Vector(ArrayList<Integer> val) {
+ }
+ public final void writeInt64Vector(ArrayList<Long> val) {
+ }
+ public final void writeFloatVector(ArrayList<Float> val) {
+ }
+ public final void writeDoubleVector(ArrayList<Double> val) {
+ }
+ public final void writeStringVector(ArrayList<String> val) {
+ }
+ public final void writeNativeHandleVector(ArrayList<NativeHandle> val) {
+ }
+ public native final void writeStrongBinder(IHwBinder binder);
+ //public native final void writeHidlMemory(HidlMemory memory);
+ public native final void enforceInterface(String interfaceName);
+ public native final boolean readBool();
+ public native final byte readInt8();
+ public native final short readInt16();
+ public native final int readInt32();
+ public native final long readInt64();
+ public native final float readFloat();
+ public native final double readDouble();
+ public native final String readString();
+ public native final NativeHandle readNativeHandle();
+ public native final NativeHandle readEmbeddedNativeHandle(
+ long parentHandle, long offset);
+ private native final boolean[] readBoolVectorAsArray();
+ private native final byte[] readInt8VectorAsArray();
+ private native final short[] readInt16VectorAsArray();
+ private native final int[] readInt32VectorAsArray();
+ private native final long[] readInt64VectorAsArray();
+ private native final float[] readFloatVectorAsArray();
+ private native final double[] readDoubleVectorAsArray();
+ private native final String[] readStringVectorAsArray();
+ private native final NativeHandle[] readNativeHandleAsArray();
+ public final ArrayList<Boolean> readBoolVector() {
+ return null;
+ }
+ public final ArrayList<Byte> readInt8Vector() {
+ return null;
+ }
+ public final ArrayList<Short> readInt16Vector() {
+ return null;
+ }
+ public final ArrayList<Integer> readInt32Vector() {
+ return null;
+ }
+ public final ArrayList<Long> readInt64Vector() {
+ return null;
+ }
+ public final ArrayList<Float> readFloatVector() {
+ return null;
+ }
+ public final ArrayList<Double> readDoubleVector() {
+ return null;
+ }
+ public final ArrayList<String> readStringVector() {
+ return null;
+ }
+ public final ArrayList<NativeHandle> readNativeHandleVector() {
+ return null;
+ }
+ public native final IHwBinder readStrongBinder();
+// public native final HidlMemory readHidlMemory();
+// public native final
+// HidlMemory readEmbeddedHidlMemory(long fieldHandle, long parentHandle, long offset);
+ public native final HwBlob readBuffer(long expectedSize);
+ public native final HwBlob readEmbeddedBuffer(
+ long expectedSize, long parentHandle, long offset,
+ boolean nullable);
+ public native final void writeBuffer(HwBlob blob);
+ public native final void writeStatus(int status);
+ public native final void verifySuccess();
+ public native final void releaseTemporaryStorage();
+ public native final void release();
+ public native final void send();}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/IHwBinder.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/IHwBinder.java
new file mode 100644
index 0000000..46e9515
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/IHwBinder.java
@@ -0,0 +1,11 @@
+package android.os;
+
+public interface IHwBinder {
+ public interface DeathRecipient {
+ public void serviceDied(long cookie);
+ }
+ public IHwInterface queryLocalInterface(String descriptor);
+ public void transact(int code, HwParcel request, HwParcel reply, int flags);
+ public boolean linkToDeath(DeathRecipient recipient, long cookie);
+ public boolean unlinkToDeath(DeathRecipient recipient);
+}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/IHwInterface.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/IHwInterface.java
new file mode 100644
index 0000000..9a4de57
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/IHwInterface.java
@@ -0,0 +1,5 @@
+package android.os;
+
+public interface IHwInterface {
+ public IHwBinder asBinder();
+}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/NativeHandle.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/NativeHandle.java
new file mode 100644
index 0000000..b6ec8c7
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/NativeHandle.java
@@ -0,0 +1,4 @@
+package android.os;
+
+public class NativeHandle {
+}
diff --git a/extras/android/RemoteHCI/lib/src/main/java/android/os/ServiceManager.java b/extras/android/RemoteHCI/lib/src/main/java/android/os/ServiceManager.java
new file mode 100644
index 0000000..5db2047
--- /dev/null
+++ b/extras/android/RemoteHCI/lib/src/main/java/android/os/ServiceManager.java
@@ -0,0 +1,7 @@
+package android.os;
+
+public final class ServiceManager {
+ public static IBinder getService(String name) {
+ return null; // Stub
+ }
+}
\ No newline at end of file
diff --git a/extras/android/RemoteHCI/scripts/generate_java_from_aidl.sh b/extras/android/RemoteHCI/scripts/generate_java_from_aidl.sh
new file mode 100644
index 0000000..1f39821
--- /dev/null
+++ b/extras/android/RemoteHCI/scripts/generate_java_from_aidl.sh
@@ -0,0 +1,32 @@
+# Run this script to generate the .java files from the .aidl files
+# then replace `this.markVintfStability()` with:
+# try {
+# Method method = this.getClass().getMethod("markVintfStability", null);
+# method.invoke(this);
+# } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+# throw new RuntimeException(e);
+# }
+
+AIDL=$ANDROID_SDK_ROOT/build-tools/34.0.0/aidl
+
+$AIDL \
+-oapp/src/main/java \
+-Iapp/src/main/aidl \
+--stability=vintf \
+--structured \
+app/src/main/aidl/android/hardware/bluetooth/IBluetoothHci.aidl
+
+$AIDL \
+-oapp/src/main/java \
+-Iapp/src/main/aidl \
+--stability=vintf \
+--structured \
+app/src/main/aidl/android/hardware/bluetooth/IBluetoothHciCallbacks.aidl
+
+$AIDL \
+-oapp/src/main/java \
+-Iapp/src/main/aidl \
+--stability=vintf \
+--structured \
+app/src/main/aidl/android/hardware/bluetooth/Status.aidl
+
diff --git a/extras/android/RemoteHCI/settings.gradle.kts b/extras/android/RemoteHCI/settings.gradle.kts
new file mode 100644
index 0000000..0edc3ec
--- /dev/null
+++ b/extras/android/RemoteHCI/settings.gradle.kts
@@ -0,0 +1,18 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+rootProject.name = "RemoteHCI"
+include(":app")
+include(":lib")
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index c2d0cd3..c443561 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -81,6 +81,37 @@
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
+name = "argh"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219"
+dependencies = [
+ "argh_derive",
+ "argh_shared",
+]
+
+[[package]]
+name = "argh_derive"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a"
+dependencies = [
+ "argh_shared",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "argh_shared"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -131,6 +162,15 @@
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "bstr"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -145,6 +185,7 @@
version = "0.1.0"
dependencies = [
"anyhow",
+ "bytes",
"clap 4.4.1",
"directories",
"env_logger",
@@ -158,6 +199,8 @@
"nix",
"nom",
"owo-colors",
+ "pdl-derive",
+ "pdl-runtime",
"pyo3",
"pyo3-asyncio",
"rand",
@@ -178,9 +221,9 @@
[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cc"
@@ -263,6 +306,16 @@
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -285,6 +338,15 @@
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
+name = "cpufeatures"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "crossbeam"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -352,6 +414,26 @@
]
[[package]]
+name = "crypto-common"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
name = "directories"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -560,6 +642,16 @@
]
[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1065,12 +1157,100 @@
]
[[package]]
+name = "pdl-compiler"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee66995739fb9ddd9155767990a54aadd226ee32408a94f99f94883ff445ceba"
+dependencies = [
+ "argh",
+ "codespan-reporting",
+ "heck",
+ "pest",
+ "pest_derive",
+ "prettyplease",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "pdl-derive"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "113e4a1215c407466b36d2c2f6a6318819d6b22ccdd3acb7bb35e27a68806034"
+dependencies = [
+ "codespan-reporting",
+ "pdl-compiler",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+ "termcolor",
+]
+
+[[package]]
+name = "pdl-runtime"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d36c2f9799613babe78eb5cd9a353d527daaba6c3d1f39a1175657a35790732"
+dependencies = [
+ "bytes",
+ "thiserror",
+]
+
+[[package]]
name = "percent-encoding"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
[[package]]
+name = "pest"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c022f1e7b65d6a24c0dbbd5fb344c66881bc01f3e5ae74a1c8100f2f985d98a4"
+dependencies = [
+ "memchr",
+ "thiserror",
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35513f630d46400a977c4cb58f78e1bfbe01434316e60c37d27b9ad6139c66d8"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc9fc1b9e7057baba189b5c626e2d6f40681ae5b6eb064dc7c7834101ec8123a"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.29",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df74e9e7ec4053ceb980e7c0c8bd3594e977fde1af91daba9c928e8e8c6708d"
+dependencies = [
+ "once_cell",
+ "pest",
+ "sha2",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1095,6 +1275,16 @@
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
+name = "prettyplease"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62"
+dependencies = [
+ "proc-macro2",
+ "syn 2.0.29",
+]
+
+[[package]]
name = "proc-macro2"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1466,6 +1656,17 @@
]
[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1712,6 +1913,18 @@
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
+name = "typenum"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+
+[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1739,6 +1952,12 @@
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
+name = "unicode-width"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
+
+[[package]]
name = "unindent"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1768,6 +1987,12 @@
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index a553afd..35a0f4c 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -23,6 +23,9 @@
itertools = "0.11.0"
lazy_static = "1.4.0"
thiserror = "1.0.41"
+bytes = "1.5.0"
+pdl-derive = "0.2.0"
+pdl-runtime = "0.2.0"
# Dev tools
file-header = { version = "0.1.2", optional = true }
diff --git a/rust/examples/scanner.rs b/rust/examples/scanner.rs
index 1b68ea5..3c328ed 100644
--- a/rust/examples/scanner.rs
+++ b/rust/examples/scanner.rs
@@ -20,7 +20,8 @@
use bumble::{
adv::CommonDataType,
wrapper::{
- core::AdvertisementDataUnit, device::Device, hci::AddressType, transport::Transport,
+ core::AdvertisementDataUnit, device::Device, hci::packets::AddressType,
+ transport::Transport,
},
};
use clap::Parser as _;
@@ -69,9 +70,7 @@
let mut seen_adv_cache = seen_adv_clone.lock().unwrap();
let expiry_duration = time::Duration::from_secs(cli.dedup_expiry_secs);
- let advs_from_addr = seen_adv_cache
- .entry(addr_bytes)
- .or_insert_with(collections::HashMap::new);
+ let advs_from_addr = seen_adv_cache.entry(addr_bytes).or_default();
// we expect cache hits to be the norm, so we do a separate lookup to avoid cloning
// on every lookup with entry()
let show = if let Some(prev) = advs_from_addr.get_mut(&data_units) {
@@ -102,7 +101,9 @@
};
let (type_style, qualifier) = match adv.address()?.address_type()? {
- AddressType::PublicIdentity | AddressType::PublicDevice => (Style::new().cyan(), ""),
+ AddressType::PublicIdentityAddress | AddressType::PublicDeviceAddress => {
+ (Style::new().cyan(), "")
+ }
_ => {
if addr.is_static()? {
(Style::new().green(), "(static)")
diff --git a/rust/pytests/wrapper.rs b/rust/pytests/wrapper.rs
index 8f69dd7..9fd65e7 100644
--- a/rust/pytests/wrapper.rs
+++ b/rust/pytests/wrapper.rs
@@ -12,9 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use bumble::wrapper::{drivers::rtk::DriverInfo, transport::Transport};
+use bumble::wrapper::{
+ controller::Controller,
+ device::Device,
+ drivers::rtk::DriverInfo,
+ hci::{
+ packets::{
+ AddressType, ErrorCode, ReadLocalVersionInformationBuilder,
+ ReadLocalVersionInformationComplete,
+ },
+ Address, Error,
+ },
+ host::Host,
+ link::Link,
+ transport::Transport,
+};
use nix::sys::stat::Mode;
-use pyo3::PyResult;
+use pyo3::{
+ exceptions::PyException,
+ {PyErr, PyResult},
+};
#[pyo3_asyncio::tokio::test]
async fn fifo_transport_can_open() -> PyResult<()> {
@@ -35,3 +52,26 @@
assert_eq!(12, DriverInfo::all_drivers()?.len());
Ok(())
}
+
+#[pyo3_asyncio::tokio::test]
+async fn hci_command_wrapper_has_correct_methods() -> PyResult<()> {
+ let address = Address::new("F0:F1:F2:F3:F4:F5", &AddressType::RandomDeviceAddress)?;
+ let link = Link::new_local_link()?;
+ let controller = Controller::new("C1", None, None, Some(link), Some(address.clone())).await?;
+ let host = Host::new(controller.clone().into(), controller.into()).await?;
+ let device = Device::new(None, Some(address), None, Some(host), None)?;
+
+ device.power_on().await?;
+
+ // Send some simple command. A successful response means [HciCommandWrapper] has the minimum
+ // required interface for the Python code to think its an [HCI_Command] object.
+ let command = ReadLocalVersionInformationBuilder {};
+ let event: ReadLocalVersionInformationComplete = device
+ .send_command(&command.into(), true)
+ .await?
+ .try_into()
+ .map_err(|e: Error| PyErr::new::<PyException, _>(e.to_string()))?;
+
+ assert_eq!(ErrorCode::Success, event.get_status());
+ Ok(())
+}
diff --git a/rust/src/cli/firmware/rtk.rs b/rust/src/cli/firmware/rtk.rs
index f5524a4..1028564 100644
--- a/rust/src/cli/firmware/rtk.rs
+++ b/rust/src/cli/firmware/rtk.rs
@@ -179,7 +179,7 @@
pub(crate) async fn info(transport: &str, force: bool) -> PyResult<()> {
let transport = Transport::open(transport).await?;
- let mut host = Host::new(transport.source()?, transport.sink()?)?;
+ let mut host = Host::new(transport.source()?, transport.sink()?).await?;
host.reset(DriverFactory::None).await?;
if !force && !Driver::check(&host).await? {
@@ -203,7 +203,7 @@
pub(crate) async fn load(transport: &str, force: bool) -> PyResult<()> {
let transport = Transport::open(transport).await?;
- let mut host = Host::new(transport.source()?, transport.sink()?)?;
+ let mut host = Host::new(transport.source()?, transport.sink()?).await?;
host.reset(DriverFactory::None).await?;
match Driver::for_host(&host, force).await? {
@@ -219,7 +219,7 @@
pub(crate) async fn drop(transport: &str) -> PyResult<()> {
let transport = Transport::open(transport).await?;
- let mut host = Host::new(transport.source()?, transport.sink()?)?;
+ let mut host = Host::new(transport.source()?, transport.sink()?).await?;
host.reset(DriverFactory::None).await?;
Driver::drop_firmware(&mut host).await?;
diff --git a/rust/src/cli/l2cap/client_bridge.rs b/rust/src/cli/l2cap/client_bridge.rs
index 37606fc..31bc021 100644
--- a/rust/src/cli/l2cap/client_bridge.rs
+++ b/rust/src/cli/l2cap/client_bridge.rs
@@ -21,8 +21,7 @@
/// TCP client to connect.
/// When the L2CAP CoC channel is closed, the TCP connection is closed as well.
use crate::cli::l2cap::{
- proxy_l2cap_rx_to_tcp_tx, proxy_tcp_rx_to_l2cap_tx, run_future_with_current_task_locals,
- BridgeData,
+ inject_py_event_loop, proxy_l2cap_rx_to_tcp_tx, proxy_tcp_rx_to_l2cap_tx, BridgeData,
};
use bumble::wrapper::{
device::{Connection, Device},
@@ -85,11 +84,12 @@
let mtu = args.mtu;
let mps = args.mps;
let ble_connection = Arc::new(Mutex::new(ble_connection));
- // Ensure Python event loop is available to l2cap `disconnect`
- let _ = run_future_with_current_task_locals(async move {
+ // spawn thread to handle incoming tcp connections
+ tokio::spawn(inject_py_event_loop(async move {
while let Ok((tcp_stream, addr)) = listener.accept().await {
let ble_connection = ble_connection.clone();
- let _ = run_future_with_current_task_locals(proxy_data_between_tcp_and_l2cap(
+ // spawn thread to handle this specific tcp connection
+ if let Ok(future) = inject_py_event_loop(proxy_data_between_tcp_and_l2cap(
ble_connection,
tcp_stream,
addr,
@@ -97,10 +97,11 @@
max_credits,
mtu,
mps,
- ));
+ )) {
+ tokio::spawn(future);
+ }
}
- Ok(())
- });
+ })?);
Ok(())
}
diff --git a/rust/src/cli/l2cap/mod.rs b/rust/src/cli/l2cap/mod.rs
index 31097ed..3f86b24 100644
--- a/rust/src/cli/l2cap/mod.rs
+++ b/rust/src/cli/l2cap/mod.rs
@@ -18,7 +18,7 @@
use anyhow::anyhow;
use bumble::wrapper::{device::Device, l2cap::LeConnectionOrientedChannel, transport::Transport};
use owo_colors::{colors::css::Orange, OwoColorize};
-use pyo3::{PyObject, PyResult, Python};
+use pyo3::{PyResult, Python};
use std::{future::Future, path::PathBuf, sync::Arc};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
@@ -170,21 +170,12 @@
}
}
-/// Copies the current thread's TaskLocals into a Python "awaitable" and encapsulates it in a Rust
-/// future, running it as a Python Task.
-/// `TaskLocals` stores the current event loop, and allows the user to copy the current Python
-/// context if necessary. In this case, the python event loop is used when calling `disconnect` on
-/// an l2cap connection, or else the call will fail.
-pub fn run_future_with_current_task_locals<F>(
- fut: F,
-) -> PyResult<impl Future<Output = PyResult<PyObject>> + Send>
+/// Copies the current thread's Python even loop (contained in `TaskLocals`) into the given future.
+/// Useful when sending work to another thread that calls Python code which calls `get_running_loop()`.
+pub fn inject_py_event_loop<F, R>(fut: F) -> PyResult<impl Future<Output = R>>
where
- F: Future<Output = PyResult<()>> + Send + 'static,
+ F: Future<Output = R> + Send + 'static,
{
- Python::with_gil(|py| {
- let locals = pyo3_asyncio::tokio::get_current_locals(py)?;
- let future = pyo3_asyncio::tokio::scope(locals.clone(), fut);
- pyo3_asyncio::tokio::future_into_py_with_locals(py, locals, future)
- .and_then(pyo3_asyncio::tokio::into_future)
- })
+ let locals = Python::with_gil(pyo3_asyncio::tokio::get_current_locals)?;
+ Ok(pyo3_asyncio::tokio::scope(locals, fut))
}
diff --git a/rust/src/cli/l2cap/server_bridge.rs b/rust/src/cli/l2cap/server_bridge.rs
index 3a32db9..3f8041a 100644
--- a/rust/src/cli/l2cap/server_bridge.rs
+++ b/rust/src/cli/l2cap/server_bridge.rs
@@ -19,10 +19,7 @@
/// When the L2CAP CoC channel is closed, the bridge disconnects the TCP socket
/// and waits for a new L2CAP CoC channel to be connected.
/// When the TCP connection is closed by the TCP server, the L2CAP connection is closed as well.
-use crate::cli::l2cap::{
- proxy_l2cap_rx_to_tcp_tx, proxy_tcp_rx_to_l2cap_tx, run_future_with_current_task_locals,
- BridgeData,
-};
+use crate::cli::l2cap::{proxy_l2cap_rx_to_tcp_tx, proxy_tcp_rx_to_l2cap_tx, BridgeData};
use bumble::wrapper::{device::Device, hci::HciConstant, l2cap::LeConnectionOrientedChannel};
use futures::executor::block_on;
use owo_colors::OwoColorize;
@@ -49,19 +46,19 @@
let port = args.tcp_port;
device.register_l2cap_channel_server(
args.psm,
- move |_py, l2cap_channel| {
+ move |py, l2cap_channel| {
let channel_info = l2cap_channel
.debug_string()
.unwrap_or_else(|e| format!("failed to get l2cap channel info ({e})"));
println!("{} {channel_info}", "*** L2CAP channel:".cyan());
let host = host.clone();
- // Ensure Python event loop is available to l2cap `disconnect`
- let _ = run_future_with_current_task_locals(proxy_data_between_l2cap_and_tcp(
- l2cap_channel,
- host,
- port,
- ));
+ // Handles setting up a tokio runtime that runs this future to completion while also
+ // containing the necessary context vars.
+ pyo3_asyncio::tokio::future_into_py(
+ py,
+ proxy_data_between_l2cap_and_tcp(l2cap_channel, host, port),
+ )?;
Ok(())
},
args.max_credits,
diff --git a/rust/src/cli/usb/mod.rs b/rust/src/cli/usb/mod.rs
index 7adbd75..5cab29a 100644
--- a/rust/src/cli/usb/mod.rs
+++ b/rust/src/cli/usb/mod.rs
@@ -143,10 +143,7 @@
);
if let Some(s) = serial {
println!("{:26}{}", " Serial:".green(), s);
- device_serials_by_id
- .entry(device_id)
- .or_insert(HashSet::new())
- .insert(s);
+ device_serials_by_id.entry(device_id).or_default().insert(s);
}
if let Some(m) = mfg {
println!("{:26}{}", " Manufacturer:".green(), m);
@@ -314,7 +311,7 @@
self.protocol,
self.protocol_name()
.map(|s| format!(" [{}]", s))
- .unwrap_or_else(String::new)
+ .unwrap_or_default()
)
}
}
diff --git a/rust/src/internal/hci/mod.rs b/rust/src/internal/hci/mod.rs
new file mode 100644
index 0000000..232c49f
--- /dev/null
+++ b/rust/src/internal/hci/mod.rs
@@ -0,0 +1,161 @@
+// Copyright 2023 Google LLC
+//
+// 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.
+
+pub use pdl_runtime::{Error, Packet};
+
+use crate::internal::hci::packets::{Acl, Command, Event, Sco};
+use pdl_derive::pdl;
+
+#[allow(missing_docs, warnings, clippy::all)]
+#[pdl("src/internal/hci/packets.pdl")]
+pub mod packets {}
+#[cfg(test)]
+mod tests;
+
+/// HCI Packet type, prepended to the packet.
+/// Rootcanal's PDL declaration excludes this from ser/deser and instead is implemented in code.
+/// To maintain the ability to easily use future versions of their packet PDL, packet type is
+/// implemented here.
+#[derive(Debug, PartialEq)]
+pub(crate) enum PacketType {
+ Command = 0x01,
+ Acl = 0x02,
+ Sco = 0x03,
+ Event = 0x04,
+}
+
+impl TryFrom<u8> for PacketType {
+ type Error = PacketTypeParseError;
+
+ fn try_from(value: u8) -> Result<Self, Self::Error> {
+ match value {
+ 0x01 => Ok(PacketType::Command),
+ 0x02 => Ok(PacketType::Acl),
+ 0x03 => Ok(PacketType::Sco),
+ 0x04 => Ok(PacketType::Event),
+ _ => Err(PacketTypeParseError::InvalidPacketType { value }),
+ }
+ }
+}
+
+impl From<PacketType> for u8 {
+ fn from(packet_type: PacketType) -> Self {
+ match packet_type {
+ PacketType::Command => 0x01,
+ PacketType::Acl => 0x02,
+ PacketType::Sco => 0x03,
+ PacketType::Event => 0x04,
+ }
+ }
+}
+
+/// Allows for smoother interoperability between a [Packet] and a bytes representation of it that
+/// includes its type as a header
+pub(crate) trait WithPacketType<T: Packet> {
+ /// Converts the [Packet] into bytes, prefixed with its type
+ fn to_vec_with_packet_type(self) -> Vec<u8>;
+
+ /// Parses a [Packet] out of bytes that are prefixed with the packet's type
+ fn parse_with_packet_type(bytes: &[u8]) -> Result<T, PacketTypeParseError>;
+}
+
+/// Errors that may arise when parsing a packet that is prefixed with its type
+#[derive(Debug, PartialEq, thiserror::Error)]
+pub(crate) enum PacketTypeParseError {
+ #[error("The slice being parsed was empty")]
+ EmptySlice,
+ #[error("Packet type ({value:#X}) is invalid")]
+ InvalidPacketType { value: u8 },
+ #[error("Expected packet type: {expected:?}, but got: {actual:?}")]
+ PacketTypeMismatch {
+ expected: PacketType,
+ actual: PacketType,
+ },
+ #[error("Failed to parse packet after header: {error}")]
+ PacketParse { error: Error },
+}
+
+impl From<Error> for PacketTypeParseError {
+ fn from(error: Error) -> Self {
+ Self::PacketParse { error }
+ }
+}
+
+impl WithPacketType<Self> for Command {
+ fn to_vec_with_packet_type(self) -> Vec<u8> {
+ prepend_packet_type(PacketType::Command, self.to_vec())
+ }
+
+ fn parse_with_packet_type(bytes: &[u8]) -> Result<Self, PacketTypeParseError> {
+ parse_with_expected_packet_type(Command::parse, PacketType::Command, bytes)
+ }
+}
+
+impl WithPacketType<Self> for Acl {
+ fn to_vec_with_packet_type(self) -> Vec<u8> {
+ prepend_packet_type(PacketType::Acl, self.to_vec())
+ }
+
+ fn parse_with_packet_type(bytes: &[u8]) -> Result<Self, PacketTypeParseError> {
+ parse_with_expected_packet_type(Acl::parse, PacketType::Acl, bytes)
+ }
+}
+
+impl WithPacketType<Self> for Sco {
+ fn to_vec_with_packet_type(self) -> Vec<u8> {
+ prepend_packet_type(PacketType::Sco, self.to_vec())
+ }
+
+ fn parse_with_packet_type(bytes: &[u8]) -> Result<Self, PacketTypeParseError> {
+ parse_with_expected_packet_type(Sco::parse, PacketType::Sco, bytes)
+ }
+}
+
+impl WithPacketType<Self> for Event {
+ fn to_vec_with_packet_type(self) -> Vec<u8> {
+ prepend_packet_type(PacketType::Event, self.to_vec())
+ }
+
+ fn parse_with_packet_type(bytes: &[u8]) -> Result<Self, PacketTypeParseError> {
+ parse_with_expected_packet_type(Event::parse, PacketType::Event, bytes)
+ }
+}
+
+fn prepend_packet_type(packet_type: PacketType, mut packet_bytes: Vec<u8>) -> Vec<u8> {
+ packet_bytes.insert(0, packet_type.into());
+ packet_bytes
+}
+
+fn parse_with_expected_packet_type<T: Packet, F, E>(
+ parser: F,
+ expected_packet_type: PacketType,
+ bytes: &[u8],
+) -> Result<T, PacketTypeParseError>
+where
+ F: Fn(&[u8]) -> Result<T, E>,
+ PacketTypeParseError: From<E>,
+{
+ let (first_byte, packet_bytes) = bytes
+ .split_first()
+ .ok_or(PacketTypeParseError::EmptySlice)?;
+ let actual_packet_type = PacketType::try_from(*first_byte)?;
+ if actual_packet_type == expected_packet_type {
+ Ok(parser(packet_bytes)?)
+ } else {
+ Err(PacketTypeParseError::PacketTypeMismatch {
+ expected: expected_packet_type,
+ actual: actual_packet_type,
+ })
+ }
+}
diff --git a/rust/src/internal/hci/packets.pdl b/rust/src/internal/hci/packets.pdl
new file mode 100644
index 0000000..694d37c
--- /dev/null
+++ b/rust/src/internal/hci/packets.pdl
@@ -0,0 +1,6253 @@
+// Copyright 2023 Google LLC
+//
+// 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.
+// Copied from:
+// https://github.com/google/rootcanal/blob/2b57c6bb3598babd99b07126e093fb6811637a47/packets/hci_packets.pdl
+little_endian_packets
+
+custom_field Address : 48 "hci/"
+
+enum Enable : 8 {
+ DISABLED = 0x00,
+ ENABLED = 0x01,
+}
+
+// HCI ACL Packets
+
+enum PacketBoundaryFlag : 2 {
+ FIRST_NON_AUTOMATICALLY_FLUSHABLE = 0,
+ CONTINUING_FRAGMENT = 1,
+ FIRST_AUTOMATICALLY_FLUSHABLE = 2,
+}
+
+enum BroadcastFlag : 2 {
+ POINT_TO_POINT = 0,
+ ACTIVE_PERIPHERAL_BROADCAST = 1,
+}
+
+packet Acl {
+ handle : 12,
+ packet_boundary_flag : PacketBoundaryFlag,
+ broadcast_flag : BroadcastFlag,
+ _size_(_payload_) : 16,
+ _payload_,
+}
+
+// HCI SCO Packets
+
+enum PacketStatusFlag : 2 {
+ CORRECTLY_RECEIVED = 0,
+ POSSIBLY_INCOMPLETE = 1,
+ NO_DATA_RECEIVED = 2,
+ PARTIALLY_LOST = 3,
+}
+
+packet Sco {
+ handle : 12,
+ packet_status_flag : PacketStatusFlag,
+ _reserved_ : 2, // BroadcastFlag
+ _size_(data) : 8,
+ data : 8[],
+}
+
+// HCI Command Packets
+
+enum OpCode : 16 {
+ NONE = 0x0000,
+
+ // LINK_CONTROL
+ INQUIRY = 0x0401,
+ INQUIRY_CANCEL = 0x0402,
+ PERIODIC_INQUIRY_MODE = 0x0403,
+ EXIT_PERIODIC_INQUIRY_MODE = 0x0404,
+ CREATE_CONNECTION = 0x0405,
+ DISCONNECT = 0x0406,
+ ADD_SCO_CONNECTION = 0x0407,
+ CREATE_CONNECTION_CANCEL = 0x0408,
+ ACCEPT_CONNECTION_REQUEST = 0x0409,
+ REJECT_CONNECTION_REQUEST = 0x040A,
+ LINK_KEY_REQUEST_REPLY = 0x040B,
+ LINK_KEY_REQUEST_NEGATIVE_REPLY = 0x040C,
+ PIN_CODE_REQUEST_REPLY = 0x040D,
+ PIN_CODE_REQUEST_NEGATIVE_REPLY = 0x040E,
+ CHANGE_CONNECTION_PACKET_TYPE = 0x040F,
+ AUTHENTICATION_REQUESTED = 0x0411,
+ SET_CONNECTION_ENCRYPTION = 0x0413,
+ CHANGE_CONNECTION_LINK_KEY = 0x0415,
+ CENTRAL_LINK_KEY = 0x0417,
+ REMOTE_NAME_REQUEST = 0x0419,
+ REMOTE_NAME_REQUEST_CANCEL = 0x041A,
+ READ_REMOTE_SUPPORTED_FEATURES = 0x041B,
+ READ_REMOTE_EXTENDED_FEATURES = 0x041C,
+ READ_REMOTE_VERSION_INFORMATION = 0x041D,
+ READ_CLOCK_OFFSET = 0x041F,
+ READ_LMP_HANDLE = 0x0420,
+ SETUP_SYNCHRONOUS_CONNECTION = 0x0428,
+ ACCEPT_SYNCHRONOUS_CONNECTION = 0x0429,
+ REJECT_SYNCHRONOUS_CONNECTION = 0x042A,
+ IO_CAPABILITY_REQUEST_REPLY = 0x042B,
+ USER_CONFIRMATION_REQUEST_REPLY = 0x042C,
+ USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 0x042D,
+ USER_PASSKEY_REQUEST_REPLY = 0x042E,
+ USER_PASSKEY_REQUEST_NEGATIVE_REPLY = 0x042F,
+ REMOTE_OOB_DATA_REQUEST_REPLY = 0x0430,
+ REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 0x0433,
+ IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 0x0434,
+ ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 0x043D,
+ ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 0x043E,
+ TRUNCATED_PAGE = 0x043F,
+ TRUNCATED_PAGE_CANCEL = 0x0440,
+ SET_CONNECTIONLESS_PERIPHERAL_BROADCAST = 0x0441,
+ SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE = 0x0442,
+ START_SYNCHRONIZATION_TRAIN = 0x0443,
+ RECEIVE_SYNCHRONIZATION_TRAIN = 0x0444,
+ REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY = 0x0445,
+
+ // LINK_POLICY
+ HOLD_MODE = 0x0801,
+ SNIFF_MODE = 0x0803,
+ EXIT_SNIFF_MODE = 0x0804,
+ QOS_SETUP = 0x0807,
+ ROLE_DISCOVERY = 0x0809,
+ SWITCH_ROLE = 0x080B,
+ READ_LINK_POLICY_SETTINGS = 0x080C,
+ WRITE_LINK_POLICY_SETTINGS = 0x080D,
+ READ_DEFAULT_LINK_POLICY_SETTINGS = 0x080E,
+ WRITE_DEFAULT_LINK_POLICY_SETTINGS = 0x080F,
+ FLOW_SPECIFICATION = 0x0810,
+ SNIFF_SUBRATING = 0x0811,
+
+ // CONTROLLER_AND_BASEBAND
+ SET_EVENT_MASK = 0x0C01,
+ RESET = 0x0C03,
+ SET_EVENT_FILTER = 0x0C05,
+ FLUSH = 0x0C08,
+ READ_PIN_TYPE = 0x0C09,
+ WRITE_PIN_TYPE = 0x0C0A,
+ READ_STORED_LINK_KEY = 0x0C0D,
+ WRITE_STORED_LINK_KEY = 0x0C11,
+ DELETE_STORED_LINK_KEY = 0x0C12,
+ WRITE_LOCAL_NAME = 0x0C13,
+ READ_LOCAL_NAME = 0x0C14,
+ READ_CONNECTION_ACCEPT_TIMEOUT = 0x0C15,
+ WRITE_CONNECTION_ACCEPT_TIMEOUT = 0x0C16,
+ READ_PAGE_TIMEOUT = 0x0C17,
+ WRITE_PAGE_TIMEOUT = 0x0C18,
+ READ_SCAN_ENABLE = 0x0C19,
+ WRITE_SCAN_ENABLE = 0x0C1A,
+ READ_PAGE_SCAN_ACTIVITY = 0x0C1B,
+ WRITE_PAGE_SCAN_ACTIVITY = 0x0C1C,
+ READ_INQUIRY_SCAN_ACTIVITY = 0x0C1D,
+ WRITE_INQUIRY_SCAN_ACTIVITY = 0x0C1E,
+ READ_AUTHENTICATION_ENABLE = 0x0C1F,
+ WRITE_AUTHENTICATION_ENABLE = 0x0C20,
+ READ_CLASS_OF_DEVICE = 0x0C23,
+ WRITE_CLASS_OF_DEVICE = 0x0C24,
+ READ_VOICE_SETTING = 0x0C25,
+ WRITE_VOICE_SETTING = 0x0C26,
+ READ_AUTOMATIC_FLUSH_TIMEOUT = 0x0C27,
+ WRITE_AUTOMATIC_FLUSH_TIMEOUT = 0x0C28,
+ READ_NUM_BROADCAST_RETRANSMITS = 0x0C29,
+ WRITE_NUM_BROADCAST_RETRANSMITS = 0x0C2A,
+ READ_HOLD_MODE_ACTIVITY = 0x0C2B,
+ WRITE_HOLD_MODE_ACTIVITY = 0x0C2C,
+ READ_TRANSMIT_POWER_LEVEL = 0x0C2D,
+ READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2E,
+ WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 0x0C2F,
+ SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 0x0C31,
+ HOST_BUFFER_SIZE = 0x0C33,
+ HOST_NUMBER_OF_COMPLETED_PACKETS = 0x0C35,
+ READ_LINK_SUPERVISION_TIMEOUT = 0x0C36,
+ WRITE_LINK_SUPERVISION_TIMEOUT = 0x0C37,
+ READ_NUMBER_OF_SUPPORTED_IAC = 0x0C38,
+ READ_CURRENT_IAC_LAP = 0x0C39,
+ WRITE_CURRENT_IAC_LAP = 0x0C3A,
+ SET_AFH_HOST_CHANNEL_CLASSIFICATION = 0x0C3F,
+ READ_INQUIRY_SCAN_TYPE = 0x0C42,
+ WRITE_INQUIRY_SCAN_TYPE = 0x0C43,
+ READ_INQUIRY_MODE = 0x0C44,
+ WRITE_INQUIRY_MODE = 0x0C45,
+ READ_PAGE_SCAN_TYPE = 0x0C46,
+ WRITE_PAGE_SCAN_TYPE = 0x0C47,
+ READ_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C48,
+ WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 0x0C49,
+ READ_EXTENDED_INQUIRY_RESPONSE = 0x0C51,
+ WRITE_EXTENDED_INQUIRY_RESPONSE = 0x0C52,
+ REFRESH_ENCRYPTION_KEY = 0x0C53,
+ READ_SIMPLE_PAIRING_MODE = 0x0C55,
+ WRITE_SIMPLE_PAIRING_MODE = 0x0C56,
+ READ_LOCAL_OOB_DATA = 0x0C57,
+ READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 0x0C58,
+ WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 0x0C59,
+ READ_DEFAULT_ERRONEOUS_DATA_REPORTING = 0x0C5A,
+ WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING = 0x0C5B,
+ ENHANCED_FLUSH = 0x0C5F,
+ SEND_KEYPRESS_NOTIFICATION = 0x0C60,
+ SET_EVENT_MASK_PAGE_2 = 0x0C63,
+ READ_FLOW_CONTROL_MODE = 0x0C66,
+ WRITE_FLOW_CONTROL_MODE = 0x0C67,
+ READ_ENHANCED_TRANSMIT_POWER_LEVEL = 0x0C68,
+ READ_LE_HOST_SUPPORT = 0x0C6C,
+ WRITE_LE_HOST_SUPPORT = 0x0C6D,
+ SET_MWS_CHANNEL_PARAMETERS = 0x0C6E,
+ SET_EXTERNAL_FRAME_CONFIGURATION = 0x0C6F,
+ SET_MWS_SIGNALING = 0x0C70,
+ SET_MWS_TRANSPORT_LAYER = 0x0C71,
+ SET_MWS_SCAN_FREQUENCY_TABLE = 0x0C72,
+ SET_MWS_PATTERN_CONFIGURATION = 0x0C73,
+ SET_RESERVED_LT_ADDR = 0x0C74,
+ DELETE_RESERVED_LT_ADDR = 0x0C75,
+ SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA = 0x0C76,
+ READ_SYNCHRONIZATION_TRAIN_PARAMETERS = 0x0C77,
+ WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS = 0x0C78,
+ READ_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C79,
+ WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 0x0C7A,
+ READ_AUTHENTICATED_PAYLOAD_TIMEOUT = 0x0C7B,
+ WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT = 0x0C7C,
+ READ_LOCAL_OOB_EXTENDED_DATA = 0x0C7D,
+ READ_EXTENDED_PAGE_TIMEOUT = 0x0C7E,
+ WRITE_EXTENDED_PAGE_TIMEOUT = 0x0C7F,
+ READ_EXTENDED_INQUIRY_LENGTH = 0x0C80,
+ WRITE_EXTENDED_INQUIRY_LENGTH = 0x0C81,
+ SET_ECOSYSTEM_BASE_INTERVAL = 0x0C82,
+ CONFIGURE_DATA_PATH = 0x0C83,
+ SET_MIN_ENCRYPTION_KEY_SIZE = 0x0C84,
+
+ // INFORMATIONAL_PARAMETERS
+ READ_LOCAL_VERSION_INFORMATION = 0x1001,
+ READ_LOCAL_SUPPORTED_COMMANDS = 0x1002,
+ READ_LOCAL_SUPPORTED_FEATURES = 0x1003,
+ READ_LOCAL_EXTENDED_FEATURES = 0x1004,
+ READ_BUFFER_SIZE = 0x1005,
+ READ_BD_ADDR = 0x1009,
+ READ_DATA_BLOCK_SIZE = 0x100A,
+ READ_LOCAL_SUPPORTED_CODECS_V1 = 0x100B,
+ READ_LOCAL_SIMPLE_PAIRING_OPTIONS = 0x100C,
+ READ_LOCAL_SUPPORTED_CODECS_V2 = 0x100D,
+ READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES = 0x100E,
+ READ_LOCAL_SUPPORTED_CONTROLLER_DELAY = 0x100F,
+
+ // STATUS_PARAMETERS
+ READ_FAILED_CONTACT_COUNTER = 0x1401,
+ RESET_FAILED_CONTACT_COUNTER = 0x1402,
+ READ_LINK_QUALITY = 0x1403,
+ READ_RSSI = 0x1405,
+ READ_AFH_CHANNEL_MAP = 0x1406,
+ READ_CLOCK = 0x1407,
+ READ_ENCRYPTION_KEY_SIZE = 0x1408,
+ GET_MWS_TRANSPORT_LAYER_CONFIGURATION = 0x140C,
+ SET_TRIGGERED_CLOCK_CAPTURE = 0x140D,
+
+ // TESTING
+ READ_LOOPBACK_MODE = 0x1801,
+ WRITE_LOOPBACK_MODE = 0x1802,
+ ENABLE_DEVICE_UNDER_TEST_MODE = 0x1803,
+ WRITE_SIMPLE_PAIRING_DEBUG_MODE = 0x1804,
+ WRITE_SECURE_CONNECTIONS_TEST_MODE = 0x180A,
+
+ // LE_CONTROLLER
+ LE_SET_EVENT_MASK = 0x2001,
+ LE_READ_BUFFER_SIZE_V1 = 0x2002,
+ LE_READ_LOCAL_SUPPORTED_FEATURES = 0x2003,
+ LE_SET_RANDOM_ADDRESS = 0x2005,
+ LE_SET_ADVERTISING_PARAMETERS = 0x2006,
+ LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER = 0x2007,
+ LE_SET_ADVERTISING_DATA = 0x2008,
+ LE_SET_SCAN_RESPONSE_DATA = 0x2009,
+ LE_SET_ADVERTISING_ENABLE = 0x200A,
+ LE_SET_SCAN_PARAMETERS = 0x200B,
+ LE_SET_SCAN_ENABLE = 0x200C,
+ LE_CREATE_CONNECTION = 0x200D,
+ LE_CREATE_CONNECTION_CANCEL = 0x200E,
+ LE_READ_FILTER_ACCEPT_LIST_SIZE = 0x200F,
+ LE_CLEAR_FILTER_ACCEPT_LIST = 0x2010,
+ LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST = 0x2011,
+ LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST = 0x2012,
+ LE_CONNECTION_UPDATE = 0x2013,
+ LE_SET_HOST_CHANNEL_CLASSIFICATION = 0x2014,
+ LE_READ_CHANNEL_MAP = 0x2015,
+ LE_READ_REMOTE_FEATURES = 0x2016,
+ LE_ENCRYPT = 0x2017,
+ LE_RAND = 0x2018,
+ LE_START_ENCRYPTION = 0x2019,
+ LE_LONG_TERM_KEY_REQUEST_REPLY = 0x201A,
+ LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = 0x201B,
+ LE_READ_SUPPORTED_STATES = 0x201C,
+ LE_RECEIVER_TEST_V1 = 0x201D,
+ LE_TRANSMITTER_TEST_V1 = 0x201E,
+ LE_TEST_END = 0x201F,
+ LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = 0x2020,
+ LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = 0x2021,
+ LE_SET_DATA_LENGTH = 0x2022,
+ LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2023,
+ LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = 0x2024,
+ LE_READ_LOCAL_P_256_PUBLIC_KEY = 0x2025,
+ LE_GENERATE_DHKEY_V1 = 0x2026,
+ LE_ADD_DEVICE_TO_RESOLVING_LIST = 0x2027,
+ LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = 0x2028,
+ LE_CLEAR_RESOLVING_LIST = 0x2029,
+ LE_READ_RESOLVING_LIST_SIZE = 0x202A,
+ LE_READ_PEER_RESOLVABLE_ADDRESS = 0x202B,
+ LE_READ_LOCAL_RESOLVABLE_ADDRESS = 0x202C,
+ LE_SET_ADDRESS_RESOLUTION_ENABLE = 0x202D,
+ LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = 0x202E,
+ LE_READ_MAXIMUM_DATA_LENGTH = 0x202F,
+ LE_READ_PHY = 0x2030,
+ LE_SET_DEFAULT_PHY = 0x2031,
+ LE_SET_PHY = 0x2032,
+ LE_RECEIVER_TEST_V2 = 0x2033,
+ LE_TRANSMITTER_TEST_V2 = 0x2034,
+ LE_SET_ADVERTISING_SET_RANDOM_ADDRESS = 0x2035,
+ LE_SET_EXTENDED_ADVERTISING_PARAMETERS = 0x2036,
+ LE_SET_EXTENDED_ADVERTISING_DATA = 0x2037,
+ LE_SET_EXTENDED_SCAN_RESPONSE_DATA = 0x2038,
+ LE_SET_EXTENDED_ADVERTISING_ENABLE = 0x2039,
+ LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 0x203A,
+ LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 0x203B,
+ LE_REMOVE_ADVERTISING_SET = 0x203C,
+ LE_CLEAR_ADVERTISING_SETS = 0x203D,
+ LE_SET_PERIODIC_ADVERTISING_PARAMETERS = 0x203E,
+ LE_SET_PERIODIC_ADVERTISING_DATA = 0x203F,
+ LE_SET_PERIODIC_ADVERTISING_ENABLE = 0x2040,
+ LE_SET_EXTENDED_SCAN_PARAMETERS = 0x2041,
+ LE_SET_EXTENDED_SCAN_ENABLE = 0x2042,
+ LE_EXTENDED_CREATE_CONNECTION = 0x2043,
+ LE_PERIODIC_ADVERTISING_CREATE_SYNC = 0x2044,
+ LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 0x2045,
+ LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 0x2046,
+ LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST = 0x2047,
+ LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST = 0x2048,
+ LE_CLEAR_PERIODIC_ADVERTISER_LIST = 0x2049,
+ LE_READ_PERIODIC_ADVERTISER_LIST_SIZE = 0x204A,
+ LE_READ_TRANSMIT_POWER = 0x204B,
+ LE_READ_RF_PATH_COMPENSATION_POWER = 0x204C,
+ LE_WRITE_RF_PATH_COMPENSATION_POWER = 0x204D,
+ LE_SET_PRIVACY_MODE = 0x204E,
+ LE_RECEIVER_TEST_V3 = 0x204F,
+ LE_TRANSMITTER_TEST_V3 = 0x2050,
+ LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS = 0x2051,
+ LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE = 0x2052,
+ LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE = 0x2053,
+ LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS = 0x2054,
+ LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS = 0x2055,
+ LE_CONNECTION_CTE_REQUEST_ENABLE = 0x2056,
+ LE_CONNECTION_CTE_RESPONSE_ENABLE = 0x2057,
+ LE_READ_ANTENNA_INFORMATION = 0x2058,
+ LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE = 0x2059,
+ LE_PERIODIC_ADVERTISING_SYNC_TRANSFER = 0x205A,
+ LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER = 0x205B,
+ LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 0x205C,
+ LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 0x205D,
+ LE_GENERATE_DHKEY_V2 = 0x205E,
+ LE_MODIFY_SLEEP_CLOCK_ACCURACY = 0x205F,
+ LE_READ_BUFFER_SIZE_V2 = 0x2060,
+ LE_READ_ISO_TX_SYNC = 0x2061,
+ LE_SET_CIG_PARAMETERS = 0x2062,
+ LE_SET_CIG_PARAMETERS_TEST = 0x2063,
+ LE_CREATE_CIS = 0x2064,
+ LE_REMOVE_CIG = 0x2065,
+ LE_ACCEPT_CIS_REQUEST = 0x2066,
+ LE_REJECT_CIS_REQUEST = 0x2067,
+ LE_CREATE_BIG = 0x2068,
+ LE_CREATE_BIG_TEST = 0x2069,
+ LE_TERMINATE_BIG = 0x206A,
+ LE_BIG_CREATE_SYNC = 0x206B,
+ LE_BIG_TERMINATE_SYNC = 0x206C,
+ LE_REQUEST_PEER_SCA = 0x206D,
+ LE_SETUP_ISO_DATA_PATH = 0x206E,
+ LE_REMOVE_ISO_DATA_PATH = 0x206F,
+ LE_ISO_TRANSMIT_TEST = 0x2070,
+ LE_ISO_RECEIVE_TEST = 0x2071,
+ LE_ISO_READ_TEST_COUNTERS = 0x2072,
+ LE_ISO_TEST_END = 0x2073,
+ LE_SET_HOST_FEATURE = 0x2074,
+ LE_READ_ISO_LINK_QUALITY = 0x2075,
+ LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL = 0x2076,
+ LE_READ_REMOTE_TRANSMIT_POWER_LEVEL = 0x2077,
+ LE_SET_PATH_LOSS_REPORTING_PARAMETERS = 0x2078,
+ LE_SET_PATH_LOSS_REPORTING_ENABLE = 0x2079,
+ LE_SET_TRANSMIT_POWER_REPORTING_ENABLE = 0x207A,
+ LE_TRANSMITTER_TEST_V4 = 0x207B,
+ LE_SET_DATA_RELATED_ADDRESS_CHANGES = 0x207C,
+ LE_SET_DEFAULT_SUBRATE = 0x207D,
+ LE_SUBRATE_REQUEST = 0x207E,
+
+ // VENDOR_SPECIFIC
+ // MSFT_OPCODE_xxxx below is needed for the tests.
+ MSFT_OPCODE_INTEL = 0xFC1E,
+ LE_GET_VENDOR_CAPABILITIES = 0xFD53,
+ LE_BATCH_SCAN = 0xFD56,
+ LE_APCF = 0xFD57,
+ LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO = 0xFD59,
+ LE_EX_SET_SCAN_PARAMETERS = 0xFD5A,
+ GET_CONTROLLER_DEBUG_INFO = 0xFD5B,
+ // MSFT_OPCODE_xxxx below are needed for the tests.
+ MSFT_OPCODE_MEDIATEK = 0xFD30,
+ MSFT_OPCODE_QUALCOMM = 0xFD70,
+}
+
+// For mapping Local Supported Commands command
+// Value = Octet * 10 + bit
+enum OpCodeIndex : 16 {
+ INQUIRY = 0,
+ INQUIRY_CANCEL = 1,
+ PERIODIC_INQUIRY_MODE = 2,
+ EXIT_PERIODIC_INQUIRY_MODE = 3,
+ CREATE_CONNECTION = 4,
+ DISCONNECT = 5,
+ ADD_SCO_CONNECTION = 6,
+ CREATE_CONNECTION_CANCEL = 7,
+ ACCEPT_CONNECTION_REQUEST = 10,
+ REJECT_CONNECTION_REQUEST = 11,
+ LINK_KEY_REQUEST_REPLY = 12,
+ LINK_KEY_REQUEST_NEGATIVE_REPLY = 13,
+ PIN_CODE_REQUEST_REPLY = 14,
+ PIN_CODE_REQUEST_NEGATIVE_REPLY = 15,
+ CHANGE_CONNECTION_PACKET_TYPE = 16,
+ AUTHENTICATION_REQUESTED = 17,
+ SET_CONNECTION_ENCRYPTION = 20,
+ CHANGE_CONNECTION_LINK_KEY = 21,
+ CENTRAL_LINK_KEY = 22,
+ REMOTE_NAME_REQUEST = 23,
+ REMOTE_NAME_REQUEST_CANCEL = 24,
+ READ_REMOTE_SUPPORTED_FEATURES = 25,
+ READ_REMOTE_EXTENDED_FEATURES = 26,
+ READ_REMOTE_VERSION_INFORMATION = 27,
+ READ_CLOCK_OFFSET = 30,
+ READ_LMP_HANDLE = 31,
+ HOLD_MODE = 41,
+ SNIFF_MODE = 42,
+ EXIT_SNIFF_MODE = 43,
+ QOS_SETUP = 46,
+ ROLE_DISCOVERY = 47,
+ SWITCH_ROLE = 50,
+ READ_LINK_POLICY_SETTINGS = 51,
+ WRITE_LINK_POLICY_SETTINGS = 52,
+ READ_DEFAULT_LINK_POLICY_SETTINGS = 53,
+ WRITE_DEFAULT_LINK_POLICY_SETTINGS = 54,
+ FLOW_SPECIFICATION = 55,
+ SET_EVENT_MASK = 56,
+ RESET = 57,
+ SET_EVENT_FILTER = 60,
+ FLUSH = 61,
+ READ_PIN_TYPE = 62,
+ WRITE_PIN_TYPE = 63,
+ READ_STORED_LINK_KEY = 65,
+ WRITE_STORED_LINK_KEY = 66,
+ DELETE_STORED_LINK_KEY = 67,
+ WRITE_LOCAL_NAME = 70,
+ READ_LOCAL_NAME = 71,
+ READ_CONNECTION_ACCEPT_TIMEOUT = 72,
+ WRITE_CONNECTION_ACCEPT_TIMEOUT = 73,
+ READ_PAGE_TIMEOUT = 74,
+ WRITE_PAGE_TIMEOUT = 75,
+ READ_SCAN_ENABLE = 76,
+ WRITE_SCAN_ENABLE = 77,
+ READ_PAGE_SCAN_ACTIVITY = 80,
+ WRITE_PAGE_SCAN_ACTIVITY = 81,
+ READ_INQUIRY_SCAN_ACTIVITY = 82,
+ WRITE_INQUIRY_SCAN_ACTIVITY = 83,
+ READ_AUTHENTICATION_ENABLE = 84,
+ WRITE_AUTHENTICATION_ENABLE = 85,
+ READ_CLASS_OF_DEVICE = 90,
+ WRITE_CLASS_OF_DEVICE = 91,
+ READ_VOICE_SETTING = 92,
+ WRITE_VOICE_SETTING = 93,
+ READ_AUTOMATIC_FLUSH_TIMEOUT = 94,
+ WRITE_AUTOMATIC_FLUSH_TIMEOUT = 95,
+ READ_NUM_BROADCAST_RETRANSMITS = 96,
+ WRITE_NUM_BROADCAST_RETRANSMITS = 97,
+ READ_HOLD_MODE_ACTIVITY = 100,
+ WRITE_HOLD_MODE_ACTIVITY = 101,
+ READ_TRANSMIT_POWER_LEVEL = 102,
+ READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 103,
+ WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE = 104,
+ SET_CONTROLLER_TO_HOST_FLOW_CONTROL = 105,
+ HOST_BUFFER_SIZE = 106,
+ HOST_NUMBER_OF_COMPLETED_PACKETS = 107,
+ READ_LINK_SUPERVISION_TIMEOUT = 110,
+ WRITE_LINK_SUPERVISION_TIMEOUT = 111,
+ READ_NUMBER_OF_SUPPORTED_IAC = 112,
+ READ_CURRENT_IAC_LAP = 113,
+ WRITE_CURRENT_IAC_LAP = 114,
+ SET_AFH_HOST_CHANNEL_CLASSIFICATION = 121,
+ READ_INQUIRY_SCAN_TYPE = 124,
+ WRITE_INQUIRY_SCAN_TYPE = 125,
+ READ_INQUIRY_MODE = 126,
+ WRITE_INQUIRY_MODE = 127,
+ READ_PAGE_SCAN_TYPE = 130,
+ WRITE_PAGE_SCAN_TYPE = 131,
+ READ_AFH_CHANNEL_ASSESSMENT_MODE = 132,
+ WRITE_AFH_CHANNEL_ASSESSMENT_MODE = 133,
+ READ_LOCAL_VERSION_INFORMATION = 143,
+ READ_LOCAL_SUPPORTED_FEATURES = 145,
+ READ_LOCAL_EXTENDED_FEATURES = 146,
+ READ_BUFFER_SIZE = 147,
+ READ_BD_ADDR = 151,
+ READ_FAILED_CONTACT_COUNTER = 152,
+ RESET_FAILED_CONTACT_COUNTER = 153,
+ READ_LINK_QUALITY = 154,
+ READ_RSSI = 155,
+ READ_AFH_CHANNEL_MAP = 156,
+ READ_CLOCK = 157,
+ READ_LOOPBACK_MODE = 160,
+ WRITE_LOOPBACK_MODE = 161,
+ ENABLE_DEVICE_UNDER_TEST_MODE = 162,
+ SETUP_SYNCHRONOUS_CONNECTION = 163,
+ ACCEPT_SYNCHRONOUS_CONNECTION = 164,
+ REJECT_SYNCHRONOUS_CONNECTION = 165,
+ READ_EXTENDED_INQUIRY_RESPONSE = 170,
+ WRITE_EXTENDED_INQUIRY_RESPONSE = 171,
+ REFRESH_ENCRYPTION_KEY = 172,
+ SNIFF_SUBRATING = 174,
+ READ_SIMPLE_PAIRING_MODE = 175,
+ WRITE_SIMPLE_PAIRING_MODE = 176,
+ READ_LOCAL_OOB_DATA = 177,
+ READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL = 180,
+ WRITE_INQUIRY_TRANSMIT_POWER_LEVEL = 181,
+ READ_DEFAULT_ERRONEOUS_DATA_REPORTING = 182,
+ WRITE_DEFAULT_ERRONEOUS_DATA_REPORTING = 183,
+ IO_CAPABILITY_REQUEST_REPLY = 187,
+ USER_CONFIRMATION_REQUEST_REPLY = 190,
+ USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY = 191,
+ USER_PASSKEY_REQUEST_REPLY = 192,
+ USER_PASSKEY_REQUEST_NEGATIVE_REPLY = 193,
+ REMOTE_OOB_DATA_REQUEST_REPLY = 194,
+ WRITE_SIMPLE_PAIRING_DEBUG_MODE = 195,
+ ENHANCED_FLUSH = 196,
+ REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY = 197,
+ SEND_KEYPRESS_NOTIFICATION = 202,
+ IO_CAPABILITY_REQUEST_NEGATIVE_REPLY = 203,
+ READ_ENCRYPTION_KEY_SIZE = 204,
+ SET_EVENT_MASK_PAGE_2 = 222,
+ READ_FLOW_CONTROL_MODE = 230,
+ WRITE_FLOW_CONTROL_MODE = 231,
+ READ_DATA_BLOCK_SIZE = 232,
+ READ_ENHANCED_TRANSMIT_POWER_LEVEL = 240,
+ READ_LE_HOST_SUPPORT = 245,
+ WRITE_LE_HOST_SUPPORT = 246,
+ LE_SET_EVENT_MASK = 250,
+ LE_READ_BUFFER_SIZE_V1 = 251,
+ LE_READ_LOCAL_SUPPORTED_FEATURES = 252,
+ LE_SET_RANDOM_ADDRESS = 254,
+ LE_SET_ADVERTISING_PARAMETERS = 255,
+ LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER = 256,
+ LE_SET_ADVERTISING_DATA = 257,
+ LE_SET_SCAN_RESPONSE_DATA = 260,
+ LE_SET_ADVERTISING_ENABLE = 261,
+ LE_SET_SCAN_PARAMETERS = 262,
+ LE_SET_SCAN_ENABLE = 263,
+ LE_CREATE_CONNECTION = 264,
+ LE_CREATE_CONNECTION_CANCEL = 265,
+ LE_READ_FILTER_ACCEPT_LIST_SIZE = 266,
+ LE_CLEAR_FILTER_ACCEPT_LIST = 267,
+ LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST = 270,
+ LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST = 271,
+ LE_CONNECTION_UPDATE = 272,
+ LE_SET_HOST_CHANNEL_CLASSIFICATION = 273,
+ LE_READ_CHANNEL_MAP = 274,
+ LE_READ_REMOTE_FEATURES = 275,
+ LE_ENCRYPT = 276,
+ LE_RAND = 277,
+ LE_START_ENCRYPTION = 280,
+ LE_LONG_TERM_KEY_REQUEST_REPLY = 281,
+ LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY = 282,
+ LE_READ_SUPPORTED_STATES = 283,
+ LE_RECEIVER_TEST_V1 = 284,
+ LE_TRANSMITTER_TEST_V1 = 285,
+ LE_TEST_END = 286,
+ ENHANCED_SETUP_SYNCHRONOUS_CONNECTION = 293,
+ ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION = 294,
+ READ_LOCAL_SUPPORTED_CODECS_V1 = 295,
+ SET_MWS_CHANNEL_PARAMETERS = 296,
+ SET_EXTERNAL_FRAME_CONFIGURATION = 297,
+ SET_MWS_SIGNALING = 300,
+ SET_MWS_TRANSPORT_LAYER = 301,
+ SET_MWS_SCAN_FREQUENCY_TABLE = 302,
+ GET_MWS_TRANSPORT_LAYER_CONFIGURATION = 303,
+ SET_MWS_PATTERN_CONFIGURATION = 304,
+ SET_TRIGGERED_CLOCK_CAPTURE = 305,
+ TRUNCATED_PAGE = 306,
+ TRUNCATED_PAGE_CANCEL = 307,
+ SET_CONNECTIONLESS_PERIPHERAL_BROADCAST = 310,
+ SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVE = 311,
+ START_SYNCHRONIZATION_TRAIN = 312,
+ RECEIVE_SYNCHRONIZATION_TRAIN = 313,
+ SET_RESERVED_LT_ADDR = 314,
+ DELETE_RESERVED_LT_ADDR = 315,
+ SET_CONNECTIONLESS_PERIPHERAL_BROADCAST_DATA = 316,
+ READ_SYNCHRONIZATION_TRAIN_PARAMETERS = 317,
+ WRITE_SYNCHRONIZATION_TRAIN_PARAMETERS = 320,
+ REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY = 321,
+ READ_SECURE_CONNECTIONS_HOST_SUPPORT = 322,
+ WRITE_SECURE_CONNECTIONS_HOST_SUPPORT = 323,
+ READ_AUTHENTICATED_PAYLOAD_TIMEOUT = 324,
+ WRITE_AUTHENTICATED_PAYLOAD_TIMEOUT = 325,
+ READ_LOCAL_OOB_EXTENDED_DATA = 326,
+ WRITE_SECURE_CONNECTIONS_TEST_MODE = 327,
+ READ_EXTENDED_PAGE_TIMEOUT = 330,
+ WRITE_EXTENDED_PAGE_TIMEOUT = 331,
+ READ_EXTENDED_INQUIRY_LENGTH = 332,
+ WRITE_EXTENDED_INQUIRY_LENGTH = 333,
+ LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY = 334,
+ LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY = 335,
+ LE_SET_DATA_LENGTH = 336,
+ LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH = 337,
+ LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH = 340,
+ LE_READ_LOCAL_P_256_PUBLIC_KEY = 341,
+ LE_GENERATE_DHKEY_V1 = 342,
+ LE_ADD_DEVICE_TO_RESOLVING_LIST = 343,
+ LE_REMOVE_DEVICE_FROM_RESOLVING_LIST = 344,
+ LE_CLEAR_RESOLVING_LIST = 345,
+ LE_READ_RESOLVING_LIST_SIZE = 346,
+ LE_READ_PEER_RESOLVABLE_ADDRESS = 347,
+ LE_READ_LOCAL_RESOLVABLE_ADDRESS = 350,
+ LE_SET_ADDRESS_RESOLUTION_ENABLE = 351,
+ LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT = 352,
+ LE_READ_MAXIMUM_DATA_LENGTH = 353,
+ LE_READ_PHY = 354,
+ LE_SET_DEFAULT_PHY = 355,
+ LE_SET_PHY = 356,
+ LE_RECEIVER_TEST_V2 = 357,
+ LE_TRANSMITTER_TEST_V2 = 360,
+ LE_SET_ADVERTISING_SET_RANDOM_ADDRESS = 361,
+ LE_SET_EXTENDED_ADVERTISING_PARAMETERS = 362,
+ LE_SET_EXTENDED_ADVERTISING_DATA = 363,
+ LE_SET_EXTENDED_SCAN_RESPONSE_DATA = 364,
+ LE_SET_EXTENDED_ADVERTISING_ENABLE = 365,
+ LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH = 366,
+ LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS = 367,
+ LE_REMOVE_ADVERTISING_SET = 370,
+ LE_CLEAR_ADVERTISING_SETS = 371,
+ LE_SET_PERIODIC_ADVERTISING_PARAMETERS = 372,
+ LE_SET_PERIODIC_ADVERTISING_DATA = 373,
+ LE_SET_PERIODIC_ADVERTISING_ENABLE = 374,
+ LE_SET_EXTENDED_SCAN_PARAMETERS = 375,
+ LE_SET_EXTENDED_SCAN_ENABLE = 376,
+ LE_EXTENDED_CREATE_CONNECTION = 377,
+ LE_PERIODIC_ADVERTISING_CREATE_SYNC = 380,
+ LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL = 381,
+ LE_PERIODIC_ADVERTISING_TERMINATE_SYNC = 382,
+ LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST = 383,
+ LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST = 384,
+ LE_CLEAR_PERIODIC_ADVERTISER_LIST = 385,
+ LE_READ_PERIODIC_ADVERTISER_LIST_SIZE = 386,
+ LE_READ_TRANSMIT_POWER = 387,
+ LE_READ_RF_PATH_COMPENSATION_POWER = 390,
+ LE_WRITE_RF_PATH_COMPENSATION_POWER = 391,
+ LE_SET_PRIVACY_MODE = 392,
+ LE_RECEIVER_TEST_V3 = 393,
+ LE_TRANSMITTER_TEST_V3 = 394,
+ LE_SET_CONNECTIONLESS_CTE_TRANSMIT_PARAMETERS = 395,
+ LE_SET_CONNECTIONLESS_CTE_TRANSMIT_ENABLE = 396,
+ LE_SET_CONNECTIONLESS_IQ_SAMPLING_ENABLE = 397,
+ LE_SET_CONNECTION_CTE_RECEIVE_PARAMETERS = 400,
+ LE_SET_CONNECTION_CTE_TRANSMIT_PARAMETERS = 401,
+ LE_CONNECTION_CTE_REQUEST_ENABLE = 402,
+ LE_CONNECTION_CTE_RESPONSE_ENABLE = 403,
+ LE_READ_ANTENNA_INFORMATION = 404,
+ LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE = 405,
+ LE_PERIODIC_ADVERTISING_SYNC_TRANSFER = 406,
+ LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER = 407,
+ LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 410,
+ LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS = 411,
+ LE_GENERATE_DHKEY_V2 = 412,
+ READ_LOCAL_SIMPLE_PAIRING_OPTIONS = 413,
+ LE_MODIFY_SLEEP_CLOCK_ACCURACY = 414,
+ LE_READ_BUFFER_SIZE_V2 = 415,
+ LE_READ_ISO_TX_SYNC = 416,
+ LE_SET_CIG_PARAMETERS = 417,
+ LE_SET_CIG_PARAMETERS_TEST = 420,
+ LE_CREATE_CIS = 421,
+ LE_REMOVE_CIG = 422,
+ LE_ACCEPT_CIS_REQUEST = 423,
+ LE_REJECT_CIS_REQUEST = 424,
+ LE_CREATE_BIG = 425,
+ LE_CREATE_BIG_TEST = 426,
+ LE_TERMINATE_BIG = 427,
+ LE_BIG_CREATE_SYNC = 430,
+ LE_BIG_TERMINATE_SYNC = 431,
+ LE_REQUEST_PEER_SCA = 432,
+ LE_SETUP_ISO_DATA_PATH = 433,
+ LE_REMOVE_ISO_DATA_PATH = 434,
+ LE_ISO_TRANSMIT_TEST = 435,
+ LE_ISO_RECEIVE_TEST = 436,
+ LE_ISO_READ_TEST_COUNTERS = 437,
+ LE_ISO_TEST_END = 440,
+ LE_SET_HOST_FEATURE = 441,
+ LE_READ_ISO_LINK_QUALITY = 442,
+ LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL = 443,
+ LE_READ_REMOTE_TRANSMIT_POWER_LEVEL = 444,
+ LE_SET_PATH_LOSS_REPORTING_PARAMETERS = 445,
+ LE_SET_PATH_LOSS_REPORTING_ENABLE = 446,
+ LE_SET_TRANSMIT_POWER_REPORTING_ENABLE = 447,
+ LE_TRANSMITTER_TEST_V4 = 450,
+ SET_ECOSYSTEM_BASE_INTERVAL = 451,
+ READ_LOCAL_SUPPORTED_CODECS_V2 = 452,
+ READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES = 453,
+ READ_LOCAL_SUPPORTED_CONTROLLER_DELAY = 454,
+ CONFIGURE_DATA_PATH = 455,
+ LE_SET_DATA_RELATED_ADDRESS_CHANGES = 456,
+ SET_MIN_ENCRYPTION_KEY_SIZE = 457,
+ LE_SET_DEFAULT_SUBRATE = 460,
+ LE_SUBRATE_REQUEST = 461,
+}
+
+packet Command {
+ op_code : OpCode,
+ _size_(_payload_) : 8,
+ _payload_,
+}
+
+// HCI Event Packets
+
+enum EventCode : 8 {
+ INQUIRY_COMPLETE = 0x01,
+ INQUIRY_RESULT = 0x02,
+ CONNECTION_COMPLETE = 0x03,
+ CONNECTION_REQUEST = 0x04,
+ DISCONNECTION_COMPLETE = 0x05,
+ AUTHENTICATION_COMPLETE = 0x06,
+ REMOTE_NAME_REQUEST_COMPLETE = 0x07,
+ ENCRYPTION_CHANGE = 0x08,
+ CHANGE_CONNECTION_LINK_KEY_COMPLETE = 0x09,
+ CENTRAL_LINK_KEY_COMPLETE = 0x0A,
+ READ_REMOTE_SUPPORTED_FEATURES_COMPLETE = 0x0B,
+ READ_REMOTE_VERSION_INFORMATION_COMPLETE = 0x0C,
+ QOS_SETUP_COMPLETE = 0x0D,
+ COMMAND_COMPLETE = 0x0E,
+ COMMAND_STATUS = 0x0F,
+ HARDWARE_ERROR = 0x10,
+ FLUSH_OCCURRED = 0x11,
+ ROLE_CHANGE = 0x12,
+ NUMBER_OF_COMPLETED_PACKETS = 0x13,
+ MODE_CHANGE = 0x14,
+ RETURN_LINK_KEYS = 0x15,
+ PIN_CODE_REQUEST = 0x16,
+ LINK_KEY_REQUEST = 0x17,
+ LINK_KEY_NOTIFICATION = 0x18,
+ LOOPBACK_COMMAND = 0x19,
+ DATA_BUFFER_OVERFLOW = 0x1A,
+ MAX_SLOTS_CHANGE = 0x1B,
+ READ_CLOCK_OFFSET_COMPLETE = 0x1C,
+ CONNECTION_PACKET_TYPE_CHANGED = 0x1D,
+ QOS_VIOLATION = 0x1E,
+ PAGE_SCAN_REPETITION_MODE_CHANGE = 0x20,
+ FLOW_SPECIFICATION_COMPLETE = 0x21,
+ INQUIRY_RESULT_WITH_RSSI = 0x22,
+ READ_REMOTE_EXTENDED_FEATURES_COMPLETE = 0x23,
+ SYNCHRONOUS_CONNECTION_COMPLETE = 0x2C,
+ SYNCHRONOUS_CONNECTION_CHANGED = 0x2D,
+ SNIFF_SUBRATING = 0x2E,
+ EXTENDED_INQUIRY_RESULT = 0x2F,
+ ENCRYPTION_KEY_REFRESH_COMPLETE = 0x30,
+ IO_CAPABILITY_REQUEST = 0x31,
+ IO_CAPABILITY_RESPONSE = 0x32,
+ USER_CONFIRMATION_REQUEST = 0x33,
+ USER_PASSKEY_REQUEST = 0x34,
+ REMOTE_OOB_DATA_REQUEST = 0x35,
+ SIMPLE_PAIRING_COMPLETE = 0x36,
+ LINK_SUPERVISION_TIMEOUT_CHANGED = 0x38,
+ ENHANCED_FLUSH_COMPLETE = 0x39,
+ USER_PASSKEY_NOTIFICATION = 0x3B,
+ KEYPRESS_NOTIFICATION = 0x3C,
+ REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION = 0x3D,
+ LE_META_EVENT = 0x3e,
+ NUMBER_OF_COMPLETED_DATA_BLOCKS = 0x48,
+ VENDOR_SPECIFIC = 0xFF,
+}
+
+// LE events
+enum SubeventCode : 8 {
+ CONNECTION_COMPLETE = 0x01,
+ ADVERTISING_REPORT = 0x02,
+ CONNECTION_UPDATE_COMPLETE = 0x03,
+ READ_REMOTE_FEATURES_COMPLETE = 0x04,
+ LONG_TERM_KEY_REQUEST = 0x05,
+ REMOTE_CONNECTION_PARAMETER_REQUEST = 0x06,
+ DATA_LENGTH_CHANGE = 0x07,
+ READ_LOCAL_P256_PUBLIC_KEY_COMPLETE = 0x08,
+ GENERATE_DHKEY_COMPLETE = 0x09,
+ ENHANCED_CONNECTION_COMPLETE = 0x0a,
+ DIRECTED_ADVERTISING_REPORT = 0x0b,
+ PHY_UPDATE_COMPLETE = 0x0c,
+ EXTENDED_ADVERTISING_REPORT = 0x0D,
+ PERIODIC_ADVERTISING_SYNC_ESTABLISHED = 0x0E,
+ PERIODIC_ADVERTISING_REPORT = 0x0F,
+ PERIODIC_ADVERTISING_SYNC_LOST = 0x10,
+ SCAN_TIMEOUT = 0x11,
+ ADVERTISING_SET_TERMINATED = 0x12,
+ SCAN_REQUEST_RECEIVED = 0x13,
+ CHANNEL_SELECTION_ALGORITHM = 0x14,
+ CONNECTIONLESS_IQ_REPORT = 0x15,
+ CONNECTION_IQ_REPORT = 0x16,
+ CTE_REQUEST_FAILED = 0x17,
+ PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED = 0x18,
+ CIS_ESTABLISHED = 0x19,
+ CIS_REQUEST = 0x1A,
+ CREATE_BIG_COMPLETE = 0x1B,
+ TERMINATE_BIG_COMPLETE = 0x1C,
+ BIG_SYNC_ESTABLISHED = 0x1D,
+ BIG_SYNC_LOST = 0x1E,
+ REQUEST_PEER_SCA_COMPLETE = 0x1F,
+ PATH_LOSS_THRESHOLD = 0x20,
+ TRANSMIT_POWER_REPORTING = 0x21,
+ BIG_INFO_ADVERTISING_REPORT = 0x22,
+ LE_SUBRATE_CHANGE = 0x23,
+}
+
+// Vendor specific events
+enum VseSubeventCode : 8 {
+ STORAGE_THRESHOLD_BREACH = 0x54,
+ LE_MULTI_ADVERTISING_STATE_CHANGE = 0x55,
+ LE_ADVERTISEMENT_TRACKING = 0x56,
+ CONTROLLER_DEBUG_INFO = 0x57,
+ BLUETOOTH_QUALITY_REPORT = 0x58,
+}
+
+packet Event {
+ event_code : EventCode,
+ _size_(_payload_) : 8,
+ _payload_,
+}
+
+// Common definitions for commands and events
+
+enum FeatureFlag : 1 {
+ UNSUPPORTED = 0,
+ SUPPORTED = 1,
+}
+
+enum ErrorCode: 8 {
+ STATUS_UNKNOWN = 0xFF,
+ SUCCESS = 0x00,
+ UNKNOWN_HCI_COMMAND = 0x01,
+ UNKNOWN_CONNECTION = 0x02,
+ HARDWARE_FAILURE = 0x03,
+ PAGE_TIMEOUT = 0x04,
+ AUTHENTICATION_FAILURE = 0x05,
+ PIN_OR_KEY_MISSING = 0x06,
+ MEMORY_CAPACITY_EXCEEDED = 0x07,
+ CONNECTION_TIMEOUT = 0x08,
+ CONNECTION_LIMIT_EXCEEDED = 0x09,
+ SYNCHRONOUS_CONNECTION_LIMIT_EXCEEDED = 0x0A,
+ CONNECTION_ALREADY_EXISTS = 0x0B,
+ COMMAND_DISALLOWED = 0x0C,
+ CONNECTION_REJECTED_LIMITED_RESOURCES = 0x0D,
+ CONNECTION_REJECTED_SECURITY_REASONS = 0x0E,
+ CONNECTION_REJECTED_UNACCEPTABLE_BD_ADDR = 0x0F,
+ CONNECTION_ACCEPT_TIMEOUT = 0x10,
+ UNSUPPORTED_FEATURE_OR_PARAMETER_VALUE = 0x11,
+ INVALID_HCI_COMMAND_PARAMETERS = 0x12,
+ REMOTE_USER_TERMINATED_CONNECTION = 0x13,
+ REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
+ REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
+ CONNECTION_TERMINATED_BY_LOCAL_HOST = 0x16,
+ REPEATED_ATTEMPTS = 0x17,
+ PAIRING_NOT_ALLOWED = 0x18,
+ UNKNOWN_LMP_PDU = 0x19,
+ UNSUPPORTED_REMOTE_OR_LMP_FEATURE = 0x1A,
+ SCO_OFFSET_REJECTED = 0x1B,
+ SCO_INTERVAL_REJECTED = 0x1C,
+ SCO_AIR_MODE_REJECTED = 0x1D,
+ INVALID_LMP_OR_LL_PARAMETERS = 0x1E,
+ UNSPECIFIED_ERROR = 0x1F,
+ UNSUPPORTED_LMP_OR_LL_PARAMETER = 0x20,
+ ROLE_CHANGE_NOT_ALLOWED = 0x21,
+ TRANSACTION_RESPONSE_TIMEOUT = 0x22,
+ LINK_LAYER_COLLISION = 0x23,
+ ENCRYPTION_MODE_NOT_ACCEPTABLE = 0x25,
+ ROLE_SWITCH_FAILED = 0x35,
+ HOST_BUSY = 0x38,
+ CONTROLLER_BUSY = 0x3A,
+ ADVERTISING_TIMEOUT = 0x3C,
+ CONNECTION_FAILED_ESTABLISHMENT = 0x3E,
+ UNKNOWN_ADVERTISING_IDENTIFIER = 0x42,
+ LIMIT_REACHED = 0x43,
+ OPERATION_CANCELLED_BY_HOST = 0x44,
+ PACKET_TOO_LONG = 0x45,
+}
+
+// Events that are defined with their respective commands
+
+packet CommandComplete : Event (event_code = COMMAND_COMPLETE) {
+ num_hci_command_packets : 8,
+ command_op_code : OpCode,
+ _payload_,
+}
+
+packet CommandStatus : Event (event_code = COMMAND_STATUS) {
+ status : ErrorCode, // SUCCESS means PENDING
+ num_hci_command_packets : 8,
+ command_op_code : OpCode,
+ _payload_,
+}
+
+packet VendorSpecificEvent : Event (event_code = VENDOR_SPECIFIC) {
+ subevent_code : VseSubeventCode,
+ _payload_,
+}
+
+ // Credits
+packet NoCommandComplete : CommandComplete (command_op_code = NONE) {
+}
+
+struct Lap { // Lower Address Part
+ lap : 6,
+ _reserved_ : 2,
+ _fixed_ = 0x9e8b : 16,
+}
+
+ // LINK_CONTROL
+packet Inquiry : Command (op_code = INQUIRY) {
+ lap : Lap,
+ inquiry_length : 8, // 0x1 - 0x30 (times 1.28s)
+ num_responses : 8, // 0x00 unlimited
+}
+
+test Inquiry {
+ "\x01\x04\x05\x33\x8b\x9e\xaa\xbb",
+}
+
+packet InquiryStatus : CommandStatus (command_op_code = INQUIRY) {
+}
+
+test InquiryStatus {
+ "\x0f\x04\x00\x01\x01\x04",
+}
+
+packet InquiryCancel : Command (op_code = INQUIRY_CANCEL) {
+}
+
+test InquiryCancel {
+ "\x02\x04\x00",
+}
+
+packet InquiryCancelComplete : CommandComplete (command_op_code = INQUIRY_CANCEL) {
+ status : ErrorCode,
+}
+
+test InquiryCancelComplete {
+ "\x0e\x04\x01\x02\x04\x00",
+}
+
+packet PeriodicInquiryMode : Command (op_code = PERIODIC_INQUIRY_MODE) {
+ max_period_length : 16, // Range 0x0003 to 0xffff (times 1.28s)
+ min_period_length : 16, // Range 0x0002 to 0xfffe (times 1.28s)
+ lap : Lap,
+ inquiry_length : 8, // 0x1 - 0x30 (times 1.28s)
+ num_responses : 8, // 0x00 unlimited
+}
+
+test PeriodicInquiryMode {
+ "\x03\x04\x09\x12\x34\x56\x78\x11\x8b\x9e\x9a\xbc",
+}
+
+packet PeriodicInquiryModeComplete : CommandComplete (command_op_code = PERIODIC_INQUIRY_MODE) {
+ status : ErrorCode,
+}
+
+test PeriodicInquiryModeComplete {
+ "\x0e\x04\x01\x03\x04\x00",
+}
+
+packet ExitPeriodicInquiryMode : Command (op_code = EXIT_PERIODIC_INQUIRY_MODE) {
+}
+
+test ExitPeriodicInquiryMode {
+ "\x04\x04\x00",
+}
+
+packet ExitPeriodicInquiryModeComplete : CommandComplete (command_op_code = EXIT_PERIODIC_INQUIRY_MODE) {
+ status : ErrorCode,
+}
+
+test ExitPeriodicInquiryModeComplete {
+ "\x0e\x04\x01\x04\x04\x00",
+}
+
+enum PageScanRepetitionMode : 8 {
+ R0 = 0x00,
+ R1 = 0x01,
+ R2 = 0x02,
+}
+
+enum ClockOffsetValid : 1 {
+ INVALID = 0,
+ VALID = 1,
+}
+
+enum CreateConnectionRoleSwitch : 8 {
+ REMAIN_CENTRAL = 0x00,
+ ALLOW_ROLE_SWITCH = 0x01,
+}
+
+packet CreateConnection : Command (op_code = CREATE_CONNECTION) {
+ bd_addr : Address,
+ packet_type : 16,
+ page_scan_repetition_mode : PageScanRepetitionMode,
+ _reserved_ : 8,
+ clock_offset : 15,
+ clock_offset_valid : ClockOffsetValid,
+ allow_role_switch : CreateConnectionRoleSwitch,
+}
+
+packet CreateConnectionStatus : CommandStatus (command_op_code = CREATE_CONNECTION) {
+}
+
+enum DisconnectReason : 8 {
+ AUTHENTICATION_FAILURE = 0x05,
+ REMOTE_USER_TERMINATED_CONNECTION = 0x13,
+ REMOTE_DEVICE_TERMINATED_CONNECTION_LOW_RESOURCES = 0x14,
+ REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF = 0x15,
+ UNSUPPORTED_REMOTE_FEATURE = 0x1A,
+ PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED = 0x29,
+ UNACCEPTABLE_CONNECTION_PARAMETERS = 0x3B,
+}
+
+packet Disconnect : Command (op_code = DISCONNECT) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ reason : DisconnectReason,
+}
+
+packet DisconnectStatus : CommandStatus (command_op_code = DISCONNECT) {
+}
+
+packet AddScoConnection : Command (op_code = ADD_SCO_CONNECTION) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ packet_type : 16,
+}
+
+packet AddScoConnectionStatus : CommandStatus (command_op_code = ADD_SCO_CONNECTION) {
+}
+
+packet CreateConnectionCancel : Command (op_code = CREATE_CONNECTION_CANCEL) {
+ bd_addr : Address,
+}
+
+packet CreateConnectionCancelComplete : CommandComplete (command_op_code = CREATE_CONNECTION_CANCEL) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+enum AcceptConnectionRequestRole : 8 {
+ BECOME_CENTRAL = 0x00,
+ REMAIN_PERIPHERAL = 0x01,
+}
+
+packet AcceptConnectionRequest : Command (op_code = ACCEPT_CONNECTION_REQUEST) {
+ bd_addr : Address,
+ role : AcceptConnectionRequestRole,
+}
+
+packet AcceptConnectionRequestStatus : CommandStatus (command_op_code = ACCEPT_CONNECTION_REQUEST) {
+}
+
+enum RejectConnectionReason : 8 {
+ LIMITED_RESOURCES = 0x0D,
+ SECURITY_REASONS = 0x0E,
+ UNACCEPTABLE_BD_ADDR = 0x0F,
+}
+
+packet RejectConnectionRequest : Command (op_code = REJECT_CONNECTION_REQUEST) {
+ bd_addr : Address,
+ reason : RejectConnectionReason,
+}
+
+packet RejectConnectionRequestStatus : CommandStatus (command_op_code = REJECT_CONNECTION_REQUEST) {
+}
+
+packet LinkKeyRequestReply : Command (op_code = LINK_KEY_REQUEST_REPLY) {
+ bd_addr : Address,
+ link_key : 8[16],
+}
+
+packet LinkKeyRequestReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet LinkKeyRequestNegativeReply : Command (op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) {
+ bd_addr : Address,
+}
+
+packet LinkKeyRequestNegativeReplyComplete : CommandComplete (command_op_code = LINK_KEY_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet PinCodeRequestReply : Command (op_code = PIN_CODE_REQUEST_REPLY) {
+ bd_addr : Address,
+ pin_code_length : 5, // 0x01 - 0x10
+ _reserved_ : 3,
+ pin_code : 8[16], // string parameter, first octet first
+}
+
+packet PinCodeRequestReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet PinCodeRequestNegativeReply : Command (op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) {
+ bd_addr : Address,
+}
+
+packet PinCodeRequestNegativeReplyComplete : CommandComplete (command_op_code = PIN_CODE_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet ChangeConnectionPacketType : Command (op_code = CHANGE_CONNECTION_PACKET_TYPE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ packet_type : 16,
+}
+
+packet ChangeConnectionPacketTypeStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_PACKET_TYPE) {
+}
+
+packet AuthenticationRequested : Command (op_code = AUTHENTICATION_REQUESTED) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet AuthenticationRequestedStatus : CommandStatus (command_op_code = AUTHENTICATION_REQUESTED) {
+}
+
+packet SetConnectionEncryption : Command (op_code = SET_CONNECTION_ENCRYPTION) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ encryption_enable : Enable,
+}
+
+packet SetConnectionEncryptionStatus : CommandStatus (command_op_code = SET_CONNECTION_ENCRYPTION) {
+}
+
+packet ChangeConnectionLinkKey : Command (op_code = CHANGE_CONNECTION_LINK_KEY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ChangeConnectionLinkKeyStatus : CommandStatus (command_op_code = CHANGE_CONNECTION_LINK_KEY) {
+}
+
+enum KeyFlag : 8 {
+ SEMI_PERMANENT = 0x00,
+ TEMPORARY = 0x01,
+}
+
+packet CentralLinkKey : Command (op_code = CENTRAL_LINK_KEY) {
+ key_flag : KeyFlag,
+}
+
+packet CentralLinkKeyStatus : CommandStatus (command_op_code = CENTRAL_LINK_KEY) {
+}
+
+packet RemoteNameRequest : Command (op_code = REMOTE_NAME_REQUEST) {
+ bd_addr : Address,
+ page_scan_repetition_mode : PageScanRepetitionMode,
+ _reserved_ : 8,
+ clock_offset : 15,
+ clock_offset_valid : ClockOffsetValid,
+}
+
+packet RemoteNameRequestStatus : CommandStatus (command_op_code = REMOTE_NAME_REQUEST) {
+}
+
+packet RemoteNameRequestCancel : Command (op_code = REMOTE_NAME_REQUEST_CANCEL) {
+ bd_addr : Address,
+}
+
+packet RemoteNameRequestCancelComplete : CommandComplete (command_op_code = REMOTE_NAME_REQUEST_CANCEL) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet ReadRemoteSupportedFeatures : Command (op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadRemoteSupportedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_SUPPORTED_FEATURES) {
+}
+
+packet ReadRemoteExtendedFeatures : Command (op_code = READ_REMOTE_EXTENDED_FEATURES) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ page_number : 8,
+}
+
+packet ReadRemoteExtendedFeaturesStatus : CommandStatus (command_op_code = READ_REMOTE_EXTENDED_FEATURES) {
+}
+
+packet ReadRemoteVersionInformation : Command (op_code = READ_REMOTE_VERSION_INFORMATION) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadRemoteVersionInformationStatus : CommandStatus (command_op_code = READ_REMOTE_VERSION_INFORMATION) {
+}
+
+packet ReadClockOffset : Command (op_code = READ_CLOCK_OFFSET) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadClockOffsetStatus : CommandStatus (command_op_code = READ_CLOCK_OFFSET) {
+}
+
+packet ReadLmpHandle : Command (op_code = READ_LMP_HANDLE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadLmpHandleComplete : CommandComplete (command_op_code = READ_LMP_HANDLE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ lmp_handle : 8,
+ _reserved_ : 32,
+}
+
+enum SynchronousPacketTypeBits : 16 {
+ HV1_ALLOWED = 0x0001,
+ HV2_ALLOWED = 0x0002,
+ HV3_ALLOWED = 0x0004,
+ EV3_ALLOWED = 0x0008,
+ EV4_ALLOWED = 0x0010,
+ EV5_ALLOWED = 0x0020,
+ NO_2_EV3_ALLOWED = 0x0040,
+ NO_3_EV3_ALLOWED = 0x0080,
+ NO_2_EV5_ALLOWED = 0x0100,
+ NO_3_EV5_ALLOWED = 0x0200,
+}
+
+enum RetransmissionEffort : 8 {
+ NO_RETRANSMISSION = 0x00,
+ OPTIMIZED_FOR_POWER = 0x01,
+ OPTIMIZED_FOR_LINK_QUALITY = 0x02,
+ DO_NOT_CARE = 0xFF,
+}
+
+packet SetupSynchronousConnection : Command (op_code = SETUP_SYNCHRONOUS_CONNECTION) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ transmit_bandwidth : 32,
+ receive_bandwidth : 32,
+ max_latency : 16, // 0-3 reserved, 0xFFFF = don't care
+ voice_setting : 10,
+ _reserved_ : 6,
+ retransmission_effort : RetransmissionEffort,
+ packet_type : 16, // See SynchronousPacketTypeBits
+}
+
+packet SetupSynchronousConnectionStatus : CommandStatus (command_op_code = SETUP_SYNCHRONOUS_CONNECTION) {
+}
+
+packet AcceptSynchronousConnection : Command (op_code = ACCEPT_SYNCHRONOUS_CONNECTION) {
+ bd_addr : Address,
+ transmit_bandwidth : 32,
+ receive_bandwidth : 32,
+ max_latency : 16, // 0-3 reserved, 0xFFFF = don't care
+ voice_setting : 10,
+ _reserved_ : 6,
+ retransmission_effort : RetransmissionEffort,
+ packet_type : 16, // See SynchronousPacketTypeBits
+}
+
+packet AcceptSynchronousConnectionStatus : CommandStatus (command_op_code = ACCEPT_SYNCHRONOUS_CONNECTION) {
+}
+
+packet RejectSynchronousConnection : Command (op_code = REJECT_SYNCHRONOUS_CONNECTION) {
+ bd_addr : Address,
+ reason : RejectConnectionReason,
+}
+
+packet RejectSynchronousConnectionStatus : CommandStatus (command_op_code = REJECT_SYNCHRONOUS_CONNECTION) {
+}
+
+enum IoCapability : 8 {
+ DISPLAY_ONLY = 0x00,
+ DISPLAY_YES_NO = 0x01,
+ KEYBOARD_ONLY = 0x02,
+ NO_INPUT_NO_OUTPUT = 0x03,
+}
+
+enum OobDataPresent : 8 {
+ NOT_PRESENT = 0x00,
+ P_192_PRESENT = 0x01,
+ P_256_PRESENT = 0x02,
+ P_192_AND_256_PRESENT = 0x03,
+}
+
+enum AuthenticationRequirements : 8 {
+ NO_BONDING = 0x00,
+ NO_BONDING_MITM_PROTECTION = 0x01,
+ DEDICATED_BONDING = 0x02,
+ DEDICATED_BONDING_MITM_PROTECTION = 0x03,
+ GENERAL_BONDING = 0x04,
+ GENERAL_BONDING_MITM_PROTECTION = 0x05,
+}
+
+packet IoCapabilityRequestReply : Command (op_code = IO_CAPABILITY_REQUEST_REPLY) {
+ bd_addr : Address,
+ io_capability : IoCapability,
+ oob_present : OobDataPresent,
+ authentication_requirements : AuthenticationRequirements,
+}
+
+packet IoCapabilityRequestReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet UserConfirmationRequestReply : Command (op_code = USER_CONFIRMATION_REQUEST_REPLY) {
+ bd_addr : Address,
+}
+
+packet UserConfirmationRequestReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet UserConfirmationRequestNegativeReply : Command (op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) {
+ bd_addr : Address,
+}
+
+packet UserConfirmationRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_CONFIRMATION_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet UserPasskeyRequestReply : Command (op_code = USER_PASSKEY_REQUEST_REPLY) {
+ bd_addr : Address,
+ numeric_value : 32, // 000000-999999 decimal or 0x0-0xF423F
+}
+
+packet UserPasskeyRequestReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet UserPasskeyRequestNegativeReply : Command (op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) {
+ bd_addr : Address,
+}
+
+packet UserPasskeyRequestNegativeReplyComplete : CommandComplete (command_op_code = USER_PASSKEY_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet RemoteOobDataRequestReply : Command (op_code = REMOTE_OOB_DATA_REQUEST_REPLY) {
+ bd_addr : Address,
+ c : 8[16],
+ r : 8[16],
+}
+
+packet RemoteOobDataRequestReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet RemoteOobDataRequestNegativeReply : Command (op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) {
+ bd_addr : Address,
+}
+
+packet RemoteOobDataRequestNegativeReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_DATA_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet IoCapabilityRequestNegativeReply : Command (op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) {
+ bd_addr : Address,
+ reason : ErrorCode,
+}
+
+packet IoCapabilityRequestNegativeReplyComplete : CommandComplete (command_op_code = IO_CAPABILITY_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+enum ScoCodingFormatValues : 8 {
+ ULAW_LONG = 0x00,
+ ALAW_LONG = 0x01,
+ CVSD = 0x02,
+ TRANSPARENT = 0x03,
+ LINEAR_PCM = 0x04,
+ MSBC = 0x05,
+ LC3 = 0x06,
+ VENDOR_SPECIFIC = 0xFF,
+}
+
+struct ScoCodingFormat {
+ coding_format : ScoCodingFormatValues,
+ company_id : 16,
+ vendor_specific_codec_id : 16,
+}
+
+enum ScoPcmDataFormat : 8 {
+ NOT_USED = 0x00,
+ ONES_COMPLEMENT = 0x01,
+ TWOS_COMPLEMENT = 0x02,
+ SIGN_MAGNITUDE = 0x03,
+ UNSIGNED = 0x04,
+}
+
+enum ScoDataPath : 8 {
+ HCI = 0x00,
+ // 0x01 to 0xFE are Logical_Channel_Number.
+ // The meaning of the logical channels will be vendor specific.
+ // In GD and legacy Android Bluetooth stack, we use channel 0x01 for hardware
+ // offloaded SCO encoding
+ GD_PCM = 0x01,
+ AUDIO_TEST_MODE = 0xFF,
+}
+
+packet EnhancedSetupSynchronousConnection : Command (op_code = ENHANCED_SETUP_SYNCHRONOUS_CONNECTION) {
+ connection_handle: 12,
+ _reserved_ : 4,
+ // Next two items
+ // [0x00000000, 0xFFFFFFFE] Bandwidth in octets per second.
+ // [0xFFFFFFFF]: Don't care
+ transmit_bandwidth : 32,
+ receive_bandwidth : 32,
+ transmit_coding_format : ScoCodingFormat,
+ receive_coding_format : ScoCodingFormat,
+ // Next two items
+ // [0x0001, 0xFFFF]: the actual size of the over-the-air encoded frame in
+ // octets.
+ transmit_codec_frame_size : 16,
+ receive_codec_frame_size : 16,
+ // Next two items
+ // Host to Controller nominal data rate in octets per second.
+ input_bandwidth : 32,
+ output_bandwidth : 32,
+ input_coding_format : ScoCodingFormat,
+ output_coding_format : ScoCodingFormat,
+ // Next two items
+ // Size, in bits, of the sample or framed data
+ input_coded_data_bits : 16,
+ output_coded_data_bits : 16,
+ input_pcm_data_format : ScoPcmDataFormat,
+ output_pcm_data_format : ScoPcmDataFormat,
+ // Next two items
+ // The number of bit positions within an audio sample that the MSB of the
+ // sample is away from starting at the MSB of the data.
+ input_pcm_sample_payload_msb_position : 8,
+ output_pcm_sample_payload_msb_position : 8,
+ input_data_path : ScoDataPath,
+ output_data_path : ScoDataPath,
+ // Next two items
+ // [1, 255] The number of bits in each unit of data received from the Host
+ // over the audio data transport.
+ // [0] Not applicable (implied by the choice of audio data transport)
+ input_transport_unit_bits : 8,
+ output_transport_unit_bits : 8,
+ // [0x0004, 0xFFFE]: in milliseconds
+ // Upper limit represent the sum of the synchronous interval and the size
+ // of the eSCO window, where the eSCO window is reserved slots plus the
+ // retransmission window
+ // [0xFFFF]: don't care
+ max_latency: 16,
+ packet_type : 16, // See SynchronousPacketTypeBits
+ retransmission_effort : RetransmissionEffort,
+}
+
+test EnhancedSetupSynchronousConnection {
+ "\x3d\x04\x3b\x02\x00\x40\x1f\x00\x00\x40\x1f\x00\x00\x05\x00\x00\x00\x00\x05\x00\x00\x00\x00\x3c\x00\x3c\x00\x00\x7d\x00\x00\x00\x7d\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x00\x10\x00\x10\x00\x02\x02\x00\x00\x01\x01\x00\x00\x0d\x00\x88\x03\x02",
+}
+
+packet EnhancedSetupSynchronousConnectionStatus : CommandStatus (command_op_code = ENHANCED_SETUP_SYNCHRONOUS_CONNECTION) {
+}
+
+packet EnhancedAcceptSynchronousConnection : Command (op_code = ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION) {
+ bd_addr : Address,
+ // Next two items
+ // [0x00000000, 0xFFFFFFFE] Bandwidth in octets per second.
+ // [0xFFFFFFFF]: Don't care
+ transmit_bandwidth : 32,
+ receive_bandwidth : 32,
+ transmit_coding_format : ScoCodingFormat,
+ receive_coding_format : ScoCodingFormat,
+ // Next two items
+ // [0x0001, 0xFFFF]: the actual size of the over-the-air encoded frame in
+ // octets.
+ transmit_codec_frame_size : 16,
+ receive_codec_frame_size : 16,
+ // Next two items
+ // Host to Controller nominal data rate in octets per second.
+ input_bandwidth : 32,
+ output_bandwidth : 32,
+ input_coding_format : ScoCodingFormat,
+ output_coding_format : ScoCodingFormat,
+ // Next two items
+ // Size, in bits, of the sample or framed data
+ input_coded_data_bits : 16,
+ output_coded_data_bits : 16,
+ input_pcm_data_format : ScoPcmDataFormat,
+ output_pcm_data_format : ScoPcmDataFormat,
+ // Next two items
+ // The number of bit positions within an audio sample that the MSB of the
+ // sample is away from starting at the MSB of the data.
+ input_pcm_sample_payload_msb_position : 8,
+ output_pcm_sample_payload_msb_position : 8,
+ input_data_path : ScoDataPath,
+ output_data_path : ScoDataPath,
+ // Next two items
+ // [1, 255] The number of bits in each unit of data received from the Host
+ // over the audio data transport.
+ // [0] Not applicable (implied by the choice of audio data transport)
+ input_transport_unit_bits : 8,
+ output_transport_unit_bits : 8,
+ // [0x0004, 0xFFFE]: in milliseconds
+ // Upper limit represent the sum of the synchronous interval and the size
+ // of the eSCO window, where the eSCO window is reserved slots plus the
+ // retransmission window
+ // [0xFFFF]: don't care
+ max_latency : 16,
+ packet_type : 16, // See SynchronousPacketTypeBits
+ retransmission_effort : RetransmissionEffort,
+}
+
+packet EnhancedAcceptSynchronousConnectionStatus : CommandStatus (command_op_code = ENHANCED_ACCEPT_SYNCHRONOUS_CONNECTION) {
+}
+
+packet RemoteOobExtendedDataRequestReply : Command (op_code = REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY) {
+ bd_addr : Address,
+ c_192 : 8[16],
+ r_192 : 8[16],
+ c_256 : 8[16],
+ r_256 : 8[16],
+}
+
+packet RemoteOobExtendedDataRequestReplyComplete : CommandComplete (command_op_code = REMOTE_OOB_EXTENDED_DATA_REQUEST_REPLY) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+
+ // LINK_POLICY
+packet HoldMode : Command (op_code = HOLD_MODE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ hold_mode_max_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+ hold_mode_min_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+}
+
+packet HoldModeStatus : CommandStatus (command_op_code = HOLD_MODE) {
+}
+
+
+packet SniffMode : Command (op_code = SNIFF_MODE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ sniff_max_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+ sniff_min_interval: 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+ sniff_attempt: 16, // 0x0001-0x7FFF (1.25ms-40.9s)
+ sniff_timeout: 16, // 0x0000-0x7FFF (0ms-40.9s)
+}
+
+packet SniffModeStatus : CommandStatus (command_op_code = SNIFF_MODE) {
+}
+
+
+packet ExitSniffMode : Command (op_code = EXIT_SNIFF_MODE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ExitSniffModeStatus : CommandStatus (command_op_code = EXIT_SNIFF_MODE) {
+}
+
+enum ServiceType : 8 {
+ NO_TRAFFIC = 0x00,
+ BEST_EFFORT = 0x01,
+ GUARANTEED = 0x02,
+}
+
+packet QosSetup : Command (op_code = QOS_SETUP) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ _reserved_ : 8,
+ service_type : ServiceType,
+ token_rate : 32, // Octets/s
+ peak_bandwidth : 32, // Octets/s
+ latency : 32, // Octets/s
+ delay_variation : 32, // microseconds
+}
+
+packet QosSetupStatus : CommandStatus (command_op_code = QOS_SETUP) {
+}
+
+packet RoleDiscovery : Command (op_code = ROLE_DISCOVERY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum Role : 8 {
+ CENTRAL = 0x00,
+ PERIPHERAL = 0x01,
+}
+
+packet RoleDiscoveryComplete : CommandComplete (command_op_code = ROLE_DISCOVERY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ current_role : Role,
+}
+
+packet SwitchRole : Command (op_code = SWITCH_ROLE) {
+ bd_addr : Address,
+ role : Role,
+}
+
+packet SwitchRoleStatus : CommandStatus (command_op_code = SWITCH_ROLE) {
+}
+
+
+packet ReadLinkPolicySettings : Command (op_code = READ_LINK_POLICY_SETTINGS) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum LinkPolicy : 16 {
+ ENABLE_ROLE_SWITCH = 0x01,
+ ENABLE_HOLD_MODE = 0x02,
+ ENABLE_SNIFF_MODE = 0x04,
+ ENABLE_PARK_MODE = 0x08, // deprecated after 5.0
+}
+
+packet ReadLinkPolicySettingsComplete : CommandComplete (command_op_code = READ_LINK_POLICY_SETTINGS) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ link_policy_settings : 16,
+}
+
+packet WriteLinkPolicySettings : Command (op_code = WRITE_LINK_POLICY_SETTINGS) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ link_policy_settings : 16,
+}
+
+packet WriteLinkPolicySettingsComplete : CommandComplete (command_op_code = WRITE_LINK_POLICY_SETTINGS) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadDefaultLinkPolicySettings : Command (op_code = READ_DEFAULT_LINK_POLICY_SETTINGS) {
+}
+
+packet ReadDefaultLinkPolicySettingsComplete : CommandComplete (command_op_code = READ_DEFAULT_LINK_POLICY_SETTINGS) {
+ status : ErrorCode,
+ default_link_policy_settings : 16,
+}
+
+packet WriteDefaultLinkPolicySettings : Command (op_code = WRITE_DEFAULT_LINK_POLICY_SETTINGS) {
+ default_link_policy_settings : 16,
+}
+
+packet WriteDefaultLinkPolicySettingsComplete : CommandComplete (command_op_code = WRITE_DEFAULT_LINK_POLICY_SETTINGS) {
+ status : ErrorCode,
+}
+
+enum FlowDirection : 8 {
+ OUTGOING_FLOW = 0x00,
+ INCOMING_FLOW = 0x01,
+}
+
+packet FlowSpecification : Command (op_code = FLOW_SPECIFICATION) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ _reserved_ : 8,
+ flow_direction : FlowDirection,
+ service_type : ServiceType,
+ token_rate : 32, // Octets/s
+ token_bucket_size : 32,
+ peak_bandwidth : 32, // Octets/s
+ access_latency : 32, // Octets/s
+}
+
+packet FlowSpecificationStatus : CommandStatus (command_op_code = FLOW_SPECIFICATION) {
+}
+
+packet SniffSubrating : Command (op_code = SNIFF_SUBRATING) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ maximum_latency : 16, // 0x0002-0xFFFE (1.25ms-40.9s)
+ minimum_remote_timeout : 16, // 0x0000-0xFFFE (0-40.9s)
+ minimum_local_timeout: 16, // 0x0000-0xFFFE (0-40.9s)
+}
+
+packet SniffSubratingComplete : CommandComplete (command_op_code = SNIFF_SUBRATING) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+ // CONTROLLER_AND_BASEBAND
+packet SetEventMask : Command (op_code = SET_EVENT_MASK) {
+ event_mask : 64,
+}
+
+packet SetEventMaskComplete : CommandComplete (command_op_code = SET_EVENT_MASK) {
+ status : ErrorCode,
+}
+
+packet Reset : Command (op_code = RESET) {
+}
+
+test Reset {
+ "\x03\x0c\x00",
+}
+
+packet ResetComplete : CommandComplete (command_op_code = RESET) {
+ status : ErrorCode,
+}
+
+test ResetComplete {
+ "\x0e\x04\x01\x03\x0c\x00",
+ "\x0e\x04\x01\x03\x0c\x01", // unknown command
+}
+
+enum FilterType : 8 {
+ CLEAR_ALL_FILTERS = 0x00,
+ INQUIRY_RESULT = 0x01,
+ CONNECTION_SETUP = 0x02,
+}
+
+packet SetEventFilter : Command (op_code = SET_EVENT_FILTER) {
+ filter_type : FilterType,
+ _body_,
+}
+
+packet SetEventFilterComplete : CommandComplete (command_op_code = SET_EVENT_FILTER) {
+ status : ErrorCode,
+}
+
+packet SetEventFilterClearAll : SetEventFilter (filter_type = CLEAR_ALL_FILTERS) {
+}
+
+enum FilterConditionType : 8 {
+ ALL_DEVICES = 0x00,
+ CLASS_OF_DEVICE = 0x01,
+ ADDRESS = 0x02,
+}
+
+packet SetEventFilterInquiryResult : SetEventFilter (filter_type = INQUIRY_RESULT) {
+ filter_condition_type : FilterConditionType,
+ _body_,
+}
+
+packet SetEventFilterInquiryResultAllDevices : SetEventFilterInquiryResult (filter_condition_type = ALL_DEVICES) {
+}
+
+packet SetEventFilterInquiryResultClassOfDevice : SetEventFilterInquiryResult (filter_condition_type = CLASS_OF_DEVICE) {
+ class_of_device : 24,
+ class_of_device_mask : 24,
+}
+
+packet SetEventFilterInquiryResultAddress : SetEventFilterInquiryResult (filter_condition_type = ADDRESS) {
+ address : Address,
+}
+
+packet SetEventFilterConnectionSetup : SetEventFilter (filter_type = CONNECTION_SETUP) {
+ filter_condition_type : FilterConditionType,
+ _body_,
+}
+
+enum AutoAcceptFlag : 8 {
+ AUTO_ACCEPT_OFF = 0x01,
+ AUTO_ACCEPT_ON_ROLE_SWITCH_DISABLED = 0x02,
+ AUTO_ACCEPT_ON_ROLE_SWITCH_ENABLED = 0x03,
+}
+
+packet SetEventFilterConnectionSetupAllDevices : SetEventFilterConnectionSetup (filter_condition_type = ALL_DEVICES) {
+ auto_accept_flag : AutoAcceptFlag,
+}
+
+packet SetEventFilterConnectionSetupClassOfDevice : SetEventFilterConnectionSetup (filter_condition_type = CLASS_OF_DEVICE) {
+ class_of_device : 24,
+ class_of_device_mask : 24,
+ auto_accept_flag : AutoAcceptFlag,
+}
+
+packet SetEventFilterConnectionSetupAddress : SetEventFilterConnectionSetup (filter_condition_type = ADDRESS) {
+ address : Address,
+ auto_accept_flag : AutoAcceptFlag,
+}
+
+packet Flush : Command (op_code = FLUSH) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet FlushComplete : CommandComplete (command_op_code = FLUSH) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum PinType : 8 {
+ VARIABLE = 0,
+ FIXED = 1,
+}
+
+packet ReadPinType : Command (op_code = READ_PIN_TYPE) {
+}
+
+packet ReadPinTypeComplete : CommandComplete (command_op_code = READ_PIN_TYPE) {
+ status : ErrorCode,
+ pin_type : PinType,
+}
+
+packet WritePinType : Command (op_code = WRITE_PIN_TYPE) {
+ pin_type : PinType,
+}
+
+packet WritePinTypeComplete : CommandComplete (command_op_code = WRITE_PIN_TYPE) {
+ status : ErrorCode,
+}
+
+enum ReadStoredLinkKeyReadAllFlag : 8 {
+ SPECIFIED_BD_ADDR = 0x00,
+ ALL = 0x01,
+}
+
+packet ReadStoredLinkKey : Command (op_code = READ_STORED_LINK_KEY) {
+ bd_addr : Address,
+ read_all_flag : ReadStoredLinkKeyReadAllFlag,
+}
+
+packet ReadStoredLinkKeyComplete : CommandComplete (command_op_code = READ_STORED_LINK_KEY) {
+ status : ErrorCode,
+ max_num_keys : 16,
+ num_keys_read : 16,
+}
+
+struct KeyAndAddress {
+ address : Address,
+ link_key : 8[16],
+}
+
+packet WriteStoredLinkKey : Command (op_code = WRITE_STORED_LINK_KEY) {
+ _count_(keys_to_write) : 8, // 0x01-0x0B
+ keys_to_write : KeyAndAddress[],
+}
+
+packet WriteStoredLinkKeyComplete : CommandComplete (command_op_code = WRITE_STORED_LINK_KEY) {
+ status : ErrorCode,
+ num_keys_written : 8,
+}
+
+enum DeleteStoredLinkKeyDeleteAllFlag : 8 {
+ SPECIFIED_BD_ADDR = 0x00,
+ ALL = 0x01,
+}
+
+packet DeleteStoredLinkKey : Command (op_code = DELETE_STORED_LINK_KEY) {
+ bd_addr : Address,
+ delete_all_flag : DeleteStoredLinkKeyDeleteAllFlag,
+}
+
+packet DeleteStoredLinkKeyComplete : CommandComplete (command_op_code = DELETE_STORED_LINK_KEY) {
+ status : ErrorCode,
+ num_keys_deleted : 16,
+}
+
+packet WriteLocalName : Command (op_code = WRITE_LOCAL_NAME) {
+ local_name : 8[248], // Null-terminated UTF-8 encoded name
+}
+
+packet WriteLocalNameComplete : CommandComplete (command_op_code = WRITE_LOCAL_NAME) {
+ status : ErrorCode,
+}
+
+packet ReadLocalName : Command (op_code = READ_LOCAL_NAME) {
+}
+
+packet ReadLocalNameComplete : CommandComplete (command_op_code = READ_LOCAL_NAME) {
+ status : ErrorCode,
+ local_name : 8[248], // Null-terminated UTF-8 encoded name
+}
+
+packet ReadConnectionAcceptTimeout : Command (op_code = READ_CONNECTION_ACCEPT_TIMEOUT) {
+}
+
+packet ReadConnectionAcceptTimeoutComplete : CommandComplete (command_op_code = READ_CONNECTION_ACCEPT_TIMEOUT) {
+ status : ErrorCode,
+ conn_accept_timeout : 16, // 0x0001 to 0xB540 (N * 0.625 ms) 0.625 ms to 29 s
+}
+
+packet WriteConnectionAcceptTimeout : Command (op_code = WRITE_CONNECTION_ACCEPT_TIMEOUT) {
+ conn_accept_timeout : 16, // 0x0001 to 0xB540 (N * 0.625 ms) 0.625 ms to 29 s, Default 0x1FA0, 5.06s
+}
+
+packet WriteConnectionAcceptTimeoutComplete : CommandComplete (command_op_code = WRITE_CONNECTION_ACCEPT_TIMEOUT) {
+ status : ErrorCode,
+}
+
+packet ReadPageTimeout : Command (op_code = READ_PAGE_TIMEOUT) {
+}
+
+test ReadPageTimeout {
+ "\x17\x0c\x00",
+}
+
+packet ReadPageTimeoutComplete : CommandComplete (command_op_code = READ_PAGE_TIMEOUT) {
+ status : ErrorCode,
+ page_timeout : 16,
+}
+
+test ReadPageTimeoutComplete {
+ "\x0e\x06\x01\x17\x0c\x00\x11\x22",
+}
+
+packet WritePageTimeout : Command (op_code = WRITE_PAGE_TIMEOUT) {
+ page_timeout : 16,
+}
+
+test WritePageTimeout {
+ "\x18\x0c\x02\x00\x20",
+}
+
+packet WritePageTimeoutComplete : CommandComplete (command_op_code = WRITE_PAGE_TIMEOUT) {
+ status : ErrorCode,
+}
+
+test WritePageTimeoutComplete {
+ "\x0e\x04\x01\x18\x0c\x00",
+}
+
+enum ScanEnable : 8 {
+ NO_SCANS = 0x00,
+ INQUIRY_SCAN_ONLY = 0x01,
+ PAGE_SCAN_ONLY = 0x02,
+ INQUIRY_AND_PAGE_SCAN = 0x03,
+}
+
+packet ReadScanEnable : Command (op_code = READ_SCAN_ENABLE) {
+}
+
+packet ReadScanEnableComplete : CommandComplete (command_op_code = READ_SCAN_ENABLE) {
+ status : ErrorCode,
+ scan_enable : ScanEnable,
+}
+
+packet WriteScanEnable : Command (op_code = WRITE_SCAN_ENABLE) {
+ scan_enable : ScanEnable,
+}
+
+packet WriteScanEnableComplete : CommandComplete (command_op_code = WRITE_SCAN_ENABLE) {
+ status : ErrorCode,
+}
+
+packet ReadPageScanActivity : Command (op_code = READ_PAGE_SCAN_ACTIVITY) {
+}
+
+packet ReadPageScanActivityComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_ACTIVITY) {
+ status : ErrorCode,
+ page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+ page_scan_window : 16, // 0x0011 to PageScanInterval
+}
+
+packet WritePageScanActivity : Command (op_code = WRITE_PAGE_SCAN_ACTIVITY) {
+ page_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+ page_scan_window : 16, // 0x0011 to PageScanInterval
+}
+
+packet WritePageScanActivityComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_ACTIVITY) {
+ status : ErrorCode,
+}
+
+packet ReadInquiryScanActivity : Command (op_code = READ_INQUIRY_SCAN_ACTIVITY) {
+}
+
+test ReadInquiryScanActivity {
+ "\x1d\x0c\x00",
+}
+
+packet ReadInquiryScanActivityComplete : CommandComplete (command_op_code = READ_INQUIRY_SCAN_ACTIVITY) {
+ status : ErrorCode,
+ inquiry_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+ inquiry_scan_window : 16, // Range: 0x0011 to 0x1000
+}
+
+test ReadInquiryScanActivityComplete {
+ "\x0e\x08\x01\x1d\x0c\x00\xaa\xbb\xcc\xdd",
+}
+
+packet WriteInquiryScanActivity : Command (op_code = WRITE_INQUIRY_SCAN_ACTIVITY) {
+ inquiry_scan_interval : 16, // Range: 0x0012 to 0x1000; only even values are valid * 0x625 ms
+ inquiry_scan_window : 16, // Range: 0x0011 to 0x1000
+}
+
+test WriteInquiryScanActivity {
+ "\x1e\x0c\x04\x00\x08\x12\x00",
+}
+
+packet WriteInquiryScanActivityComplete : CommandComplete (command_op_code = WRITE_INQUIRY_SCAN_ACTIVITY) {
+ status : ErrorCode,
+}
+
+test WriteInquiryScanActivityComplete {
+ "\x0e\x04\x01\x1e\x0c\x00",
+}
+
+enum AuthenticationEnable : 8 {
+ NOT_REQUIRED = 0x00,
+ REQUIRED = 0x01,
+}
+
+packet ReadAuthenticationEnable : Command (op_code = READ_AUTHENTICATION_ENABLE) {
+}
+
+packet ReadAuthenticationEnableComplete : CommandComplete (command_op_code = READ_AUTHENTICATION_ENABLE) {
+ status : ErrorCode,
+ authentication_enable : AuthenticationEnable,
+}
+
+packet WriteAuthenticationEnable : Command (op_code = WRITE_AUTHENTICATION_ENABLE) {
+ authentication_enable : AuthenticationEnable,
+}
+
+packet WriteAuthenticationEnableComplete : CommandComplete (command_op_code = WRITE_AUTHENTICATION_ENABLE) {
+ status : ErrorCode,
+}
+
+packet ReadClassOfDevice : Command (op_code = READ_CLASS_OF_DEVICE) {
+}
+
+packet ReadClassOfDeviceComplete : CommandComplete (command_op_code = READ_CLASS_OF_DEVICE) {
+ status : ErrorCode,
+ class_of_device : 24,
+}
+
+packet WriteClassOfDevice : Command (op_code = WRITE_CLASS_OF_DEVICE) {
+ class_of_device : 24,
+}
+
+packet WriteClassOfDeviceComplete : CommandComplete (command_op_code = WRITE_CLASS_OF_DEVICE) {
+ status : ErrorCode,
+}
+
+packet ReadVoiceSetting : Command (op_code = READ_VOICE_SETTING) {
+}
+
+packet ReadVoiceSettingComplete : CommandComplete (command_op_code = READ_VOICE_SETTING) {
+ status : ErrorCode,
+ voice_setting : 10,
+ _reserved_ : 6,
+}
+
+packet WriteVoiceSetting : Command (op_code = WRITE_VOICE_SETTING) {
+ voice_setting : 10,
+ _reserved_ : 6,
+}
+
+packet WriteVoiceSettingComplete : CommandComplete (command_op_code = WRITE_VOICE_SETTING) {
+ status : ErrorCode,
+}
+
+packet ReadAutomaticFlushTimeout : Command (op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadAutomaticFlushTimeoutComplete : CommandComplete (command_op_code = READ_AUTOMATIC_FLUSH_TIMEOUT) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ flush_timeout : 16,
+}
+
+packet WriteAutomaticFlushTimeout : Command (op_code = WRITE_AUTOMATIC_FLUSH_TIMEOUT) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ flush_timeout : 16, // 0x0000-0x07FF Default 0x0000 (No Automatic Flush)
+}
+
+packet WriteAutomaticFlushTimeoutComplete : CommandComplete (command_op_code = WRITE_AUTOMATIC_FLUSH_TIMEOUT) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadNumBroadcastRetransmits : Command (op_code = READ_NUM_BROADCAST_RETRANSMITS) {
+}
+
+packet ReadNumBroadcastRetransmitsComplete : CommandComplete (command_op_code = READ_NUM_BROADCAST_RETRANSMITS) {
+ status : ErrorCode,
+ num_broadcast_retransmissions : 8,
+}
+
+packet WriteNumBroadcastRetransmits : Command (op_code = WRITE_NUM_BROADCAST_RETRANSMITS) {
+ num_broadcast_retransmissions : 8,
+}
+
+packet WriteNumBroadcastRetransmitsComplete : CommandComplete (command_op_code = WRITE_NUM_BROADCAST_RETRANSMITS) {
+ status : ErrorCode,
+}
+
+enum HoldModeActivity : 8 {
+ MAINTAIN_CURRENT_POWER_STATE = 0x00,
+ SUSPEND_PAGE_SCAN = 0x01,
+ SUSPEND_INQUIRY_SCAN = 0x02,
+ SUSPEND_PERIODIC_INQUIRY = 0x03,
+}
+
+packet ReadHoldModeActivity : Command (op_code = READ_HOLD_MODE_ACTIVITY) {
+}
+
+packet ReadHoldModeActivityComplete : CommandComplete (command_op_code = READ_HOLD_MODE_ACTIVITY) {
+ status : ErrorCode,
+ hold_mode_activity : HoldModeActivity,
+}
+
+packet WriteHoldModeActivity : Command (op_code = WRITE_HOLD_MODE_ACTIVITY) {
+ hold_mode_activity : HoldModeActivity,
+}
+
+packet WriteHoldModeActivityComplete : CommandComplete (command_op_code = WRITE_HOLD_MODE_ACTIVITY) {
+ status : ErrorCode,
+}
+
+enum TransmitPowerLevelType : 8 {
+ CURRENT = 0x00,
+ MAXIMUM = 0x01,
+}
+
+packet ReadTransmitPowerLevel : Command (op_code = READ_TRANSMIT_POWER_LEVEL) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ transmit_power_level_type : TransmitPowerLevelType,
+}
+
+packet ReadTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_TRANSMIT_POWER_LEVEL) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ transmit_power_level : 8,
+}
+
+packet ReadSynchronousFlowControlEnable : Command (op_code = READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+}
+
+packet ReadSynchronousFlowControlEnableComplete : CommandComplete (command_op_code = READ_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+ status : ErrorCode,
+ enable : Enable,
+}
+
+packet WriteSynchronousFlowControlEnable : Command (op_code = WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+ enable : Enable,
+}
+
+packet WriteSynchronousFlowControlEnableComplete : CommandComplete (command_op_code = WRITE_SYNCHRONOUS_FLOW_CONTROL_ENABLE) {
+ status : ErrorCode,
+}
+
+packet SetControllerToHostFlowControl : Command (op_code = SET_CONTROLLER_TO_HOST_FLOW_CONTROL) {
+ acl : 1,
+ synchronous : 1,
+ _reserved_ : 6,
+}
+
+packet SetControllerToHostFlowControlComplete : CommandComplete (command_op_code = SET_CONTROLLER_TO_HOST_FLOW_CONTROL) {
+ status : ErrorCode,
+}
+
+packet HostBufferSize : Command (op_code = HOST_BUFFER_SIZE) {
+ host_acl_data_packet_length : 16,
+ host_synchronous_data_packet_length : 8,
+ host_total_num_acl_data_packets : 16,
+ host_total_num_synchronous_data_packets : 16,
+}
+
+test HostBufferSize {
+ "\x33\x0c\x07\x9b\x06\xff\x14\x00\x0a\x00",
+}
+
+packet HostBufferSizeComplete : CommandComplete (command_op_code = HOST_BUFFER_SIZE) {
+ status : ErrorCode,
+}
+
+test HostBufferSizeComplete {
+ "\x0e\x04\x01\x33\x0c\x00",
+}
+
+struct CompletedPackets {
+ connection_handle : 12,
+ _reserved_ : 4,
+ host_num_of_completed_packets : 16,
+}
+
+packet HostNumCompletedPackets : Command (op_code = HOST_NUMBER_OF_COMPLETED_PACKETS) {
+ _count_(completed_packets) : 8,
+ completed_packets : CompletedPackets[],
+}
+
+packet HostNumCompletedPacketsError : CommandComplete (command_op_code = HOST_NUMBER_OF_COMPLETED_PACKETS) {
+ error_code : ErrorCode,
+}
+
+packet ReadLinkSupervisionTimeout : Command (op_code = READ_LINK_SUPERVISION_TIMEOUT) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = READ_LINK_SUPERVISION_TIMEOUT) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet WriteLinkSupervisionTimeout : Command (op_code = WRITE_LINK_SUPERVISION_TIMEOUT) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+packet WriteLinkSupervisionTimeoutComplete : CommandComplete (command_op_code = WRITE_LINK_SUPERVISION_TIMEOUT) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadNumberOfSupportedIac : Command (op_code = READ_NUMBER_OF_SUPPORTED_IAC) {
+}
+
+test ReadNumberOfSupportedIac {
+ "\x38\x0c\x00",
+}
+
+packet ReadNumberOfSupportedIacComplete : CommandComplete (command_op_code = READ_NUMBER_OF_SUPPORTED_IAC) {
+ status : ErrorCode,
+ num_support_iac : 8,
+}
+
+test ReadNumberOfSupportedIacComplete {
+ "\x0e\x05\x01\x38\x0c\x00\x99",
+}
+
+packet ReadCurrentIacLap : Command (op_code = READ_CURRENT_IAC_LAP) {
+}
+
+test ReadCurrentIacLap {
+ "\x39\x0c\x00",
+}
+
+packet ReadCurrentIacLapComplete : CommandComplete (command_op_code = READ_CURRENT_IAC_LAP) {
+ status : ErrorCode,
+ _count_(laps_to_read) : 8,
+ laps_to_read : Lap[],
+}
+
+test ReadCurrentIacLapComplete {
+ "\x0e\x0b\x01\x39\x0c\x00\x02\x11\x8b\x9e\x22\x8b\x9e",
+}
+
+packet WriteCurrentIacLap : Command (op_code = WRITE_CURRENT_IAC_LAP) {
+ _count_(laps_to_write) : 8,
+ laps_to_write : Lap[],
+}
+
+test WriteCurrentIacLap {
+ "\x3a\x0c\x07\x02\x11\x8b\x9e\x22\x8b\x9e",
+}
+
+packet WriteCurrentIacLapComplete : CommandComplete (command_op_code = WRITE_CURRENT_IAC_LAP) {
+ status : ErrorCode,
+}
+
+test WriteCurrentIacLapComplete {
+ "\x0e\x04\x01\x3a\x0c\x00",
+}
+
+packet SetAfhHostChannelClassification : Command (op_code = SET_AFH_HOST_CHANNEL_CLASSIFICATION) {
+ afh_host_channel_classification : 8[10],
+}
+
+packet SetAfhHostChannelClassificationComplete : CommandComplete (command_op_code = SET_AFH_HOST_CHANNEL_CLASSIFICATION) {
+ status : ErrorCode,
+}
+
+enum InquiryScanType : 8 {
+ STANDARD = 0x00,
+ INTERLACED = 0x01,
+}
+
+packet ReadInquiryScanType : Command (op_code = READ_INQUIRY_SCAN_TYPE) {
+}
+
+packet ReadInquiryScanTypeComplete : CommandComplete (command_op_code = READ_INQUIRY_SCAN_TYPE) {
+ status : ErrorCode,
+ inquiry_scan_type : InquiryScanType,
+}
+
+packet WriteInquiryScanType : Command (op_code = WRITE_INQUIRY_SCAN_TYPE) {
+ inquiry_scan_type : InquiryScanType,
+}
+
+packet WriteInquiryScanTypeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_SCAN_TYPE) {
+ status : ErrorCode,
+}
+
+enum InquiryMode : 8 {
+ STANDARD = 0x00,
+ RSSI = 0x01,
+ RSSI_OR_EXTENDED = 0x02,
+}
+
+packet ReadInquiryMode : Command (op_code = READ_INQUIRY_MODE) {
+}
+
+packet ReadInquiryModeComplete : CommandComplete (command_op_code = READ_INQUIRY_MODE) {
+ status : ErrorCode,
+ inquiry_mode : InquiryMode,
+}
+
+packet WriteInquiryMode : Command (op_code = WRITE_INQUIRY_MODE) {
+ inquiry_mode : InquiryMode,
+}
+
+packet WriteInquiryModeComplete : CommandComplete (command_op_code = WRITE_INQUIRY_MODE) {
+ status : ErrorCode,
+}
+
+enum PageScanType : 8 {
+ STANDARD = 0x00,
+ INTERLACED = 0x01,
+}
+
+packet ReadPageScanType : Command (op_code = READ_PAGE_SCAN_TYPE) {
+}
+
+packet ReadPageScanTypeComplete : CommandComplete (command_op_code = READ_PAGE_SCAN_TYPE) {
+ status : ErrorCode,
+ page_scan_type : PageScanType,
+}
+
+packet WritePageScanType : Command (op_code = WRITE_PAGE_SCAN_TYPE) {
+ page_scan_type : PageScanType,
+}
+
+packet WritePageScanTypeComplete : CommandComplete (command_op_code = WRITE_PAGE_SCAN_TYPE) {
+ status : ErrorCode,
+}
+
+packet ReadAfhChannelAssessmentMode : Command (op_code = READ_AFH_CHANNEL_ASSESSMENT_MODE) {
+}
+
+packet ReadAfhChannelAssessmentModeComplete : CommandComplete (command_op_code = READ_AFH_CHANNEL_ASSESSMENT_MODE) {
+ status : ErrorCode,
+ controller_channel_assessment : Enable,
+}
+
+packet WriteAfhChannelAssessmentMode : Command (op_code = WRITE_AFH_CHANNEL_ASSESSMENT_MODE) {
+ controller_channel_assessment : Enable,
+}
+
+packet WriteAfhChannelAssessmentModeComplete : CommandComplete (command_op_code = WRITE_AFH_CHANNEL_ASSESSMENT_MODE) {
+ status : ErrorCode,
+}
+
+enum FecRequired : 8 {
+ NOT_REQUIRED = 0x00,
+ REQUIRED = 0x01,
+}
+
+packet ReadExtendedInquiryResponse : Command (op_code = READ_EXTENDED_INQUIRY_RESPONSE) {
+}
+
+packet ReadExtendedInquiryResponseComplete : CommandComplete (command_op_code = READ_EXTENDED_INQUIRY_RESPONSE) {
+ status : ErrorCode,
+ fec_required : FecRequired,
+ extended_inquiry_response : 8[240],
+}
+
+packet WriteExtendedInquiryResponse : Command (op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) {
+ fec_required : FecRequired,
+ extended_inquiry_response : 8[240],
+}
+
+packet WriteExtendedInquiryResponseComplete : CommandComplete (command_op_code = WRITE_EXTENDED_INQUIRY_RESPONSE) {
+ status : ErrorCode,
+}
+
+packet RefreshEncryptionKey : Command (op_code = REFRESH_ENCRYPTION_KEY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet RefreshEncryptionKeyStatus : CommandStatus (command_op_code = REFRESH_ENCRYPTION_KEY) {
+}
+
+packet ReadSimplePairingMode : Command (op_code = READ_SIMPLE_PAIRING_MODE) {
+}
+
+packet ReadSimplePairingModeComplete : CommandComplete (command_op_code = READ_SIMPLE_PAIRING_MODE) {
+ status : ErrorCode,
+ simple_pairing_mode : Enable,
+}
+
+packet WriteSimplePairingMode : Command (op_code = WRITE_SIMPLE_PAIRING_MODE) {
+ simple_pairing_mode : Enable,
+}
+
+test WriteSimplePairingMode {
+ "\x56\x0c\x01\x01",
+}
+
+packet WriteSimplePairingModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_MODE) {
+ status : ErrorCode,
+}
+
+test WriteSimplePairingModeComplete {
+ "\x0e\x04\x01\x56\x0c\x00",
+}
+
+packet ReadLocalOobData : Command (op_code = READ_LOCAL_OOB_DATA) {
+}
+
+packet ReadLocalOobDataComplete : CommandComplete (command_op_code = READ_LOCAL_OOB_DATA) {
+ status : ErrorCode,
+ c : 8[16],
+ r : 8[16],
+}
+
+packet ReadInquiryResponseTransmitPowerLevel : Command (op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) {
+}
+
+packet ReadInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_INQUIRY_RESPONSE_TRANSMIT_POWER_LEVEL) {
+ status : ErrorCode,
+ tx_power : 8, // (-70dBm to 20dBm)
+}
+
+packet WriteInquiryTransmitPowerLevel : Command (op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) {
+ tx_power : 8,
+}
+
+packet WriteInquiryResponseTransmitPowerLevelComplete : CommandComplete (command_op_code = WRITE_INQUIRY_TRANSMIT_POWER_LEVEL) {
+ status : ErrorCode,
+}
+
+enum KeypressNotificationType : 8 {
+ ENTRY_STARTED = 0,
+ DIGIT_ENTERED = 1,
+ DIGIT_ERASED = 2,
+ CLEARED = 3,
+ ENTRY_COMPLETED = 4,
+}
+
+packet SendKeypressNotification : Command (op_code = SEND_KEYPRESS_NOTIFICATION) {
+ bd_addr : Address,
+ notification_type : KeypressNotificationType,
+}
+
+packet SendKeypressNotificationComplete : CommandComplete (command_op_code = SEND_KEYPRESS_NOTIFICATION) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet SetEventMaskPage2 : Command (op_code = SET_EVENT_MASK_PAGE_2) {
+ event_mask_page_2: 64,
+}
+
+packet SetEventMaskPage2Complete : CommandComplete (command_op_code = SET_EVENT_MASK_PAGE_2) {
+ status: ErrorCode,
+}
+
+packet ReadEnhancedTransmitPowerLevel : Command (op_code = READ_ENHANCED_TRANSMIT_POWER_LEVEL) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ transmit_power_level_type : TransmitPowerLevelType,
+}
+
+packet ReadEnhancedTransmitPowerLevelComplete : CommandComplete (command_op_code = READ_ENHANCED_TRANSMIT_POWER_LEVEL) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ tx_power_level_gfsk : 8,
+ tx_power_level_dqpsk : 8,
+ tx_power_level_8dpsk : 8,
+}
+
+packet ReadLeHostSupport : Command (op_code = READ_LE_HOST_SUPPORT) {
+}
+
+packet ReadLeHostSupportComplete : CommandComplete (command_op_code = READ_LE_HOST_SUPPORT) {
+ status : ErrorCode,
+ le_supported_host : Enable,
+ _reserved_ : 8, // simultaneous_le_host reserved since 4.1
+}
+
+packet WriteLeHostSupport : Command (op_code = WRITE_LE_HOST_SUPPORT) {
+ le_supported_host : Enable,
+ simultaneous_le_host : Enable, // According to the spec, this should be 0x00 since 4.1
+}
+
+test WriteLeHostSupport {
+ "\x6d\x0c\x02\x01\x01",
+}
+
+packet WriteLeHostSupportComplete : CommandComplete (command_op_code = WRITE_LE_HOST_SUPPORT) {
+ status : ErrorCode,
+}
+
+test WriteLeHostSupportComplete {
+ "\x0e\x04\x01\x6d\x0c\x00",
+}
+
+packet ReadSecureConnectionsHostSupport : Command (op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) {
+}
+
+packet ReadSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = READ_SECURE_CONNECTIONS_HOST_SUPPORT) {
+ status : ErrorCode,
+ secure_connections_host_support : Enable,
+}
+
+packet WriteSecureConnectionsHostSupport : Command (op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
+ secure_connections_host_support : Enable,
+}
+
+test WriteSecureConnectionsHostSupport {
+ "\x7a\x0c\x01\x01",
+}
+
+packet WriteSecureConnectionsHostSupportComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_HOST_SUPPORT) {
+ status : ErrorCode,
+}
+
+test WriteSecureConnectionsHostSupportComplete {
+ "\x0e\x04\x01\x7a\x0c\x00",
+}
+
+packet ReadLocalOobExtendedData : Command (op_code = READ_LOCAL_OOB_EXTENDED_DATA) {
+}
+
+packet ReadLocalOobExtendedDataComplete : CommandComplete (command_op_code = READ_LOCAL_OOB_EXTENDED_DATA) {
+ status : ErrorCode,
+ c_192 : 8[16],
+ r_192 : 8[16],
+ c_256 : 8[16],
+ r_256 : 8[16],
+}
+
+packet SetEcosystemBaseInterval : Command (op_code = SET_ECOSYSTEM_BASE_INTERVAL) {
+ interval : 16,
+}
+
+packet SetEcosystemBaseIntervalComplete : CommandComplete (command_op_code = SET_ECOSYSTEM_BASE_INTERVAL) {
+ status : ErrorCode,
+}
+
+enum DataPathDirection : 8 {
+ INPUT = 0,
+ OUTPUT = 1,
+}
+
+packet ConfigureDataPath : Command (op_code = CONFIGURE_DATA_PATH) {
+ data_path_direction : DataPathDirection,
+ data_path_id : 8,
+ _size_(vendor_specific_config) : 8,
+ vendor_specific_config : 8[],
+}
+
+packet ConfigureDataPathComplete : CommandComplete (command_op_code = CONFIGURE_DATA_PATH) {
+ status : ErrorCode,
+}
+
+packet SetMinEncryptionKeySize : Command (op_code = SET_MIN_ENCRYPTION_KEY_SIZE) {
+ min_encryption_key_size : 8,
+}
+
+packet SetMinEncryptionKeySizeComplete : CommandComplete (command_op_code = SET_MIN_ENCRYPTION_KEY_SIZE) {
+ status : ErrorCode,
+}
+
+
+ // INFORMATIONAL_PARAMETERS
+packet ReadLocalVersionInformation : Command (op_code = READ_LOCAL_VERSION_INFORMATION) {
+}
+
+test ReadLocalVersionInformation {
+ "\x01\x10\x00",
+}
+
+enum HciVersion : 8 {
+ V_1_0B = 0x00,
+ V_1_1 = 0x01,
+ V_1_2 = 0x02,
+ V_2_0 = 0x03, // + EDR
+ V_2_1 = 0x04, // + EDR
+ V_3_0 = 0x05, // + HS
+ V_4_0 = 0x06,
+ V_4_1 = 0x07,
+ V_4_2 = 0x08,
+ V_5_0 = 0x09,
+ V_5_1 = 0x0a,
+ V_5_2 = 0x0b,
+ V_5_3 = 0x0c,
+ V_5_4 = 0x0d,
+}
+
+enum LmpVersion : 8 {
+ V_1_0B = 0x00, // withdrawn
+ V_1_1 = 0x01, // withdrawn
+ V_1_2 = 0x02, // withdrawn
+ V_2_0 = 0x03, // + EDR
+ V_2_1 = 0x04, // + EDR
+ V_3_0 = 0x05, // + HS
+ V_4_0 = 0x06,
+ V_4_1 = 0x07,
+ V_4_2 = 0x08,
+ V_5_0 = 0x09,
+ V_5_1 = 0x0a,
+ V_5_2 = 0x0b,
+ V_5_3 = 0x0c,
+ V_5_4 = 0x0d,
+}
+
+struct LocalVersionInformation {
+ hci_version : HciVersion,
+ hci_revision : 16,
+ lmp_version : LmpVersion,
+ manufacturer_name : 16,
+ lmp_subversion : 16,
+}
+
+packet ReadLocalVersionInformationComplete : CommandComplete (command_op_code = READ_LOCAL_VERSION_INFORMATION) {
+ status : ErrorCode,
+ local_version_information : LocalVersionInformation,
+}
+
+test ReadLocalVersionInformationComplete {
+ "\x0e\x0c\x01\x01\x10\x00\x09\x00\x00\x09\x1d\x00\xbe\x02",
+}
+
+packet ReadLocalSupportedCommands : Command (op_code = READ_LOCAL_SUPPORTED_COMMANDS) {
+}
+
+test ReadLocalSupportedCommands {
+ "\x02\x10\x00",
+}
+
+packet ReadLocalSupportedCommandsComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_COMMANDS) {
+ status : ErrorCode,
+ supported_commands : 8[64],
+}
+
+test ReadLocalSupportedCommandsComplete {
+ "\x0e\x44\x01\x02\x10\x00\xff\xff\xff\x03\xce\xff\xef\xff\xff\xff\xff\x7f\xf2\x0f\xe8\xfe\x3f\xf7\x83\xff\x1c\x00\x00\x00\x61\xff\xff\xff\x7f\xbe\x20\xf5\xff\xf0\xff\xff\xff\xff\xff\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+}
+
+packet ReadLocalSupportedFeatures : Command (op_code = READ_LOCAL_SUPPORTED_FEATURES) {
+}
+
+packet ReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_FEATURES) {
+ status : ErrorCode,
+ lmp_features : 64,
+}
+
+packet ReadLocalExtendedFeatures : Command (op_code = READ_LOCAL_EXTENDED_FEATURES) {
+ page_number : 8,
+}
+
+test ReadLocalExtendedFeatures {
+ "\x04\x10\x01\x00",
+ "\x04\x10\x01\x01",
+ "\x04\x10\x01\x02",
+}
+
+enum LMPFeaturesPage0Bits: 64 {
+ // Byte 0
+ LMP_3_SLOT_PACKETS = 0x01,
+ LMP_5_SLOT_PACKETS = 0x02,
+ ENCRYPTION = 0x04,
+ SLOT_OFFSET = 0x08,
+ TIMING_ACCURACY = 0x10,
+ ROLE_SWITCH = 0x20,
+ HOLD_MODE = 0x40,
+ SNIFF_MODE = 0x80,
+
+ // Byte 1
+ POWER_CONTROL_REQUESTS = 0x0200,
+ CHANNEL_QUALITY_DRIVEN_DATA_RATE = 0x0400,
+ SCO_LINK = 0x0800,
+ HV2_PACKETS = 0x1000,
+ HV3_PACKETS = 0x2000,
+ M_LAW_LOG_SYNCHRONOUS_DATA = 0x4000,
+ A_LAW_LOG_SYNCHRONOUS_DATA = 0x8000,
+
+ // Byte 2
+ CVSD_SYNCHRONOUS_DATA = 0x010000,
+ PAGING_PARAMETER_NEGOTIATION = 0x020000,
+ POWER_CONTROL = 0x040000,
+ TRANSPARENT_SYNCHRONOUS_DATA = 0x080000,
+ FLOW_CONTROL_LAG_LEAST_SIGNIFICANT_BIT = 0x100000,
+ FLOW_CONTROL_LAG_MIDDLE_BIT = 0x200000,
+ FLOW_CONTROL_LAG_MOST_SIGNIFICANT_BIT = 0x400000,
+ BROADCAST_ENCRYPTION = 0x800000,
+
+ // Byte 3
+ ENHANCED_DATA_RATE_ACL_2_MB_S_MODE = 0x02000000,
+ ENHANCED_DATA_RATE_ACL_3_MB_S_MODE = 0x04000000,
+ ENHANCED_INQUIRY_SCAN = 0x08000000,
+ INTERLACED_INQUIRY_SCAN = 0x10000000,
+ INTERLACED_PAGE_SCAN = 0x20000000,
+ RSSI_WITH_INQUIRY_RESULTS = 0x40000000,
+ EXTENDED_SCO_LINK = 0x80000000,
+
+ // Byte 4
+ EV4_PACKETS = 0x0100000000,
+ EV5_PACKETS = 0x0200000000,
+ AFH_CAPABLE_PERIPHERAL = 0x0800000000,
+ AFH_CLASSIFICATION_PERIPHERAL = 0x1000000000,
+ BR_EDR_NOT_SUPPORTED = 0x2000000000,
+ LE_SUPPORTED_CONTROLLER = 0x4000000000,
+ LMP_3_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS = 0x8000000000,
+
+ // Byte 5
+ LMP_5_SLOT_ENHANCED_DATA_RATE_ACL_PACKETS = 0x010000000000,
+ SNIFF_SUBRATING = 0x020000000000,
+ PAUSE_ENCRYPTION = 0x040000000000,
+ AFH_CAPABLE_CENTRAL = 0x080000000000,
+ AFH_CLASSIFICATION_CENTRAL = 0x100000000000,
+ ENHANCED_DATA_RATE_ESCO_2_MB_S_MODE = 0x200000000000,
+ ENHANCED_DATA_RATE_ESCO_3_MB_S_MODE = 0x400000000000,
+ LMP_3_SLOT_ENHANCED_DATA_RATE_ESCO_PACKETS = 0x800000000000,
+
+ // Byte 6
+ EXTENDED_INQUIRY_RESPONSE = 0x01000000000000,
+ SIMULTANEOUS_LE_AND_BR_CONTROLLER = 0x02000000000000,
+ SECURE_SIMPLE_PAIRING_CONTROLLER = 0x08000000000000,
+ ENCAPSULATED_PDU = 0x10000000000000,
+ ERRONEOUS_DATA_REPORTING = 0x20000000000000,
+ NON_FLUSHABLE_PACKET_BOUNDARY_FLAG = 0x40000000000000,
+
+ // Byte 7
+ HCI_LINK_SUPERVISION_TIMEOUT_CHANGED_EVENT = 0x0100000000000000,
+ VARIABLE_INQUIRY_TX_POWER_LEVEL = 0x0200000000000000,
+ ENHANCED_POWER_CONTROL = 0x0400000000000000,
+ EXTENDED_FEATURES = 0x8000000000000000,
+}
+
+enum LMPFeaturesPage1Bits: 64 {
+ // Byte 0
+ SECURE_SIMPLE_PAIRING_HOST_SUPPORT = 0x01,
+ LE_SUPPORTED_HOST = 0x02,
+ SIMULTANEOUS_LE_AND_BR_HOST = 0x04,
+ SECURE_CONNECTIONS_HOST_SUPPORT = 0x08,
+}
+
+enum LMPFeaturesPage2Bits: 64 {
+ // Byte 0
+ CONNECTIONLESS_PERIPHERAL_BROADCAST_TRANSMITTER_OPERATION = 0x01,
+ CONNECTIONLESS_PERIPHERAL_BROADCAST_RECEIVER_OPERATION = 0x02,
+ SYNCHRONIZATION_TRAIN = 0x04,
+ SYNCHRONIZATION_SCAN = 0x08,
+ HCI_INQUIRY_RESPONSE_NOTIFICATION_EVENT = 0x10,
+ GENERALIZED_INTERLACED_SCAN = 0x20,
+ COARSE_CLOCK_ADJUSTMENT = 0x40,
+
+ // Byte 1
+ SECURE_CONNECTIONS_CONTROLLER_SUPPORT = 0x0100,
+ PING = 0x0200,
+ SLOT_AVAILABILITY_MASK = 0x0400,
+ TRAIN_NUDGING = 0x0800,
+}
+
+packet ReadLocalExtendedFeaturesComplete : CommandComplete (command_op_code = READ_LOCAL_EXTENDED_FEATURES) {
+ status : ErrorCode,
+ page_number : 8,
+ maximum_page_number : 8,
+ extended_lmp_features : 64,
+}
+
+test ReadLocalExtendedFeaturesComplete {
+ "\x0e\x0e\x01\x04\x10\x00\x00\x02\xff\xfe\x8f\xfe\xd8\x3f\x5b\x87",
+ "\x0e\x0e\x01\x04\x10\x00\x01\x02\x07\x00\x00\x00\x00\x00\x00\x00",
+ "\x0e\x0e\x01\x04\x10\x00\x02\x02\x45\x03\x00\x00\x00\x00\x00\x00",
+}
+
+packet ReadBufferSize : Command (op_code = READ_BUFFER_SIZE) {
+}
+
+test ReadBufferSize {
+ "\x05\x10\x00",
+}
+
+packet ReadBufferSizeComplete : CommandComplete (command_op_code = READ_BUFFER_SIZE) {
+ status : ErrorCode,
+ acl_data_packet_length : 16,
+ synchronous_data_packet_length : 8,
+ total_num_acl_data_packets : 16,
+ total_num_synchronous_data_packets : 16,
+}
+
+test ReadBufferSizeComplete {
+ "\x0e\x0b\x01\x05\x10\x00\x00\x04\x3c\x07\x00\x08\x00",
+}
+
+packet ReadBdAddr : Command (op_code = READ_BD_ADDR) {
+}
+
+test ReadBdAddr {
+ "\x09\x10\x00",
+}
+
+packet ReadBdAddrComplete : CommandComplete (command_op_code = READ_BD_ADDR) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+test ReadBdAddrComplete {
+ "\x0e\x0a\x01\x09\x10\x00\x14\x8e\x61\x5f\x36\x88",
+}
+
+packet ReadDataBlockSize : Command (op_code = READ_DATA_BLOCK_SIZE) {
+}
+
+packet ReadDataBlockSizeComplete : CommandComplete (command_op_code = READ_DATA_BLOCK_SIZE) {
+ status : ErrorCode,
+ max_acl_data_packet_length : 16,
+ data_block_length : 16,
+ total_num_data_blocks : 16,
+}
+
+packet ReadLocalSupportedCodecsV1 : Command (op_code = READ_LOCAL_SUPPORTED_CODECS_V1) {
+}
+
+packet ReadLocalSupportedCodecsV1Complete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS_V1) {
+ status : ErrorCode,
+ _count_(supported_codecs) : 8,
+ supported_codecs : 8[],
+ _count_(vendor_specific_codecs) : 8,
+ vendor_specific_codecs : 32[],
+}
+
+packet ReadLocalSupportedCodecsV2 : Command (op_code = READ_LOCAL_SUPPORTED_CODECS_V2) {
+}
+
+group CodecTransport {
+ br_edr : 1,
+ br_edr_sco_and_esco : 1,
+ le_cis : 1,
+ le_bis : 1,
+ _reserved_ : 4,
+}
+
+struct CodecConfiguration {
+ codec_id : 8,
+ CodecTransport,
+}
+
+struct VendorCodecConfiguration {
+ company_id : 16,
+ codec_vendor_id : 16,
+ CodecTransport,
+}
+
+packet ReadLocalSupportedCodecsV2Complete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODECS_V2) {
+ status : ErrorCode,
+ _count_(supported_codecs) : 8,
+ supported_codecs : CodecConfiguration[],
+ _count_(vendor_specific_codecs) : 8,
+ vendor_specific_codecs : VendorCodecConfiguration[],
+}
+
+packet ReadLocalSupportedCodecCapabilities : Command (op_code = READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES) {
+ codec_id : 8,
+ company_id : 16,
+ codec_vendor_id : 16,
+ CodecTransport,
+ direction : DataPathDirection,
+}
+
+struct CodecCapability {
+ _size_(capability) : 8,
+ capability : 8[],
+}
+
+packet ReadLocalSupportedCodecCapabilitiesComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CODEC_CAPABILITIES) {
+ status : ErrorCode,
+ _count_(codec_capabilities) : 8,
+ codec_capabilities : CodecCapability[],
+}
+
+packet ReadLocalSupportedControllerDelay : Command (op_code = READ_LOCAL_SUPPORTED_CONTROLLER_DELAY) {
+ codec_id : 8,
+ company_id : 16,
+ codec_vendor_id : 16,
+ CodecTransport,
+ direction : DataPathDirection,
+ _size_(codec_configuration) : 8,
+ codec_configuration : 8[],
+}
+
+packet ReadLocalSupportedControllerDelayComplete : CommandComplete (command_op_code = READ_LOCAL_SUPPORTED_CONTROLLER_DELAY) {
+ status : ErrorCode,
+ min_controller_delay : 24,
+ max_controller_delay : 24,
+}
+
+
+ // STATUS_PARAMETERS
+packet ReadFailedContactCounter : Command (op_code = READ_FAILED_CONTACT_COUNTER) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadFailedContactCounterComplete : CommandComplete (command_op_code = READ_FAILED_CONTACT_COUNTER) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ failed_contact_counter : 16,
+}
+
+packet ResetFailedContactCounter : Command (op_code = RESET_FAILED_CONTACT_COUNTER) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ResetFailedContactCounterComplete : CommandComplete (command_op_code = RESET_FAILED_CONTACT_COUNTER) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadLinkQuality : Command (op_code = READ_LINK_QUALITY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadLinkQualityComplete : CommandComplete (command_op_code = READ_LINK_QUALITY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ link_quality : 8,
+}
+
+packet ReadRssi : Command (op_code = READ_RSSI) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadRssiComplete : CommandComplete (command_op_code = READ_RSSI) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ rssi : 8,
+}
+
+packet ReadAfhChannelMap : Command (op_code = READ_AFH_CHANNEL_MAP) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum AfhMode : 8 {
+ AFH_DISABLED = 0x00,
+ AFH_ENABLED = 0x01,
+}
+
+packet ReadAfhChannelMapComplete : CommandComplete (command_op_code = READ_AFH_CHANNEL_MAP) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ afh_mode : AfhMode,
+ afh_channel_map : 8[10],
+}
+
+
+enum WhichClock : 8 {
+ LOCAL = 0x00,
+ PICONET = 0x01,
+}
+
+packet ReadClock : Command (op_code = READ_CLOCK) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ which_clock : WhichClock,
+}
+
+packet ReadClockComplete : CommandComplete (command_op_code = READ_CLOCK) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ clock : 28,
+ _reserved_ : 4,
+ accuracy : 16,
+}
+
+packet ReadEncryptionKeySize : Command (op_code = READ_ENCRYPTION_KEY_SIZE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet ReadEncryptionKeySizeComplete : CommandComplete (command_op_code = READ_ENCRYPTION_KEY_SIZE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ key_size : 8,
+}
+
+ // TESTING
+enum LoopbackMode : 8 {
+ NO_LOOPBACK = 0x00,
+ ENABLE_LOCAL = 0x01,
+ ENABLE_REMOTE = 0x02,
+}
+
+packet ReadLoopbackMode : Command (op_code = READ_LOOPBACK_MODE) {
+}
+
+packet ReadLoopbackModeComplete : CommandComplete (command_op_code = READ_LOOPBACK_MODE) {
+ status : ErrorCode,
+ loopback_mode : LoopbackMode,
+}
+
+packet WriteLoopbackMode : Command (op_code = WRITE_LOOPBACK_MODE) {
+ loopback_mode : LoopbackMode,
+}
+
+packet WriteLoopbackModeComplete : CommandComplete (command_op_code = WRITE_LOOPBACK_MODE) {
+ status : ErrorCode,
+}
+
+packet EnableDeviceUnderTestMode : Command (op_code = ENABLE_DEVICE_UNDER_TEST_MODE) {
+}
+
+packet EnableDeviceUnderTestModeComplete : CommandComplete (command_op_code = ENABLE_DEVICE_UNDER_TEST_MODE) {
+ status : ErrorCode,
+}
+
+packet WriteSimplePairingDebugMode : Command (op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
+ simple_pairing_debug_mode : Enable,
+}
+
+packet WriteSimplePairingDebugModeComplete : CommandComplete (command_op_code = WRITE_SIMPLE_PAIRING_DEBUG_MODE) {
+ status : ErrorCode,
+}
+
+packet WriteSecureConnectionsTestMode : Command (op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ dm1_aclu_mode : Enable,
+ esco_loopback_mode : Enable,
+}
+
+packet WriteSecureConnectionsTestModeComplete : CommandComplete (command_op_code = WRITE_SECURE_CONNECTIONS_TEST_MODE) {
+ status : ErrorCode,
+}
+
+ // LE_CONTROLLER
+packet LeSetEventMask : Command (op_code = LE_SET_EVENT_MASK) {
+ le_event_mask : 64,
+}
+
+packet LeSetEventMaskComplete : CommandComplete (command_op_code = LE_SET_EVENT_MASK) {
+ status : ErrorCode,
+}
+
+packet LeReadBufferSizeV1 : Command (op_code = LE_READ_BUFFER_SIZE_V1) {
+}
+
+struct LeBufferSize {
+ le_data_packet_length : 16,
+ total_num_le_packets : 8,
+}
+
+test LeReadBufferSizeV1 {
+ "\x02\x20\x00",
+}
+
+packet LeReadBufferSizeV1Complete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE_V1) {
+ status : ErrorCode,
+ le_buffer_size : LeBufferSize,
+}
+
+test LeReadBufferSizeV1Complete {
+ "\x0e\x07\x01\x02\x20\x00\xfb\x00\x10",
+}
+
+enum LLFeaturesBits : 64 {
+ // Byte 0
+ LE_ENCRYPTION = 0x0000000000000001,
+ CONNECTION_PARAMETERS_REQUEST_PROCEDURE = 0x0000000000000002,
+ EXTENDED_REJECT_INDICATION = 0x0000000000000004,
+ PERIPHERAL_INITIATED_FEATURES_EXCHANGE = 0x0000000000000008,
+ LE_PING = 0x0000000000000010,
+ LE_DATA_PACKET_LENGTH_EXTENSION = 0x0000000000000020,
+ LL_PRIVACY = 0x0000000000000040,
+ EXTENDED_SCANNER_FILTER_POLICIES = 0x0000000000000080,
+
+ // Byte 1
+ LE_2M_PHY = 0x0000000000000100,
+ STABLE_MODULATION_INDEX_TRANSMITTER = 0x0000000000000200,
+ STABLE_MODULATION_INDEX_RECEIVER = 0x0000000000000400,
+ LE_CODED_PHY = 0x0000000000000800,
+ LE_EXTENDED_ADVERTISING = 0x0000000000001000,
+ LE_PERIODIC_ADVERTISING = 0x0000000000002000,
+ CHANNEL_SELECTION_ALGORITHM_2 = 0x0000000000004000,
+ LE_POWER_CLASS_1 = 0x0000000000008000,
+
+ // Byte 2
+ MINIMUM_NUMBER_OF_USED_CHANNELS_PROCEDURE = 0x0000000000010000,
+ CONNECTION_CTE_REQUEST = 0x0000000000020000,
+ CONNECTION_CTE_RESPONSE = 0x0000000000040000,
+ CONNECTIONLESS_CTE_TRANSMITTER = 0x0000000000080000,
+ CONNECTIONLESS_CTE_RECEIVER = 0x0000000000100000,
+ ANTENNA_SWITCHING_DURING_CTE_TRANSMISSION = 0x0000000000200000,
+ ANTENNA_SWITCHING_DURING_CTE_RECEPTION = 0x0000000000400000,
+ RECEIVING_CONSTANT_TONE_EXTENSIONS = 0x0000000000800000,
+
+ // Byte 3
+ PERIODIC_ADVERTISING_SYNC_TRANSFER_SENDER = 0x0000000001000000,
+ PERIODIC_ADVERTISING_SYNC_TRANSFER_RECIPIENT = 0x0000000002000000,
+ SLEEP_CLOCK_ACCURACY_UPDATES = 0x0000000004000000,
+ REMOTE_PUBLIC_KEY_VALIDATION = 0x0000000008000000,
+ CONNECTED_ISOCHRONOUS_STREAM_CENTRAL = 0x0000000010000000,
+ CONNECTED_ISOCHRONOUS_STREAM_PERIPHERAL = 0x0000000020000000,
+ ISOCHRONOUS_BROADCASTER = 0x0000000040000000,
+ SYNCHRONIZED_RECEIVER = 0x0000000080000000,
+
+ // Byte 4
+ CONNECTED_ISOCHRONOUS_STREAM_HOST_SUPPORT = 0x0000000100000000,
+ LE_POWER_CONTROL_REQUEST = 0x0000000200000000,
+ LE_POWER_CONTROL_REQUEST_BIS = 0x0000000400000000,
+ LE_PATH_LOSS_MONITORING = 0x0000000800000000,
+ PERIODIC_ADVERTISING_ADI_SUPPORT = 0x0000001000000000,
+ CONNECTION_SUBRATING = 0x0000002000000000,
+ CONNECTION_SUBRATING_HOST_SUPPORT = 0x0000004000000000,
+ CHANNEL_CLASSIFICATION = 0x0000008000000000,
+}
+
+packet LeReadLocalSupportedFeatures : Command (op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) {
+}
+
+packet LeReadLocalSupportedFeaturesComplete : CommandComplete (command_op_code = LE_READ_LOCAL_SUPPORTED_FEATURES) {
+ status : ErrorCode,
+ le_features : 64,
+}
+
+packet LeSetRandomAddress : Command (op_code = LE_SET_RANDOM_ADDRESS) {
+ random_address : Address,
+}
+
+packet LeSetRandomAddressComplete : CommandComplete (command_op_code = LE_SET_RANDOM_ADDRESS) {
+ status : ErrorCode,
+}
+
+enum AdvertisingFilterPolicy : 2 {
+ ALL_DEVICES = 0, // Default
+ LISTED_SCAN = 1,
+ LISTED_CONNECT = 2,
+ LISTED_SCAN_AND_CONNECT = 3,
+}
+
+enum PeerAddressType : 8 {
+ PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x00,
+ RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x01,
+}
+
+enum AdvertisingType : 8 {
+ ADV_IND = 0x00,
+ ADV_DIRECT_IND_HIGH = 0x01,
+ ADV_SCAN_IND = 0x02,
+ ADV_NONCONN_IND = 0x03,
+ ADV_DIRECT_IND_LOW = 0x04,
+}
+
+enum AddressType : 8 {
+ PUBLIC_DEVICE_ADDRESS = 0x00,
+ RANDOM_DEVICE_ADDRESS = 0x01,
+ PUBLIC_IDENTITY_ADDRESS = 0x02,
+ RANDOM_IDENTITY_ADDRESS = 0x03,
+}
+
+enum OwnAddressType : 8 {
+ PUBLIC_DEVICE_ADDRESS = 0x00,
+ RANDOM_DEVICE_ADDRESS = 0x01,
+ RESOLVABLE_OR_PUBLIC_ADDRESS = 0x02,
+ RESOLVABLE_OR_RANDOM_ADDRESS = 0x03,
+}
+
+packet LeSetAdvertisingParameters : Command (op_code = LE_SET_ADVERTISING_PARAMETERS) {
+ advertising_interval_min : 16,
+ advertising_interval_max : 16,
+ advertising_type : AdvertisingType,
+ own_address_type : OwnAddressType,
+ peer_address_type : PeerAddressType,
+ peer_address : Address,
+ advertising_channel_map : 8,
+ advertising_filter_policy : AdvertisingFilterPolicy,
+ _reserved_ : 6,
+}
+
+packet LeSetAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_PARAMETERS) {
+ status : ErrorCode,
+}
+
+packet LeReadAdvertisingPhysicalChannelTxPower : Command (op_code = LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER) {
+}
+
+packet LeReadAdvertisingPhysicalChannelTxPowerComplete : CommandComplete (command_op_code = LE_READ_ADVERTISING_PHYSICAL_CHANNEL_TX_POWER) {
+ status : ErrorCode,
+ transmit_power_level : 8, // (-127dBm to 20dBm) Accuracy: +/-4dB
+}
+
+packet LeSetAdvertisingData : Command (op_code = LE_SET_ADVERTISING_DATA) {
+ _size_(advertising_data) : 8,
+ advertising_data : 8[],
+ _padding_[31], // Zero padding to 31 bytes of advertising_data
+}
+
+packet LeSetAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_DATA) {
+ status : ErrorCode,
+}
+
+packet LeSetScanResponseData : Command (op_code = LE_SET_SCAN_RESPONSE_DATA) {
+ _size_(advertising_data) : 8,
+ advertising_data : 8[],
+ _padding_[31], // Zero padding to 31 bytes of advertising_data
+}
+
+packet LeSetScanResponseDataComplete : CommandComplete (command_op_code = LE_SET_SCAN_RESPONSE_DATA) {
+ status : ErrorCode,
+}
+
+packet LeSetAdvertisingEnable : Command (op_code = LE_SET_ADVERTISING_ENABLE) {
+ advertising_enable : Enable, // Default DISABLED
+}
+
+packet LeSetAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_ENABLE) {
+ status : ErrorCode,
+}
+
+enum LeScanType : 8 {
+ PASSIVE = 0x00, // Default
+ ACTIVE = 0x01,
+}
+
+enum LeScanningFilterPolicy : 8 {
+ ACCEPT_ALL = 0x00, // Default
+ FILTER_ACCEPT_LIST_ONLY = 0x01,
+ CHECK_INITIATORS_IDENTITY = 0x02,
+ FILTER_ACCEPT_LIST_AND_INITIATORS_IDENTITY = 0x03,
+}
+
+packet LeSetScanParameters : Command (op_code = LE_SET_SCAN_PARAMETERS) {
+ le_scan_type : LeScanType,
+ le_scan_interval : 16, // 0x0004-0x4000 Default 0x10 (10ms)
+ le_scan_window : 16, // Default 0x10 (10ms)
+ own_address_type : OwnAddressType,
+ scanning_filter_policy : LeScanningFilterPolicy,
+}
+
+test LeSetScanParameters {
+ "\x0b\x20\x07\x01\x12\x00\x12\x00\x01\x00",
+}
+
+packet LeSetScanParametersComplete : CommandComplete (command_op_code = LE_SET_SCAN_PARAMETERS) {
+ status : ErrorCode,
+}
+
+packet LeSetScanEnable : Command (op_code = LE_SET_SCAN_ENABLE) {
+ le_scan_enable : Enable,
+ filter_duplicates : Enable,
+}
+
+test LeSetScanEnable {
+ "\x0c\x20\x02\x01\x00",
+}
+
+packet LeSetScanEnableComplete : CommandComplete (command_op_code = LE_SET_SCAN_ENABLE) {
+ status : ErrorCode,
+}
+
+enum InitiatorFilterPolicy : 8 {
+ USE_PEER_ADDRESS = 0x00,
+ USE_FILTER_ACCEPT_LIST = 0x01,
+}
+
+packet LeCreateConnection : Command (op_code = LE_CREATE_CONNECTION) {
+ le_scan_interval : 16, // 0x0004-0x4000
+ le_scan_window : 16, // < = LeScanInterval
+ initiator_filter_policy : InitiatorFilterPolicy,
+ peer_address_type : AddressType,
+ peer_address : Address,
+ own_address_type : OwnAddressType,
+ connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ max_latency : 16, // 0x0006-0x01F3
+ supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+ min_ce_length : 16, // 0.625ms
+ max_ce_length : 16, // 0.625ms
+}
+
+packet LeCreateConnectionStatus : CommandStatus (command_op_code = LE_CREATE_CONNECTION) {
+}
+
+packet LeCreateConnectionCancel : Command (op_code = LE_CREATE_CONNECTION_CANCEL) {
+}
+
+packet LeCreateConnectionCancelComplete : CommandComplete (command_op_code = LE_CREATE_CONNECTION_CANCEL) {
+ status : ErrorCode,
+}
+
+packet LeReadFilterAcceptListSize : Command (op_code = LE_READ_FILTER_ACCEPT_LIST_SIZE) {
+}
+
+test LeReadFilterAcceptListSize {
+ "\x0f\x20\x00",
+}
+
+packet LeReadFilterAcceptListSizeComplete : CommandComplete (command_op_code = LE_READ_FILTER_ACCEPT_LIST_SIZE) {
+ status : ErrorCode,
+ filter_accept_list_size : 8,
+}
+
+test LeReadFilterAcceptListSizeComplete {
+ "\x0e\x05\x01\x0f\x20\x00\x80",
+}
+
+packet LeClearFilterAcceptList : Command (op_code = LE_CLEAR_FILTER_ACCEPT_LIST) {
+}
+
+packet LeClearFilterAcceptListComplete : CommandComplete (command_op_code = LE_CLEAR_FILTER_ACCEPT_LIST) {
+ status : ErrorCode,
+}
+
+enum FilterAcceptListAddressType : 8 {
+ PUBLIC = 0x00,
+ RANDOM = 0x01,
+ ANONYMOUS_ADVERTISERS = 0xFF,
+}
+
+packet LeAddDeviceToFilterAcceptList : Command (op_code = LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST) {
+ address_type : FilterAcceptListAddressType,
+ address : Address,
+}
+
+packet LeAddDeviceToFilterAcceptListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST) {
+ status : ErrorCode,
+}
+
+packet LeRemoveDeviceFromFilterAcceptList : Command (op_code = LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST) {
+ address_type : FilterAcceptListAddressType,
+ address : Address,
+}
+
+packet LeRemoveDeviceFromFilterAcceptListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST) {
+ status : ErrorCode,
+}
+
+packet LeConnectionUpdate : Command (op_code = LE_CONNECTION_UPDATE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ max_latency : 16, // 0x0006-0x01F3
+ supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+ min_ce_length : 16, // 0.625ms
+ max_ce_length : 16, // 0.625ms
+}
+
+packet LeConnectionUpdateStatus : CommandStatus (command_op_code = LE_CONNECTION_UPDATE) {
+}
+
+packet LeSetHostChannelClassification : Command (op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) {
+ channel_map : 8[5],
+}
+
+packet LeSetHostChannelClassificationComplete : CommandComplete (command_op_code = LE_SET_HOST_CHANNEL_CLASSIFICATION) {
+ status : ErrorCode,
+}
+
+packet LeReadChannelMap : Command (op_code = LE_READ_CHANNEL_MAP) {
+}
+
+packet LeReadChannelMapComplete : CommandComplete (command_op_code = LE_READ_CHANNEL_MAP) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ channel_map : 8[5],
+}
+
+packet LeReadRemoteFeatures : Command (op_code = LE_READ_REMOTE_FEATURES) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeReadRemoteFeaturesStatus : CommandStatus (command_op_code = LE_READ_REMOTE_FEATURES) {
+}
+
+packet LeEncrypt : Command (op_code = LE_ENCRYPT) {
+ key : 8[16],
+ plaintext_data : 8[16],
+}
+
+packet LeEncryptComplete : CommandComplete (command_op_code = LE_ENCRYPT) {
+ status : ErrorCode,
+ encrypted_data : 8[16],
+}
+
+packet LeRand : Command (op_code = LE_RAND) {
+}
+
+packet LeRandComplete : CommandComplete (command_op_code = LE_RAND) {
+ status : ErrorCode,
+ random_number : 64,
+}
+
+packet LeStartEncryption : Command (op_code = LE_START_ENCRYPTION) {
+ connection_handle: 16,
+ rand: 8[8],
+ ediv: 16,
+ ltk: 8[16],
+}
+
+packet LeStartEncryptionStatus : CommandStatus (command_op_code = LE_START_ENCRYPTION) {
+}
+
+packet LeLongTermKeyRequestReply : Command (op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) {
+ connection_handle: 16,
+ long_term_key: 8[16],
+}
+
+packet LeLongTermKeyRequestReplyComplete : CommandComplete (command_op_code = LE_LONG_TERM_KEY_REQUEST_REPLY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeLongTermKeyRequestNegativeReply : Command (op_code = LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeLongTermKeyRequestNegativeReplyComplete : CommandComplete (command_op_code = LE_LONG_TERM_KEY_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeReadSupportedStates : Command (op_code = LE_READ_SUPPORTED_STATES) {
+}
+
+packet LeReadSupportedStatesComplete : CommandComplete (command_op_code = LE_READ_SUPPORTED_STATES) {
+ status : ErrorCode,
+ le_states : 64,
+}
+
+packet LeReceiverTest : Command (op_code = LE_RECEIVER_TEST_V1) {
+ rx_channel : 8,
+}
+
+packet LeReceiverTestComplete : CommandComplete (command_op_code = LE_RECEIVER_TEST_V1) {
+ status : ErrorCode,
+}
+
+enum LeTestPayload : 8 {
+ PRBS9 = 0x00,
+ REPEATED_F0 = 0x01,
+ REPEATED_AA = 0x02,
+ PRBS15 = 0x03,
+ REPEATED_FF = 0x04,
+ REPEATED_00 = 0x05,
+ REPEATED_0F = 0x06,
+ REPEATED_55 = 0x07,
+}
+
+packet LeTransmitterTest : Command (op_code = LE_TRANSMITTER_TEST_V1) {
+ tx_channel : 8,
+ test_data_length : 8,
+ packet_payload : LeTestPayload,
+}
+
+packet LeTransmitterTestComplete : CommandComplete (command_op_code = LE_TRANSMITTER_TEST_V1) {
+ status : ErrorCode,
+}
+
+packet LeTestEnd : Command (op_code = LE_TEST_END) {
+}
+
+packet LeTestEndComplete : CommandComplete (command_op_code = LE_TEST_END) {
+ status : ErrorCode,
+}
+
+packet LeRemoteConnectionParameterRequestReply : Command (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ latency : 16, // 0x0006-0x01F3
+ timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+ minimum_ce_length : 16, // 0.625ms
+ maximum_ce_length : 16, // 0.625ms
+}
+
+packet LeRemoteConnectionParameterRequestReplyComplete : CommandComplete (command_op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_REPLY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeRemoteConnectionParameterRequestNegativeReply : Command (op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ reason : ErrorCode,
+}
+
+packet LeRemoteConnectionParameterRequestNegativeReplyComplete : CommandComplete (command_op_code = LE_REMOTE_CONNECTION_PARAMETER_REQUEST_NEGATIVE_REPLY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeSetDataLength : Command (op_code = LE_SET_DATA_LENGTH) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB
+ tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290
+}
+
+packet LeSetDataLengthComplete : CommandComplete (command_op_code = LE_SET_DATA_LENGTH) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeReadSuggestedDefaultDataLength : Command (op_code = LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) {
+}
+
+packet LeReadSuggestedDefaultDataLengthComplete : CommandComplete (command_op_code = LE_READ_SUGGESTED_DEFAULT_DATA_LENGTH) {
+ status : ErrorCode,
+ tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB
+ tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290
+}
+
+packet LeWriteSuggestedDefaultDataLength : Command (op_code = LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) {
+ tx_octets : 16, // payload octets per single PDU 0x1B to 0x00FB
+ tx_time : 16, // microseconds used to transmit a single PDU 0x0148 to 0x4290
+}
+
+packet LeWriteSuggestedDefaultDataLengthComplete : CommandComplete (command_op_code = LE_WRITE_SUGGESTED_DEFAULT_DATA_LENGTH) {
+ status : ErrorCode,
+}
+
+packet LeReadLocalP256PublicKeyCommand : Command (op_code = LE_READ_LOCAL_P_256_PUBLIC_KEY) {
+}
+
+packet LeReadLocalP256PublicKeyCommandStatus : CommandStatus (command_op_code = LE_READ_LOCAL_P_256_PUBLIC_KEY) {
+}
+
+packet LeGenerateDhkeyV1Command : Command (op_code = LE_GENERATE_DHKEY_V1) {
+ remote_p_256_public_key : 8[64],
+}
+
+packet LeGenerateDhkeyV1CommandStatus : CommandStatus (command_op_code = LE_GENERATE_DHKEY_V1) {
+}
+
+packet LeAddDeviceToResolvingList : Command (op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) {
+ peer_identity_address_type : PeerAddressType,
+ peer_identity_address : Address,
+ peer_irk : 8[16],
+ local_irk : 8[16],
+}
+
+packet LeAddDeviceToResolvingListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_RESOLVING_LIST) {
+ status : ErrorCode,
+}
+
+packet LeRemoveDeviceFromResolvingList : Command (op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) {
+ peer_identity_address_type : PeerAddressType,
+ peer_identity_address : Address,
+}
+
+packet LeRemoveDeviceFromResolvingListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_RESOLVING_LIST) {
+ status : ErrorCode,
+}
+
+packet LeClearResolvingList : Command (op_code = LE_CLEAR_RESOLVING_LIST) {
+}
+
+packet LeClearResolvingListComplete : CommandComplete (command_op_code = LE_CLEAR_RESOLVING_LIST) {
+ status : ErrorCode,
+}
+
+packet LeReadResolvingListSize : Command (op_code = LE_READ_RESOLVING_LIST_SIZE) {
+}
+
+packet LeReadResolvingListSizeComplete : CommandComplete (command_op_code = LE_READ_RESOLVING_LIST_SIZE) {
+ status : ErrorCode,
+ resolving_list_size : 8,
+}
+
+packet LeReadPeerResolvableAddress : Command (op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) {
+ peer_identity_address_type : PeerAddressType,
+ peer_identity_address : Address,
+}
+
+packet LeReadPeerResolvableAddressComplete : CommandComplete (command_op_code = LE_READ_PEER_RESOLVABLE_ADDRESS) {
+ status : ErrorCode,
+ peer_resolvable_address : Address,
+}
+
+packet LeReadLocalResolvableAddress : Command (op_code = LE_READ_LOCAL_RESOLVABLE_ADDRESS) {
+ peer_identity_address_type : PeerAddressType,
+ peer_identity_address : Address,
+}
+
+packet LeReadLocalResolvableAddressComplete : CommandComplete (command_op_code = LE_READ_LOCAL_RESOLVABLE_ADDRESS) {
+ status : ErrorCode,
+ local_resolvable_address : Address,
+}
+
+packet LeSetAddressResolutionEnable : Command (op_code = LE_SET_ADDRESS_RESOLUTION_ENABLE) {
+ address_resolution_enable : Enable,
+}
+
+packet LeSetAddressResolutionEnableComplete : CommandComplete (command_op_code = LE_SET_ADDRESS_RESOLUTION_ENABLE) {
+ status : ErrorCode,
+}
+
+packet LeSetResolvablePrivateAddressTimeout : Command (op_code = LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) {
+ rpa_timeout : 16, // RPA_Timeout measured in seconds 0x0001 to 0xA1B8 1s to 11.5 hours
+}
+
+packet LeSetResolvablePrivateAddressTimeoutComplete : CommandComplete (command_op_code = LE_SET_RESOLVABLE_PRIVATE_ADDRESS_TIMEOUT) {
+ status : ErrorCode,
+}
+
+packet LeReadMaximumDataLength : Command (op_code = LE_READ_MAXIMUM_DATA_LENGTH) {
+}
+
+struct LeMaximumDataLength {
+ supported_max_tx_octets : 16,
+ supported_max_tx_time: 16,
+ supported_max_rx_octets : 16,
+ supported_max_rx_time: 16,
+}
+
+packet LeReadMaximumDataLengthComplete : CommandComplete (command_op_code = LE_READ_MAXIMUM_DATA_LENGTH) {
+ status : ErrorCode,
+ le_maximum_data_length : LeMaximumDataLength,
+}
+
+packet LeReadPhy : Command (op_code = LE_READ_PHY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum PhyType : 8 {
+ LE_1M = 0x01,
+ LE_2M = 0x02,
+ LE_CODED = 0x03,
+}
+
+packet LeReadPhyComplete : CommandComplete (command_op_code = LE_READ_PHY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ tx_phy : PhyType,
+ rx_phy : PhyType,
+}
+
+packet LeSetDefaultPhy : Command (op_code = LE_SET_DEFAULT_PHY) {
+ all_phys_no_transmit_preference : 1,
+ all_phys_no_receive_preference : 1,
+ _reserved_ : 6,
+ tx_phys : 3,
+ _reserved_ : 5,
+ rx_phys : 3,
+ _reserved_ : 5,
+}
+
+packet LeSetDefaultPhyComplete : CommandComplete (command_op_code = LE_SET_DEFAULT_PHY) {
+ status : ErrorCode,
+}
+
+enum PhyOptions : 8 {
+ NO_PREFERENCE = 0x00,
+ S_2 = 0x01,
+ S_8 = 0x02,
+}
+
+packet LeSetPhy : Command (op_code = LE_SET_PHY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ all_phys_no_transmit_preference : 1,
+ all_phys_no_receive_preference : 1,
+ _reserved_ : 6,
+ tx_phys : 3,
+ _reserved_ : 5,
+ rx_phys : 3,
+ _reserved_ : 5,
+ phy_options : PhyOptions,
+}
+
+packet LeSetPhyStatus : CommandStatus (command_op_code = LE_SET_PHY) {
+}
+
+enum ModulationIndex : 8 {
+ STANDARD = 0x00,
+ STABLE = 0x01,
+}
+
+packet LeEnhancedReceiverTest : Command (op_code = LE_RECEIVER_TEST_V2) {
+ rx_channel : 8,
+ phy : PhyType,
+ modulation_index : ModulationIndex,
+}
+
+packet LeEnhancedReceiverTestComplete : CommandComplete (command_op_code = LE_RECEIVER_TEST_V2) {
+ status : ErrorCode,
+}
+
+packet LeEnhancedTransmitterTest : Command (op_code = LE_TRANSMITTER_TEST_V2) {
+ tx_channel : 8,
+ test_data_length : 8,
+ packet_payload : LeTestPayload,
+ phy : PhyType,
+}
+
+packet LeEnhancedTransmitterTestComplete : CommandComplete (command_op_code = LE_TRANSMITTER_TEST_V2) {
+ status : ErrorCode,
+}
+
+packet LeSetAdvertisingSetRandomAddress : Command (op_code = LE_SET_ADVERTISING_SET_RANDOM_ADDRESS) {
+ advertising_handle : 8,
+ random_address : Address,
+}
+
+test LeSetAdvertisingSetRandomAddress {
+ "\x35\x20\x07\x00\x77\x58\xeb\xd3\x1c\x6e",
+}
+
+packet LeSetAdvertisingSetRandomAddressComplete : CommandComplete (command_op_code = LE_SET_ADVERTISING_SET_RANDOM_ADDRESS) {
+ status : ErrorCode,
+}
+
+test LeSetAdvertisingSetRandomAddressComplete {
+ "\x0e\x04\x01\x35\x20\x00",
+}
+
+// The lower 4 bits of the advertising event properties
+enum LegacyAdvertisingEventProperties : 4 {
+ ADV_IND = 0x3,
+ ADV_DIRECT_IND_LOW = 0x5,
+ ADV_DIRECT_IND_HIGH = 0xD,
+ ADV_SCAN_IND = 0x2,
+ ADV_NONCONN_IND = 0,
+}
+
+enum PrimaryPhyType : 8 {
+ LE_1M = 0x01,
+ LE_CODED = 0x03,
+}
+
+enum SecondaryPhyType : 8 {
+ NO_PACKETS = 0x00,
+ LE_1M = 0x01,
+ LE_2M = 0x02,
+ LE_CODED = 0x03,
+}
+
+packet LeSetExtendedAdvertisingParametersLegacy : Command (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+ advertising_handle : 8,
+ legacy_advertising_event_properties : LegacyAdvertisingEventProperties,
+ _fixed_ = 0x1 : 1, // legacy bit set
+ _reserved_ : 11, // advertising_event_properties reserved bits
+ primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+ primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+ primary_advertising_channel_map : 3, // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39
+ _reserved_ : 5,
+ own_address_type : OwnAddressType,
+ peer_address_type : PeerAddressType,
+ peer_address : Address,
+ advertising_filter_policy : AdvertisingFilterPolicy,
+ _reserved_ : 6,
+ advertising_tx_power : 8, // -127 to +20, 0x7F - no preference
+ _fixed_ = 0x1 : 8, // PrimaryPhyType LE_1M
+ _reserved_ : 8, // secondary_advertising_max_skip
+ _fixed_ = 0x1 : 8, // secondary_advertising_phy LE_1M
+ advertising_sid : 8, // SID subfield from the ADI field of the PDU
+ scan_request_notification_enable : Enable,
+}
+
+test LeSetExtendedAdvertisingParametersLegacy {
+ "\x36\x20\x19\x00\x13\x00\x90\x01\x00\xc2\x01\x00\x07\x01\x00\x00\x00\x00\x00\x00\x00\x00\xf9\x01\x00\x01\x01\x00",
+ "\x36\x20\x19\x01\x13\x00\x90\x01\x00\xc2\x01\x00\x07\x01\x00\x00\x00\x00\x00\x00\x00\x00\xf9\x01\x00\x01\x01\x00",
+}
+
+struct AdvertisingEventProperties {
+ connectable : 1,
+ scannable : 1,
+ directed : 1,
+ high_duty_cycle : 1,
+ legacy : 1,
+ anonymous : 1,
+ tx_power : 1,
+ _reserved_ : 9,
+}
+
+packet LeSetExtendedAdvertisingParameters : Command (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+ advertising_handle : 8,
+ advertising_event_properties : AdvertisingEventProperties,
+ primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+ primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+ primary_advertising_channel_map : 3, // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39
+ _reserved_ : 5,
+ own_address_type : OwnAddressType,
+ peer_address_type : PeerAddressType,
+ peer_address : Address,
+ advertising_filter_policy : AdvertisingFilterPolicy,
+ _reserved_ : 6,
+ advertising_tx_power : 8, // -127 to +20, 0x7F - no preference
+ primary_advertising_phy : PrimaryPhyType,
+ secondary_advertising_max_skip : 8, // 1 to 255, 0x00 - AUX_ADV_IND sent before next advertising event
+ secondary_advertising_phy : SecondaryPhyType,
+ advertising_sid : 8, // SID subfield from the ADI field of the PDU
+ scan_request_notification_enable : Enable,
+}
+
+packet LeSetExtendedAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+ status : ErrorCode,
+ selected_tx_power : 8, // -127 to +20
+}
+
+enum Operation : 3 {
+ INTERMEDIATE_FRAGMENT = 0,
+ FIRST_FRAGMENT = 1,
+ LAST_FRAGMENT = 2,
+ COMPLETE_ADVERTISEMENT = 3,
+ UNCHANGED_DATA = 4,
+}
+
+enum FragmentPreference : 1 {
+ CONTROLLER_MAY_FRAGMENT = 0,
+ CONTROLLER_SHOULD_NOT = 1,
+}
+
+packet LeSetExtendedAdvertisingData : Command (op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+ advertising_handle : 8,
+ operation : Operation,
+ _reserved_ : 5,
+ fragment_preference : FragmentPreference,
+ _reserved_ : 7,
+ _size_(advertising_data) : 8,
+ advertising_data : 8[],
+}
+
+test LeSetExtendedAdvertisingData {
+ "\x37\x20\x12\x00\x03\x01\x0e\x02\x01\x02\x0a\x09\x50\x69\x78\x65\x6c\x20\x33\x20\x58",
+}
+
+packet LeSetExtendedAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+ status : ErrorCode,
+}
+
+test LeSetExtendedAdvertisingDataComplete {
+ "\x0e\x04\x01\x37\x20\x00",
+}
+
+packet LeSetExtendedScanResponseData : Command (op_code = LE_SET_EXTENDED_SCAN_RESPONSE_DATA) {
+ advertising_handle : 8,
+ operation : Operation,
+ _reserved_ : 5,
+ fragment_preference : FragmentPreference,
+ _reserved_ : 7,
+ _size_(scan_response_data) : 8,
+ scan_response_data : 8[],
+}
+
+packet LeSetExtendedScanResponseDataComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_RESPONSE_DATA) {
+ status : ErrorCode,
+}
+
+packet LeSetExtendedAdvertisingEnableDisableAll : Command (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+ _fixed_ = 0x00 : 8, // Enable::DISABLED
+ _fixed_ = 0x00 : 8, // Disable all sets
+}
+
+struct EnabledSet {
+ advertising_handle : 8,
+ duration : 16,
+ max_extended_advertising_events : 8,
+}
+
+struct DisabledSet {
+ advertising_handle : 8,
+ _fixed_ = 0x00 : 16, // duration
+ _fixed_ = 0x00 : 8, // max_extended_advertising_events
+}
+
+packet LeSetExtendedAdvertisingDisable : Command (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+ _fixed_ = 0x00 : 8, // Enable::DISABLED
+ _count_(disabled_sets) : 8,
+ disabled_sets : DisabledSet[],
+}
+
+packet LeSetExtendedAdvertisingEnable : Command (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+ enable : Enable,
+ _count_(enabled_sets) : 8,
+ enabled_sets : EnabledSet[],
+}
+
+test LeSetExtendedAdvertisingEnable {
+ "\x39\x20\x06\x01\x01\x01\x00\x00\x00",
+}
+
+test LeSetExtendedAdvertisingDisable {
+ "\x39\x20\x06\x00\x01\x01\x00\x00\x00",
+}
+
+packet LeSetExtendedAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+ status : ErrorCode,
+}
+
+test LeSetExtendedAdvertisingEnableComplete {
+ "\x0e\x04\x01\x39\x20\x00",
+}
+
+packet LeReadMaximumAdvertisingDataLength : Command (op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) {
+}
+
+packet LeReadMaximumAdvertisingDataLengthComplete : CommandComplete (command_op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) {
+ status : ErrorCode,
+ maximum_advertising_data_length : 16,
+}
+
+packet LeReadNumberOfSupportedAdvertisingSets : Command (op_code = LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) {
+}
+
+packet LeReadNumberOfSupportedAdvertisingSetsComplete : CommandComplete (command_op_code = LE_READ_NUMBER_OF_SUPPORTED_ADVERTISING_SETS) {
+ status : ErrorCode,
+ number_supported_advertising_sets : 8,
+}
+
+packet LeRemoveAdvertisingSet : Command (op_code = LE_REMOVE_ADVERTISING_SET) {
+ advertising_handle : 8,
+}
+
+test LeRemoveAdvertisingSet {
+ "\x3c\x20\x01\x01",
+}
+
+packet LeRemoveAdvertisingSetComplete : CommandComplete (command_op_code = LE_REMOVE_ADVERTISING_SET) {
+ status : ErrorCode,
+}
+
+test LeRemoveAdvertisingSetComplete {
+ "\x0e\x04\x01\x3c\x20\x00",
+}
+
+packet LeClearAdvertisingSets : Command (op_code = LE_CLEAR_ADVERTISING_SETS) {
+}
+
+packet LeClearAdvertisingSetsComplete : CommandComplete (command_op_code = LE_CLEAR_ADVERTISING_SETS) {
+ status : ErrorCode,
+}
+
+packet LeSetPeriodicAdvertisingParameters : Command (op_code = LE_SET_PERIODIC_ADVERTISING_PARAMETERS) {
+ advertising_handle : 8,
+ periodic_advertising_interval_min : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+ periodic_advertising_interval_max : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+ _reserved_ : 6,
+ include_tx_power : 1,
+ _reserved_ : 9,
+}
+
+packet LeSetPeriodicAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_PARAMETERS) {
+ status : ErrorCode,
+}
+
+packet LeSetPeriodicAdvertisingData : Command (op_code = LE_SET_PERIODIC_ADVERTISING_DATA) {
+ advertising_handle : 8,
+ operation : Operation,
+ _reserved_ : 5,
+ _size_(advertising_data) : 8,
+ advertising_data : 8[],
+}
+
+packet LeSetPeriodicAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_DATA) {
+ status : ErrorCode,
+}
+
+packet LeSetPeriodicAdvertisingEnable : Command (op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) {
+ enable : 1,
+ include_adi: 1,
+ _reserved_: 6,
+ advertising_handle : 8,
+}
+
+packet LeSetPeriodicAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) {
+ status : ErrorCode,
+}
+
+struct ScanningPhyParameters {
+ le_scan_type : LeScanType,
+ le_scan_interval : 16, // 0x0004-0xFFFF Default 0x10 (10ms)
+ le_scan_window : 16, // 0x004-0xFFFF Default 0x10 (10ms)
+}
+
+packet LeSetExtendedScanParameters : Command (op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
+ own_address_type : OwnAddressType,
+ scanning_filter_policy : LeScanningFilterPolicy,
+ scanning_phys : 8,
+ scanning_phy_parameters : ScanningPhyParameters[],
+}
+
+test LeSetExtendedScanParameters {
+ "\x41\x20\x08\x01\x00\x01\x01\x12\x00\x12\x00",
+ "\x41\x20\x08\x01\x00\x01\x01\x99\x19\x99\x19",
+}
+
+packet LeSetExtendedScanParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_PARAMETERS) {
+ status : ErrorCode,
+}
+
+test LeSetExtendedScanParametersComplete {
+ "\x0e\x04\x01\x41\x20\x00",
+}
+
+enum FilterDuplicates : 8 {
+ DISABLED = 0,
+ ENABLED = 1,
+ RESET_EACH_PERIOD = 2,
+}
+
+packet LeSetExtendedScanEnable : Command (op_code = LE_SET_EXTENDED_SCAN_ENABLE) {
+ enable : Enable,
+ filter_duplicates : FilterDuplicates,
+ duration : 16, // 0 - Scan continuously, N * 10 ms
+ period : 16, // 0 - Scan continuously, N * 1.28 sec
+}
+
+test LeSetExtendedScanEnable {
+ "\x42\x20\x06\x01\x00\x00\x00\x00\x00",
+ "\x42\x20\x06\x00\x01\x00\x00\x00\x00",
+}
+
+packet LeSetExtendedScanEnableComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_SCAN_ENABLE) {
+ status : ErrorCode,
+}
+
+test LeSetExtendedScanEnableComplete {
+ "\x0e\x04\x01\x42\x20\x00",
+}
+
+struct InitiatingPhyParameters {
+ scan_interval : 16, // 0x0004-0xFFFF
+ scan_window : 16, // < = LeScanInterval
+ connection_interval_min : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ connection_interval_max : 16, // 0x0006-0x0C80 (7.5ms to 4s)
+ max_latency : 16, // 0x0006-0x01F3
+ supervision_timeout : 16, // 0x00A to 0x0C80 (100ms to 32s)
+ min_ce_length : 16, // 0.625ms
+ max_ce_length : 16, // 0.625ms
+}
+
+packet LeExtendedCreateConnection : Command (op_code = LE_EXTENDED_CREATE_CONNECTION) {
+ initiator_filter_policy : InitiatorFilterPolicy,
+ own_address_type : OwnAddressType,
+ peer_address_type : PeerAddressType,
+ peer_address : Address,
+ initiating_phys : 8,
+ initiating_phy_parameters : InitiatingPhyParameters[],
+}
+
+test LeExtendedCreateConnection {
+ "\x43\x20\x2a\x01\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x08\x30\x00\x18\x00\x28\x00\x00\x00\xf4\x01\x00\x00\x00\x00\x00\x08\x30\x00\x18\x00\x28\x00\x00\x00\xf4\x01\x00\x00\x00\x00",
+}
+
+packet LeExtendedCreateConnectionStatus : CommandStatus (command_op_code = LE_EXTENDED_CREATE_CONNECTION) {
+}
+
+enum PeriodicSyncCteType : 8 {
+ AVOID_AOA_CONSTANT_TONE_EXTENSION = 0x01,
+ AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_ONE_US_SLOTS = 0x02,
+ AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_TWO_US_SLOTS = 0x04,
+ AVOID_TYPE_THREE_CONSTANT_TONE_EXTENSION = 0x08,
+ AVOID_NO_CONSTANT_TONE_EXTENSION = 0x10,
+}
+
+struct PeriodicAdvertisingOptions {
+ use_periodic_advertiser_list: 1,
+ disable_reporting: 1,
+ enable_duplicate_filtering: 1,
+ _reserved_: 5,
+}
+
+enum AdvertiserAddressType : 8 {
+ PUBLIC_DEVICE_OR_IDENTITY_ADDRESS = 0x00,
+ RANDOM_DEVICE_OR_IDENTITY_ADDRESS = 0x01,
+}
+
+packet LePeriodicAdvertisingCreateSync : Command (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC) {
+ options : PeriodicAdvertisingOptions,
+ advertising_sid : 8,
+ advertiser_address_type : AdvertiserAddressType,
+ advertiser_address : Address,
+ skip : 16,
+ sync_timeout : 16,
+ sync_cte_type : 8,
+}
+
+packet LePeriodicAdvertisingCreateSyncStatus : CommandStatus (command_op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC) {
+}
+
+packet LePeriodicAdvertisingCreateSyncCancel : Command (op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL) {
+}
+
+packet LePeriodicAdvertisingCreateSyncCancelComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_CREATE_SYNC_CANCEL) {
+ status : ErrorCode,
+}
+
+packet LePeriodicAdvertisingTerminateSync : Command (op_code = LE_PERIODIC_ADVERTISING_TERMINATE_SYNC) {
+ sync_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LePeriodicAdvertisingTerminateSyncComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_TERMINATE_SYNC) {
+ status : ErrorCode,
+}
+
+packet LeAddDeviceToPeriodicAdvertiserList : Command (op_code = LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST) {
+ advertiser_address_type : AdvertiserAddressType,
+ advertiser_address : Address,
+ advertising_sid : 8,
+}
+
+packet LeAddDeviceToPeriodicAdvertiserListComplete : CommandComplete (command_op_code = LE_ADD_DEVICE_TO_PERIODIC_ADVERTISER_LIST) {
+ status : ErrorCode,
+}
+
+packet LeRemoveDeviceFromPeriodicAdvertiserList : Command (op_code = LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST) {
+ advertiser_address_type : AdvertiserAddressType,
+ advertiser_address : Address,
+ advertising_sid : 8,
+}
+
+packet LeRemoveDeviceFromPeriodicAdvertiserListComplete : CommandComplete (command_op_code = LE_REMOVE_DEVICE_FROM_PERIODIC_ADVERTISER_LIST) {
+ status : ErrorCode,
+}
+
+packet LeClearPeriodicAdvertiserList : Command (op_code = LE_CLEAR_PERIODIC_ADVERTISER_LIST) {
+}
+
+packet LeClearPeriodicAdvertiserListComplete : CommandComplete (command_op_code = LE_CLEAR_PERIODIC_ADVERTISER_LIST) {
+ status : ErrorCode,
+}
+
+packet LeReadPeriodicAdvertiserListSize : Command (op_code = LE_READ_PERIODIC_ADVERTISER_LIST_SIZE) {
+}
+
+packet LeReadPeriodicAdvertiserListSizeComplete : CommandComplete (command_op_code = LE_READ_PERIODIC_ADVERTISER_LIST_SIZE) {
+ status : ErrorCode,
+ periodic_advertiser_list_size : 8,
+}
+
+packet LeReadTransmitPower : Command (op_code = LE_READ_TRANSMIT_POWER) {
+}
+
+packet LeReadTransmitPowerComplete : CommandComplete (command_op_code = LE_READ_TRANSMIT_POWER) {
+ status : ErrorCode,
+ min_tx_power_dbm : 8,
+ max_tx_power_dbm : 8,
+}
+
+packet LeReadRfPathCompensationPower : Command (op_code = LE_READ_RF_PATH_COMPENSATION_POWER) {
+}
+
+packet LeReadRfPathCompensationPowerComplete : CommandComplete (command_op_code = LE_READ_RF_PATH_COMPENSATION_POWER) {
+ status : ErrorCode,
+ rf_tx_path_compensation_tenths_db : 16,
+ rf_rx_path_compensation_tenths_db : 16,
+}
+
+packet LeWriteRfPathCompensationPower : Command (op_code = LE_WRITE_RF_PATH_COMPENSATION_POWER) {
+ rf_tx_path_compensation_tenths_db : 16,
+ rf_rx_path_compensation_tenths_db : 16,
+}
+
+packet LeWriteRfPathCompensationPowerComplete : CommandComplete (command_op_code = LE_WRITE_RF_PATH_COMPENSATION_POWER) {
+ status : ErrorCode,
+}
+
+enum PrivacyMode : 8 {
+ NETWORK = 0,
+ DEVICE = 1,
+}
+
+packet LeSetPrivacyMode : Command (op_code = LE_SET_PRIVACY_MODE) {
+ peer_identity_address_type : PeerAddressType,
+ peer_identity_address : Address,
+ privacy_mode : PrivacyMode,
+}
+
+packet LeSetPrivacyModeComplete : CommandComplete (command_op_code = LE_SET_PRIVACY_MODE) {
+ status : ErrorCode,
+}
+
+packet LeSetPeriodicAdvertisingReceiveEnable : Command (op_code = LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE) {
+ sync_handle : 12,
+ _reserved_ : 4,
+ enable : 8,
+}
+
+packet LeSetPeriodicAdvertisingReceiveEnableComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_RECEIVE_ENABLE) {
+ status : ErrorCode,
+}
+
+packet LePeriodicAdvertisingSyncTransfer : Command (op_code = LE_PERIODIC_ADVERTISING_SYNC_TRANSFER) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ service_data : 16,
+ sync_handle: 12,
+ _reserved_ : 4,
+}
+
+packet LePeriodicAdvertisingSyncTransferComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_SYNC_TRANSFER) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LePeriodicAdvertisingSetInfoTransfer : Command (op_code = LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ service_data : 16,
+ advertising_handle: 8,
+}
+
+packet LePeriodicAdvertisingSetInfoTransferComplete : CommandComplete (command_op_code = LE_PERIODIC_ADVERTISING_SET_INFO_TRANSFER) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum SyncTransferMode : 8 {
+ NO_SYNC = 0,
+ SEND_SYNC_RECEIVED_DISABLE_REPORTS = 1,
+ SEND_SYNC_RECEIVED_SEND_REPORTS = 2,
+}
+
+enum CteType : 8 {
+ AOA_CONSTANT_TONE_EXTENSION = 0x00,
+ AOD_CONSTANT_TONE_EXTENSION_ONE_US_SLOTS = 0x01,
+ AOD_CONSTANT_TONE_EXTENSION_TWO_US_SLOTS = 0x02,
+ NO_CONSTANT_TONE_EXTENSION = 0xFF,
+}
+
+packet LeSetPeriodicAdvertisingSyncTransferParameters : Command (op_code = LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ mode : SyncTransferMode,
+ skip: 16,
+ sync_timeout : 16,
+ cte_type : CteType,
+}
+
+packet LeSetPeriodicAdvertisingSyncTransferParametersComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeSetDefaultPeriodicAdvertisingSyncTransferParameters : Command (op_code = LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) {
+ mode : SyncTransferMode,
+ skip: 16,
+ sync_timeout : 16,
+ cte_type : CteType,
+}
+
+packet LeSetDefaultPeriodicAdvertisingSyncTransferParametersComplete : CommandComplete (command_op_code = LE_SET_DEFAULT_PERIODIC_ADVERTISING_SYNC_TRANSFER_PARAMETERS) {
+ status : ErrorCode,
+}
+
+enum UseDebugKey : 8 {
+ USE_GENERATED_KEY = 0,
+ USE_DEBUG_KEY = 1,
+}
+
+packet LeGenerateDhkeyCommand : Command (op_code = LE_GENERATE_DHKEY_V2) {
+ remote_p_256_public_key : 8[64],
+ key_type : UseDebugKey,
+}
+
+packet LeGenerateDhkeyCommandStatus : CommandStatus (command_op_code = LE_GENERATE_DHKEY_V2) {
+}
+
+enum ScaAction : 8 {
+ MORE_ACCURATE_CLOCK = 0,
+ LESS_ACCURATE_CLOCK = 1,
+}
+
+packet LeModifySleepClockAccuracy : Command (op_code = LE_MODIFY_SLEEP_CLOCK_ACCURACY) {
+ action : ScaAction,
+}
+
+packet LeModifySleepClockAccuracyComplete : CommandComplete (command_op_code = LE_MODIFY_SLEEP_CLOCK_ACCURACY) {
+ status : ErrorCode,
+}
+
+packet LeReadBufferSizeV2 : Command (op_code = LE_READ_BUFFER_SIZE_V2) {
+}
+
+packet LeReadBufferSizeV2Complete : CommandComplete (command_op_code = LE_READ_BUFFER_SIZE_V2) {
+ status : ErrorCode,
+ le_buffer_size : LeBufferSize,
+ iso_buffer_size : LeBufferSize,
+}
+
+packet LeReadIsoTxSync : Command (op_code = LE_READ_ISO_TX_SYNC) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeReadIsoTxSyncComplete : CommandComplete (command_op_code = LE_READ_ISO_TX_SYNC) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ packet_sequence_number : 16,
+ timestamp : 32,
+ time_offset : 24,
+}
+
+struct CisParametersConfig {
+ cis_id : 8,
+ max_sdu_c_to_p : 12,
+ _reserved_ : 4,
+ max_sdu_p_to_c : 12,
+ _reserved_ : 4,
+ phy_c_to_p : 3,
+ _reserved_ : 5,
+ phy_p_to_c : 3,
+ _reserved_ : 5,
+ rtn_c_to_p : 4,
+ _reserved_ : 4,
+ rtn_p_to_c : 4,
+ _reserved_ : 4,
+}
+
+enum Packing : 8 {
+ SEQUENTIAL = 0,
+ INTERLEAVED = 1,
+}
+
+enum ClockAccuracy : 8 {
+ PPM_500 = 0x00,
+ PPM_250 = 0x01,
+ PPM_150 = 0x02,
+ PPM_100 = 0x03,
+ PPM_75 = 0x04,
+ PPM_50 = 0x05,
+ PPM_30 = 0x06,
+ PPM_20 = 0x07,
+}
+
+packet LeSetCigParameters : Command (op_code = LE_SET_CIG_PARAMETERS) {
+ cig_id : 8,
+ sdu_interval_c_to_p : 24,
+ sdu_interval_p_to_c : 24,
+ worst_case_sca: ClockAccuracy,
+ packing : Packing,
+ framing : Enable,
+ max_transport_latency_c_to_p : 16,
+ max_transport_latency_p_to_c : 16,
+ _count_(cis_config) : 8,
+ cis_config : CisParametersConfig[],
+}
+
+packet LeSetCigParametersComplete : CommandComplete (command_op_code = LE_SET_CIG_PARAMETERS) {
+ status : ErrorCode,
+ cig_id : 8,
+ _count_(connection_handle) : 8,
+ connection_handle : 16[],
+}
+
+struct LeCisParametersTestConfig {
+ cis_id : 8,
+ nse : 8,
+ max_sdu_c_to_p : 16,
+ max_sdu_p_to_c : 16,
+ max_pdu_c_to_p : 16,
+ max_pdu_p_to_c : 16,
+ phy_c_to_p : 8,
+ phy_p_to_c : 8,
+ bn_c_to_p : 8,
+ bn_p_to_c : 8,
+}
+
+packet LeSetCigParametersTest : Command (op_code = LE_SET_CIG_PARAMETERS_TEST) {
+ cig_id : 8,
+ sdu_interval_c_to_p : 24,
+ sdu_interval_p_to_c : 24,
+ ft_c_to_p : 8,
+ ft_p_to_c : 8,
+ iso_interval : 16,
+ worst_case_sca: ClockAccuracy,
+ packing : Packing,
+ framing : Enable,
+ _count_(cis_config) : 8,
+ cis_config : LeCisParametersTestConfig[],
+}
+
+packet LeSetCigParametersTestComplete : CommandComplete (command_op_code = LE_SET_CIG_PARAMETERS_TEST) {
+ status : ErrorCode,
+ cig_id : 8,
+ _count_(connection_handle) : 8,
+ connection_handle : 16[],
+}
+
+struct LeCreateCisConfig {
+ cis_connection_handle : 12,
+ _reserved_ : 4,
+ acl_connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeCreateCis : Command (op_code = LE_CREATE_CIS) {
+ _count_(cis_config) : 8,
+ cis_config : LeCreateCisConfig[],
+}
+
+packet LeCreateCisStatus : CommandStatus (command_op_code = LE_CREATE_CIS) {
+}
+
+packet LeRemoveCig : Command (op_code = LE_REMOVE_CIG) {
+ cig_id : 8,
+}
+
+packet LeRemoveCigComplete : CommandComplete (command_op_code = LE_REMOVE_CIG) {
+ status : ErrorCode,
+ cig_id : 8,
+}
+
+packet LeAcceptCisRequest : Command (op_code = LE_ACCEPT_CIS_REQUEST) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeAcceptCisRequestStatus : CommandStatus (command_op_code = LE_ACCEPT_CIS_REQUEST) {
+}
+
+packet LeRejectCisRequest : Command (op_code = LE_REJECT_CIS_REQUEST) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ reason : ErrorCode,
+}
+
+packet LeRejectCisRequestComplete : CommandComplete (command_op_code = LE_REJECT_CIS_REQUEST) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeCreateBig : Command (op_code = LE_CREATE_BIG) {
+ big_handle : 8,
+ advertising_handle : 8,
+ num_bis : 8,
+ sdu_interval : 24,
+ max_sdu : 16,
+ max_transport_latency : 16,
+ rtn : 4,
+ _reserved_ : 4,
+ phy : SecondaryPhyType,
+ packing : Packing,
+ framing : Enable,
+ encryption : Enable,
+ broadcast_code: 8[16],
+}
+
+packet LeCreateBigStatus : CommandStatus (command_op_code = LE_CREATE_BIG) {
+}
+
+packet LeTerminateBig : Command (op_code = LE_TERMINATE_BIG) {
+ big_handle : 8,
+ reason : ErrorCode,
+}
+
+packet LeTerminateBigStatus : CommandStatus (command_op_code = LE_TERMINATE_BIG) {
+}
+
+packet LeBigCreateSync : Command (op_code = LE_BIG_CREATE_SYNC) {
+ big_handle : 8,
+ sync_handle : 12,
+ _reserved_ : 4,
+ encryption : Enable,
+ broadcast_code : 8[16],
+ mse : 5,
+ _reserved_ : 3,
+ big_sync_timeout : 16,
+ _count_(bis) : 8,
+ bis : 8[],
+}
+
+packet LeBigCreateSyncStatus : CommandStatus (command_op_code = LE_BIG_CREATE_SYNC) {
+}
+
+packet LeBigTerminateSync : Command (op_code = LE_BIG_TERMINATE_SYNC) {
+ big_handle : 8,
+}
+
+packet LeBigTerminateSyncComplete : CommandComplete (command_op_code = LE_BIG_TERMINATE_SYNC) {
+ status : ErrorCode,
+ big_handle : 8,
+}
+
+packet LeRequestPeerSca : Command (op_code = LE_REQUEST_PEER_SCA) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeRequestPeerScaStatus : CommandStatus (command_op_code = LE_REQUEST_PEER_SCA) {
+}
+
+packet LeSetupIsoDataPath : Command (op_code = LE_SETUP_ISO_DATA_PATH) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ data_path_direction : DataPathDirection,
+ data_path_id : 8,
+ codec_id : 40,
+ controller_delay : 24,
+ _count_(codec_configuration) : 8,
+ codec_configuration : 8[],
+}
+
+packet LeSetupIsoDataPathComplete : CommandComplete (command_op_code = LE_SETUP_ISO_DATA_PATH) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum RemoveDataPathDirection : 8 {
+ INPUT = 1,
+ OUTPUT = 2,
+ INPUT_AND_OUTPUT = 3,
+}
+
+packet LeRemoveIsoDataPath : Command (op_code = LE_REMOVE_ISO_DATA_PATH) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ remove_data_path_direction : RemoveDataPathDirection,
+}
+
+packet LeRemoveIsoDataPathComplete : CommandComplete (command_op_code = LE_REMOVE_ISO_DATA_PATH) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+enum LeHostFeatureBits : 8 {
+ CONNECTED_ISO_STREAM_HOST_SUPPORT = 32,
+ CONNECTION_SUBRATING_HOST_SUPPORT = 38,
+}
+
+packet LeSetHostFeature : Command (op_code = LE_SET_HOST_FEATURE) {
+ bit_number : LeHostFeatureBits,
+ bit_value: Enable,
+}
+
+packet LeSetHostFeatureComplete : CommandComplete (command_op_code = LE_SET_HOST_FEATURE) {
+ status : ErrorCode,
+}
+
+packet LeReadIsoLinkQuality : Command (op_code = LE_READ_ISO_LINK_QUALITY) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeReadIsoLinkQualityComplete : CommandComplete (command_op_code = LE_READ_ISO_LINK_QUALITY) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ tx_unacked_packets : 32,
+ tx_flushed_packets : 32,
+ tx_last_subevent_packets : 32,
+ retransmitted_packets : 32,
+ crc_error_packets : 32,
+ rx_unreceived_packets : 32,
+ duplicate_packets : 32,
+}
+
+packet LeEnhancedReadTransmitPowerLevel : Command (op_code = LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ phy : 8,
+}
+
+enum PhyWithCodedSpecified : 8 {
+ LE_1M = 1,
+ LE_2M = 2,
+ LE_CODED_S_8 = 3,
+ LE_CODED_S_2 = 4,
+}
+
+packet LeEnhancedReadTransmitPowerLevelComplete : CommandComplete (command_op_code = LE_ENHANCED_READ_TRANSMIT_POWER_LEVEL) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ phy : PhyWithCodedSpecified,
+ current_transmit_power_level : 8,
+ max_transmit_power_level : 8,
+}
+
+packet LeReadRemoteTransmitPowerLevel : Command (op_code = LE_READ_REMOTE_TRANSMIT_POWER_LEVEL) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ phy : 8,
+}
+
+packet LeReadRemoteTransmitPowerLevelStatus : CommandStatus (command_op_code = LE_READ_REMOTE_TRANSMIT_POWER_LEVEL) {
+}
+
+packet LeSetPathLossReportingParameters : Command (op_code = LE_SET_PATH_LOSS_REPORTING_PARAMETERS) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ high_threshold : 8,
+ high_hysteresis : 8,
+ low_threshold : 8,
+ low_hysteresis : 8,
+ min_time_spent : 16,
+}
+
+packet LeSetPathLossReportingParametersComplete : CommandComplete (command_op_code = LE_SET_PATH_LOSS_REPORTING_PARAMETERS) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeSetPathLossReportingEnable : Command (op_code = LE_SET_PATH_LOSS_REPORTING_ENABLE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ enable : 8,
+}
+
+packet LeSetPathLossReportingEnableComplete : CommandComplete (command_op_code = LE_SET_PATH_LOSS_REPORTING_ENABLE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeSetTransmitPowerReportingEnable : Command (op_code = LE_SET_TRANSMIT_POWER_REPORTING_ENABLE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ local_enable : 8,
+ remote_enable : 8,
+}
+
+packet LeSetTransmitPowerReportingEnableComplete : CommandComplete (command_op_code = LE_SET_TRANSMIT_POWER_REPORTING_ENABLE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeSetDataRelatedAddressChanges : Command (op_code = LE_SET_DATA_RELATED_ADDRESS_CHANGES) {
+ advertising_handle : 8,
+ change_reasons : 8,
+}
+
+packet LeSetDataRelatedAddressChangesComplete : CommandComplete (command_op_code = LE_SET_DATA_RELATED_ADDRESS_CHANGES) {
+ status : ErrorCode,
+}
+
+packet LeSetDefaultSubrate : Command (op_code = LE_SET_DEFAULT_SUBRATE) {
+ subrate_min : 9,
+ _reserved_ : 7,
+ subrate_max : 9,
+ _reserved_ : 7,
+ max_latency : 9,
+ _reserved_ : 7,
+ continuation_number : 9,
+ _reserved_ : 7,
+ supervision_timeout: 12,
+ _reserved_ : 4,
+}
+
+packet LeSetDefaultSubrateComplete : CommandComplete (command_op_code = LE_SET_DEFAULT_SUBRATE) {
+ status : ErrorCode,
+}
+
+packet LeSubrateRequest : Command (op_code = LE_SUBRATE_REQUEST) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ subrate_min : 9,
+ _reserved_ : 7,
+ subrate_max : 9,
+ _reserved_ : 7,
+ max_latency : 9,
+ _reserved_ : 7,
+ continuation_number : 9,
+ _reserved_ : 7,
+ supervision_timeout: 12,
+ _reserved_ : 4,
+}
+
+packet LeSubrateRequestStatus : CommandStatus (command_op_code = LE_SUBRATE_REQUEST) {
+}
+
+// HCI Event Packets
+
+packet InquiryComplete : Event (event_code = INQUIRY_COMPLETE) {
+ status : ErrorCode,
+}
+
+struct InquiryResponse {
+ bd_addr : Address,
+ page_scan_repetition_mode : PageScanRepetitionMode,
+ _reserved_ : 8,
+ _reserved_ : 8,
+ class_of_device : 24,
+ clock_offset : 15,
+ _reserved_ : 1,
+}
+
+packet InquiryResult : Event (event_code = INQUIRY_RESULT) {
+ _count_(responses) : 8,
+ responses : InquiryResponse[],
+}
+
+enum LinkType : 8 {
+ SCO = 0x00,
+ ACL = 0x01,
+}
+
+packet ConnectionComplete : Event (event_code = CONNECTION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ bd_addr : Address,
+ link_type : LinkType,
+ encryption_enabled : Enable,
+}
+
+enum ConnectionRequestLinkType : 8 {
+ UNKNOWN = 0xFF,
+ SCO = 0x00,
+ ACL = 0x01,
+ ESCO = 0x02,
+}
+
+packet ConnectionRequest : Event (event_code = CONNECTION_REQUEST) {
+ bd_addr : Address,
+ class_of_device : 24,
+ link_type : ConnectionRequestLinkType,
+}
+
+packet DisconnectionComplete : Event (event_code = DISCONNECTION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ reason : ErrorCode,
+}
+
+packet AuthenticationComplete : Event (event_code = AUTHENTICATION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet RemoteNameRequestComplete : Event (event_code = REMOTE_NAME_REQUEST_COMPLETE) {
+ status : ErrorCode,
+ bd_addr : Address,
+ remote_name : 8[248], // UTF-8 encoded user-friendly descriptive name
+}
+
+enum EncryptionEnabled : 8 {
+ OFF = 0x00,
+ ON = 0x01, // E0 for BR/EDR and AES-CCM for LE
+ BR_EDR_AES_CCM = 0x02,
+}
+
+packet EncryptionChange : Event (event_code = ENCRYPTION_CHANGE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ encryption_enabled : EncryptionEnabled,
+}
+
+packet ChangeConnectionLinkKeyComplete : Event (event_code = CHANGE_CONNECTION_LINK_KEY_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet CentralLinkKeyComplete : Event (event_code = CENTRAL_LINK_KEY_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ key_flag : KeyFlag,
+}
+
+packet ReadRemoteSupportedFeaturesComplete : Event (event_code = READ_REMOTE_SUPPORTED_FEATURES_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ lmp_features : 64,
+}
+
+packet ReadRemoteVersionInformationComplete : Event (event_code = READ_REMOTE_VERSION_INFORMATION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ version : 8,
+ manufacturer_name : 16,
+ sub_version : 16,
+}
+
+packet QosSetupComplete : Event (event_code = QOS_SETUP_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ _reserved_ : 8,
+ service_type : ServiceType,
+ token_rate : 32, // Octets/s
+ peak_bandwidth : 32, // Octets/s
+ latency : 32, // Octets/s
+ delay_variation : 32, // microseconds
+}
+
+// Command Complete and Command Status Events are implemented above Commands.
+
+packet HardwareError : Event (event_code = HARDWARE_ERROR) {
+ hardware_code : 8,
+}
+
+packet FlushOccurred : Event (event_code = FLUSH_OCCURRED) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet RoleChange : Event (event_code = ROLE_CHANGE) {
+ status : ErrorCode,
+ bd_addr : Address,
+ new_role : Role,
+}
+
+packet NumberOfCompletedPackets : Event (event_code = NUMBER_OF_COMPLETED_PACKETS) {
+ _count_(completed_packets) : 8,
+ completed_packets : CompletedPackets[],
+}
+
+enum Mode : 8 {
+ ACTIVE = 0x00,
+ HOLD = 0x01,
+ SNIFF = 0x02,
+}
+
+packet ModeChange : Event (event_code = MODE_CHANGE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ current_mode : Mode,
+ interval : 16, // 0x002 - 0xFFFE (1.25ms - 40.9s)
+}
+
+struct ZeroKeyAndAddress {
+ address : Address,
+ _fixed_ = 0 : 64,
+ _fixed_ = 0 : 64,
+}
+
+packet ReturnLinkKeys : Event (event_code = RETURN_LINK_KEYS) {
+ _count_(keys) : 8,
+ keys : ZeroKeyAndAddress[],
+}
+
+packet PinCodeRequest : Event (event_code = PIN_CODE_REQUEST) {
+ bd_addr : Address,
+}
+
+packet LinkKeyRequest : Event (event_code = LINK_KEY_REQUEST) {
+ bd_addr : Address,
+}
+
+enum KeyType : 8 {
+ COMBINATION = 0x00,
+ DEBUG_COMBINATION = 0x03,
+ UNAUTHENTICATED_P192 = 0x04,
+ AUTHENTICATED_P192 = 0x05,
+ CHANGED = 0x06,
+ UNAUTHENTICATED_P256 = 0x07,
+ AUTHENTICATED_P256 = 0x08,
+}
+
+packet LinkKeyNotification : Event (event_code = LINK_KEY_NOTIFICATION) {
+ bd_addr : Address,
+ link_key : 8[16],
+ key_type : KeyType,
+}
+
+packet LoopbackCommand : Event (event_code = LOOPBACK_COMMAND) {
+ _payload_, // Command packet, truncated if it was longer than 252 bytes
+}
+
+packet DataBufferOverflow : Event (event_code = DATA_BUFFER_OVERFLOW) {
+ link_type : LinkType,
+}
+
+packet MaxSlotsChange : Event (event_code = MAX_SLOTS_CHANGE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ lmp_max_slots : 8,
+}
+
+packet ReadClockOffsetComplete : Event (event_code = READ_CLOCK_OFFSET_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ clock_offset : 15,
+ _reserved_ : 1,
+}
+
+packet ConnectionPacketTypeChanged : Event (event_code = CONNECTION_PACKET_TYPE_CHANGED) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ packet_type : 16,
+}
+
+packet QosViolation : Event (event_code = QOS_VIOLATION) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet PageScanRepetitionModeChange : Event (event_code = PAGE_SCAN_REPETITION_MODE_CHANGE) {
+ bd_addr : Address,
+ page_scan_repetition_mode : PageScanRepetitionMode,
+}
+
+packet FlowSpecificationComplete : Event (event_code = FLOW_SPECIFICATION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ _reserved_ : 8,
+ flow_direction : FlowDirection,
+ service_type : ServiceType,
+ token_rate : 32, // Octets/s
+ token_bucket_size : 32,
+ peak_bandwidth : 32, // Octets/s
+ access_latency : 32, // Octets/s
+}
+
+struct InquiryResponseWithRssi {
+ address : Address,
+ page_scan_repetition_mode : PageScanRepetitionMode,
+ _reserved_ : 8,
+ class_of_device : 24,
+ clock_offset : 15,
+ _reserved_ : 1,
+ rssi : 8,
+}
+
+packet InquiryResultWithRssi : Event (event_code = INQUIRY_RESULT_WITH_RSSI) {
+ _count_(responses) : 8,
+ responses : InquiryResponseWithRssi[],
+}
+
+packet ReadRemoteExtendedFeaturesComplete : Event (event_code = READ_REMOTE_EXTENDED_FEATURES_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ page_number : 8,
+ maximum_page_number : 8,
+ extended_lmp_features : 64,
+}
+
+enum ScoLinkType : 8 {
+ SCO = 0x00,
+ ESCO = 0x02,
+}
+
+enum ScoAirMode : 8 {
+ ULAW_LOG = 0x00,
+ ALAW_LOG = 0x01,
+ CVSD = 0x02,
+ TRANSPARENT = 0x03,
+}
+
+packet SynchronousConnectionComplete : Event (event_code = SYNCHRONOUS_CONNECTION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ bd_addr : Address,
+ link_type : ScoLinkType,
+ // Time between two consecutive eSCO instants measured in slots.
+ // eSCO only, Shall be zero for SCO links.
+ transmission_interval_slots : 8,
+ // The size of the retransmission window measured in slots.
+ // eSCO only. Shall be zero for SCO links.
+ retransmission_window_slots : 8,
+ // Length in bytes of the eSCO payload in the receive direction.
+ // eSCO only. Shall be zero for SCO links.
+ rx_packet_length : 16,
+ // Length in bytes of the eSCO payload in the transmit direction.
+ // eSCO only. Shall be zero for SCO links.
+ tx_packet_length : 16,
+ air_mode : ScoAirMode,
+}
+
+test SynchronousConnectionComplete {
+ "\x2c\x11\x00\x03\x00\x1d\xdf\xed\x2b\x1a\xf8\x02\x0c\x04\x3c\x00\x3c\x00\x03",
+}
+
+packet SynchronousConnectionChanged : Event (event_code = SYNCHRONOUS_CONNECTION_CHANGED) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ // Time between two consecutive eSCO instants measured in slots.
+ // eSCO only, Shall be zero for SCO links.
+ transmission_interval_slots : 8,
+ // Time between two consecutive SCO/eSCO instants measured in slots.
+ retransmission_window_slots : 8,
+ // Length in bytes of the SCO/eSCO payload in the receive direction.
+ rx_packet_length : 16,
+ // Length in bytes of the SCO/eSCO payload in the transmit direction.
+ tx_packet_length : 16,
+}
+
+packet SniffSubratingEvent : Event (event_code = SNIFF_SUBRATING) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ maximum_transmit_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+ maximum_receive_latency : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+ minimum_remote_timeout : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+ minimum_local_timeout : 16, // 0x000 - 0xFFFE (0s - 40.9s)
+}
+
+packet ExtendedInquiryResult : Event (event_code = EXTENDED_INQUIRY_RESULT) {
+ _fixed_ = 0x01 : 8,
+ address : Address,
+ page_scan_repetition_mode : PageScanRepetitionMode,
+ _reserved_ : 8,
+ class_of_device : 24,
+ clock_offset : 15,
+ _reserved_ : 1,
+ rssi : 8,
+ // Extended inquiry Result is always 255 bytes long;
+ // the gap data is padded with zeros as necessary.
+ // Refer to BLUETOOTH CORE SPECIFICATION Version 5.2 | Vol 3, Part C Section 8 on page 1340
+ extended_inquiry_response : 8[240],
+}
+
+packet EncryptionKeyRefreshComplete : Event (event_code = ENCRYPTION_KEY_REFRESH_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+packet IoCapabilityRequest : Event (event_code = IO_CAPABILITY_REQUEST) {
+ bd_addr : Address,
+}
+
+packet IoCapabilityResponse : Event (event_code = IO_CAPABILITY_RESPONSE) {
+ bd_addr : Address,
+ io_capability : IoCapability,
+ oob_data_present : OobDataPresent,
+ authentication_requirements : AuthenticationRequirements,
+}
+
+packet UserConfirmationRequest : Event (event_code = USER_CONFIRMATION_REQUEST) {
+ bd_addr : Address,
+ numeric_value : 20, // 0x00000-0xF423F (000000 - 999999)
+ _reserved_ : 12,
+}
+
+packet UserPasskeyRequest : Event (event_code = USER_PASSKEY_REQUEST) {
+ bd_addr : Address,
+}
+
+packet RemoteOobDataRequest : Event (event_code = REMOTE_OOB_DATA_REQUEST) {
+ bd_addr : Address,
+}
+
+packet SimplePairingComplete : Event (event_code = SIMPLE_PAIRING_COMPLETE) {
+ status : ErrorCode,
+ bd_addr : Address,
+}
+
+packet LinkSupervisionTimeoutChanged : Event (event_code = LINK_SUPERVISION_TIMEOUT_CHANGED) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ link_supervision_timeout : 16, // 0x001-0xFFFF (0.625ms-40.9s)
+}
+
+enum FlushablePacketType : 8 {
+ AUTOMATICALLY_FLUSHABLE_ONLY = 0,
+}
+
+packet EnhancedFlush : Command (op_code = ENHANCED_FLUSH) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ packet_type : FlushablePacketType,
+}
+
+test EnhancedFlush {
+ "\x5f\x0c\x03\x02\x00\x00",
+}
+
+packet EnhancedFlushStatus : CommandStatus (command_op_code = ENHANCED_FLUSH) {
+}
+
+packet EnhancedFlushComplete : Event (event_code = ENHANCED_FLUSH_COMPLETE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+}
+
+test EnhancedFlushComplete {
+ "\x39\x02\x02\x00",
+}
+
+packet UserPasskeyNotification : Event (event_code = USER_PASSKEY_NOTIFICATION) {
+ bd_addr : Address,
+ passkey : 20, // 0x00000-0xF423F (000000 - 999999)
+ _reserved_ : 12,
+}
+
+packet KeypressNotification : Event (event_code = KEYPRESS_NOTIFICATION) {
+ bd_addr : Address,
+ notification_type : KeypressNotificationType,
+}
+
+packet RemoteHostSupportedFeaturesNotification : Event (event_code = REMOTE_HOST_SUPPORTED_FEATURES_NOTIFICATION) {
+ bd_addr : Address,
+ host_supported_features : 64,
+}
+
+packet LeMetaEvent : Event (event_code = LE_META_EVENT) {
+ subevent_code : SubeventCode,
+ _body_,
+}
+
+packet NumberOfCompletedDataBlocks : Event (event_code = NUMBER_OF_COMPLETED_DATA_BLOCKS) {
+ total_num_data_blocks : 16,
+ _payload_, // placeholder (unimplemented)
+}
+
+// LE Events
+packet LeConnectionComplete : LeMetaEvent (subevent_code = CONNECTION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ role : Role,
+ peer_address_type : AddressType,
+ peer_address : Address,
+ connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+ peripheral_latency : 16,
+ supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s)
+ central_clock_accuracy : ClockAccuracy,
+}
+
+enum AdvertisingEventType : 8 {
+ ADV_IND = 0x00,
+ ADV_DIRECT_IND = 0x01,
+ ADV_SCAN_IND = 0x02,
+ ADV_NONCONN_IND = 0x03,
+ SCAN_RESPONSE = 0x04,
+}
+
+struct LeAdvertisingResponse {
+ event_type : AdvertisingEventType,
+ address_type : AddressType,
+ address : Address,
+ _size_(advertising_data) : 8,
+ advertising_data : 8[],
+ rssi : 8,
+}
+
+packet LeAdvertisingReport : LeMetaEvent (subevent_code = ADVERTISING_REPORT) {
+ _count_(responses) : 8,
+ responses : LeAdvertisingResponse[],
+}
+
+packet LeConnectionUpdateComplete : LeMetaEvent (subevent_code = CONNECTION_UPDATE_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+ peripheral_latency : 16, // Number of connection events
+ supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s)
+}
+
+packet LeReadRemoteFeaturesComplete : LeMetaEvent (subevent_code = READ_REMOTE_FEATURES_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ le_features : 64,
+}
+
+packet LeLongTermKeyRequest : LeMetaEvent (subevent_code = LONG_TERM_KEY_REQUEST) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ random_number : 8[8],
+ encrypted_diversifier : 16,
+}
+
+packet LeRemoteConnectionParameterRequest : LeMetaEvent (subevent_code = REMOTE_CONNECTION_PARAMETER_REQUEST) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ interval_min : 16, // 0x006 - 0x0C80 (7.5ms - 4s)
+ interval_max : 16, // 0x006 - 0x0C80 (7.5ms - 4s)
+ latency : 16, // Number of connection events (0x0000 to 0x01f3 (499)
+ timeout : 16, // 0x000A to 0x0C80 (100ms to 32s)
+}
+
+packet LeDataLengthChange : LeMetaEvent (subevent_code = DATA_LENGTH_CHANGE) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ max_tx_octets : 16, // 0x001B - 0x00FB
+ max_tx_time : 16, // 0x0148 - 0x4290
+ max_rx_octets : 16, // 0x001B - 0x00FB
+ max_rx_time : 16, // 0x0148 - 0x4290
+}
+
+packet ReadLocalP256PublicKeyComplete : LeMetaEvent (subevent_code = READ_LOCAL_P256_PUBLIC_KEY_COMPLETE) {
+ status : ErrorCode,
+ local_p_256_public_key : 8[64],
+}
+
+packet GenerateDhKeyComplete : LeMetaEvent (subevent_code = GENERATE_DHKEY_COMPLETE) {
+ status : ErrorCode,
+ dh_key : 8[32],
+}
+
+packet LeEnhancedConnectionComplete : LeMetaEvent (subevent_code = ENHANCED_CONNECTION_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ role : Role,
+ peer_address_type : AddressType,
+ peer_address : Address,
+ local_resolvable_private_address : Address,
+ peer_resolvable_private_address : Address,
+ connection_interval : 16, // 0x006 - 0x0C80 (7.5ms - 4000ms)
+ peripheral_latency : 16,
+ supervision_timeout : 16, // 0x000A to 0x0C80 (100ms to 32s)
+ central_clock_accuracy : ClockAccuracy,
+}
+
+enum DirectAdvertisingAddressType : 8 {
+ PUBLIC_DEVICE_ADDRESS = 0x00,
+ RANDOM_DEVICE_ADDRESS = 0x01,
+ PUBLIC_IDENTITY_ADDRESS = 0x02,
+ RANDOM_IDENTITY_ADDRESS = 0x03,
+ CONTROLLER_UNABLE_TO_RESOLVE = 0xFE,
+ NO_ADDRESS_PROVIDED = 0xFF,
+}
+
+enum DirectAdvertisingEventType : 8 {
+ ADV_DIRECT_IND = 0x01,
+}
+
+enum DirectAddressType : 8 {
+ RANDOM_DEVICE_ADDRESS = 0x01,
+}
+
+struct LeDirectedAdvertisingResponse {
+ event_type : DirectAdvertisingEventType,
+ address_type : DirectAdvertisingAddressType,
+ address : Address,
+ direct_address_type : DirectAddressType,
+ direct_address : Address,
+ rssi : 8,
+}
+
+packet LeDirectedAdvertisingReport : LeMetaEvent (subevent_code = DIRECTED_ADVERTISING_REPORT) {
+ _count_(responses) : 8,
+ responses : LeDirectedAdvertisingResponse[],
+}
+
+packet LePhyUpdateComplete : LeMetaEvent (subevent_code = PHY_UPDATE_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ tx_phy : PhyType,
+ rx_phy : PhyType,
+}
+
+enum DataStatus : 2 {
+ COMPLETE = 0x0,
+ CONTINUING = 0x1,
+ TRUNCATED = 0x2,
+ RESERVED = 0x3,
+}
+
+struct LeExtendedAdvertisingResponse {
+ connectable : 1,
+ scannable : 1,
+ directed : 1,
+ scan_response : 1,
+ legacy : 1,
+ data_status : DataStatus,
+ _reserved_ : 9,
+ address_type : DirectAdvertisingAddressType,
+ address : Address,
+ primary_phy : PrimaryPhyType,
+ secondary_phy : SecondaryPhyType,
+ advertising_sid : 8, // SID subfield in the ADI field
+ tx_power : 8,
+ rssi : 8, // -127 to +20 (0x7F means not available)
+ periodic_advertising_interval : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+ direct_address_type : DirectAdvertisingAddressType,
+ direct_address : Address,
+ _size_(advertising_data) : 8,
+ advertising_data: 8[],
+}
+
+packet LeExtendedAdvertisingReport : LeMetaEvent (subevent_code = EXTENDED_ADVERTISING_REPORT) {
+ _count_(responses) : 8,
+ responses : LeExtendedAdvertisingResponse[],
+}
+
+packet LePeriodicAdvertisingSyncEstablished : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_ESTABLISHED) {
+ status : ErrorCode,
+ sync_handle : 12,
+ _reserved_ : 4,
+ advertising_sid : 8,
+ advertiser_address_type : AddressType,
+ advertiser_address : Address,
+ advertiser_phy : SecondaryPhyType,
+ periodic_advertising_interval : 16,
+ advertiser_clock_accuracy : ClockAccuracy,
+}
+
+packet LePeriodicAdvertisingReport : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_REPORT) {
+ sync_handle : 12,
+ _reserved_ : 4,
+ tx_power : 8,
+ rssi : 8,
+ cte_type : CteType,
+ data_status : DataStatus,
+ _reserved_: 6,
+ _size_(data) : 8,
+ data : 8[],
+}
+
+packet LePeriodicAdvertisingSyncLost : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_LOST) {
+ sync_handle : 12,
+ _reserved_ : 4,
+}
+
+packet LeScanTimeout : LeMetaEvent (subevent_code = SCAN_TIMEOUT) {
+}
+
+packet LeAdvertisingSetTerminated : LeMetaEvent (subevent_code = ADVERTISING_SET_TERMINATED) {
+ status : ErrorCode,
+ advertising_handle : 8,
+ connection_handle : 12,
+ _reserved_ : 4,
+ num_completed_extended_advertising_events : 8,
+}
+
+packet LeScanRequestReceived : LeMetaEvent (subevent_code = SCAN_REQUEST_RECEIVED) {
+ advertising_handle : 8,
+ scanner_address_type : AddressType,
+ scanner_address : Address,
+}
+
+enum ChannelSelectionAlgorithm : 8 {
+ ALGORITHM_1 = 0,
+ ALGORITHM_2 = 1,
+}
+
+packet LeChannelSelectionAlgorithm : LeMetaEvent (subevent_code = CHANNEL_SELECTION_ALGORITHM) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ channel_selection_algorithm : ChannelSelectionAlgorithm,
+}
+
+packet LeConnectionlessIqReport : LeMetaEvent (subevent_code = CONNECTIONLESS_IQ_REPORT) {
+ _payload_, // placeholder (unimplemented)
+}
+
+packet LeConnectionIqReport : LeMetaEvent (subevent_code = CONNECTION_IQ_REPORT) {
+ _payload_, // placeholder (unimplemented)
+}
+
+packet LeCteRequestFailed : LeMetaEvent (subevent_code = CTE_REQUEST_FAILED) {
+ _payload_, // placeholder (unimplemented)
+}
+
+packet LePeriodicAdvertisingSyncTransferReceived : LeMetaEvent (subevent_code = PERIODIC_ADVERTISING_SYNC_TRANSFER_RECEIVED) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ service_data : 16,
+ sync_handle : 12,
+ _reserved_ : 4,
+ advertising_sid : 4,
+ _reserved_ : 4,
+ advertiser_address_type : AddressType,
+ advertiser_address : Address,
+ advertiser_phy : SecondaryPhyType,
+ periodic_advertising_interval : 16,
+ advertiser_clock_accuracy : ClockAccuracy,
+}
+
+packet LeCisEstablished : LeMetaEvent (subevent_code = CIS_ESTABLISHED) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ cig_sync_delay : 24,
+ cis_sync_delay : 24,
+ transport_latency_c_to_p : 24,
+ transport_latency_p_to_c : 24,
+ phy_c_to_p : SecondaryPhyType,
+ phy_p_to_c : SecondaryPhyType,
+ nse : 8,
+ bn_c_to_p : 4,
+ _reserved_ : 4,
+ bn_p_to_c : 4,
+ _reserved_ : 4,
+ ft_c_to_p : 8,
+ ft_p_to_c : 8,
+ max_pdu_c_to_p : 8,
+ _reserved_ : 8,
+ max_pdu_p_to_c : 8,
+ _reserved_ : 8,
+ iso_interval : 16,
+}
+
+packet LeCisRequest : LeMetaEvent (subevent_code = CIS_REQUEST) {
+ acl_connection_handle : 12,
+ _reserved_ : 4,
+ cis_connection_handle : 12,
+ _reserved_ : 4,
+ cig_id : 8,
+ cis_id : 8,
+}
+
+packet LeCreateBigComplete : LeMetaEvent (subevent_code = CREATE_BIG_COMPLETE) {
+ status : ErrorCode,
+ big_handle : 8,
+ big_sync_delay : 24,
+ transport_latency_big: 24,
+ phy : SecondaryPhyType,
+ nse : 8,
+ bn : 8,
+ pto : 8,
+ irc : 8,
+ max_pdu : 16,
+ iso_interval : 16,
+ _size_(connection_handle) : 8,
+ connection_handle : 16[],
+}
+
+packet LeTerminateBigComplete : LeMetaEvent (subevent_code = TERMINATE_BIG_COMPLETE) {
+ big_handle : 8,
+ reason : ErrorCode,
+}
+
+packet LeBigSyncEstablished : LeMetaEvent (subevent_code = BIG_SYNC_ESTABLISHED) {
+ status : ErrorCode,
+ big_handle : 8,
+ transport_latency_big : 24,
+ nse : 8,
+ bn : 8,
+ pto : 8,
+ irc : 8,
+ max_pdu : 16,
+ iso_interval : 16,
+ _size_(connection_handle) : 8,
+ connection_handle : 16[],
+}
+
+packet LeBigSyncLost : LeMetaEvent (subevent_code = BIG_SYNC_LOST) {
+ big_handle : 8,
+ reason : ErrorCode,
+}
+
+packet LeRequestPeerScaComplete : LeMetaEvent (subevent_code = REQUEST_PEER_SCA_COMPLETE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ peer_clock_accuracy : ClockAccuracy,
+}
+
+enum PathLossZone : 8 {
+ LOW = 0,
+ MID = 1,
+ HIGH = 2,
+}
+
+packet LePathLossThreshold : LeMetaEvent (subevent_code = PATH_LOSS_THRESHOLD) {
+ connection_handle : 12,
+ _reserved_ : 4,
+ current_path_loss : 8,
+ zone_entered : PathLossZone,
+}
+
+enum ReportingReason : 8 {
+ LOCAL_TRANSMIT_POWER_CHANGED = 0x00,
+ REMOTE_TRANSMIT_POWER_CHANGED = 0x01,
+ READ_COMMAND_COMPLETE = 0x02,
+}
+
+packet LeTransmitPowerReporting : LeMetaEvent (subevent_code = TRANSMIT_POWER_REPORTING) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ reason : ReportingReason,
+ phy : 8,
+ transmit_power_level : 8,
+ transmit_power_level_flag : 8,
+ delta : 8,
+}
+
+packet LeBigInfoAdvertisingReport : LeMetaEvent (subevent_code = BIG_INFO_ADVERTISING_REPORT) {
+ sync_handle : 12,
+ _reserved_ : 4,
+ num_bis : 8,
+ nse : 8,
+ iso_interval : 16,
+ bn : 8,
+ pto : 8,
+ irc : 8,
+ max_pdu : 16,
+ sdu_interval : 24,
+ max_sdu : 16,
+ phy : SecondaryPhyType,
+ framing : Enable,
+ encryption : Enable,
+}
+
+packet LeSubrateChange : LeMetaEvent (subevent_code = LE_SUBRATE_CHANGE) {
+ status : ErrorCode,
+ connection_handle : 12,
+ _reserved_ : 4,
+ subrate_factor : 9,
+ _reserved_ : 7,
+ peripheral_latency : 9,
+ _reserved_ : 7,
+ continuation_number : 9,
+ _reserved_ : 7,
+ supervision_timeout: 12,
+ _reserved_ : 4,
+}
+
+enum IsoPacketBoundaryFlag : 2 {
+ FIRST_FRAGMENT = 0,
+ CONTINUATION_FRAGMENT = 1,
+ COMPLETE_SDU = 2,
+ LAST_FRAGMENT = 3,
+}
+
+enum TimeStampFlag : 1 {
+ NOT_PRESENT = 0,
+ PRESENT = 1,
+}
+
+packet Iso {
+ connection_handle : 12,
+ pb_flag : IsoPacketBoundaryFlag,
+ ts_flag : TimeStampFlag,
+ _reserved_ : 1,
+ _size_(_payload_) : 14,
+ _reserved_ : 2,
+ _payload_,
+}
+
+enum IsoPacketStatusFlag : 2 {
+ VALID = 0,
+ POSSIBLY_INVALID = 1,
+ LOST_DATA = 2,
+}
+
+packet IsoWithTimestamp : Iso (ts_flag = PRESENT) {
+ time_stamp : 32,
+ packet_sequence_number : 16,
+ iso_sdu_length : 12,
+ _reserved_ : 2,
+ packet_status_flag : IsoPacketStatusFlag,
+ _payload_,
+}
+
+packet IsoWithoutTimestamp : Iso (ts_flag = NOT_PRESENT) {
+ packet_sequence_number : 16,
+ iso_sdu_length : 12,
+ _reserved_ : 2,
+ packet_status_flag : IsoPacketStatusFlag,
+ _payload_,
+}
+
+// https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
+enum GapDataType : 8 {
+ INVALID = 0x00,
+ FLAGS = 0x01,
+ INCOMPLETE_LIST_16_BIT_UUIDS = 0x02,
+ COMPLETE_LIST_16_BIT_UUIDS = 0x03,
+ INCOMPLETE_LIST_32_BIT_UUIDS = 0x04,
+ COMPLETE_LIST_32_BIT_UUIDS = 0x05,
+ INCOMPLETE_LIST_128_BIT_UUIDS = 0x06,
+ COMPLETE_LIST_128_BIT_UUIDS = 0x07,
+ SHORTENED_LOCAL_NAME = 0x08,
+ COMPLETE_LOCAL_NAME = 0x09,
+ TX_POWER_LEVEL = 0x0A,
+ CLASS_OF_DEVICE = 0x0D,
+ SIMPLE_PAIRING_HASH_C = 0x0E,
+ SIMPLE_PAIRING_RANDOMIZER_R = 0x0F,
+ DEVICE_ID = 0x10,
+ SECURITY_MANAGER_OOB_FLAGS = 0x11,
+ SLAVE_CONNECTION_INTERVAL_RANGE = 0x12,
+ LIST_16BIT_SERVICE_SOLICITATION_UUIDS = 0x14,
+ LIST_128BIT_SERVICE_SOLICITATION_UUIDS = 0x15,
+ SERVICE_DATA_16_BIT_UUIDS = 0x16,
+ PUBLIC_TARGET_ADDRESS = 0x17,
+ RANDOM_TARGET_ADDRESS = 0x18,
+ APPEARANCE = 0x19,
+ ADVERTISING_INTERVAL = 0x1A,
+ LE_BLUETOOTH_DEVICE_ADDRESS = 0x1B,
+ LE_ROLE = 0x1C,
+ SIMPLE_PAIRING_HASH_C_256 = 0x1D,
+ SIMPLE_PAIRING_RANDOMIZER_R_256 = 0x1E,
+ LIST_32BIT_SERVICE_SOLICITATION_UUIDS = 0x1F,
+ SERVICE_DATA_32_BIT_UUIDS = 0x20,
+ SERVICE_DATA_128_BIT_UUIDS = 0x21,
+ LE_SECURE_CONNECTIONS_CONFIRMATION_VALUE = 0x22,
+ LE_SECURE_CONNECTIONS_RANDOM_VALUE = 0x23,
+ URI = 0x24,
+ INDOOR_POSITIONING = 0x25,
+ TRANSPORT_DISCOVERY_DATA = 0x26,
+ LE_SUPPORTED_FEATURES = 0x27,
+ CHANNEL_MAP_UPDATE_INDICATION = 0x28,
+ MESH_PB_ADV = 0x29,
+ MESH_MESSAGE = 0x2A,
+ MESH_BEACON = 0x2B,
+ BIG_INFO = 0x2C,
+ BROADCAST_CODE = 0x2D,
+ THREE_D_INFORMATION_DATA = 0x3D,
+ MANUFACTURER_SPECIFIC_DATA = 0xFF,
+}
+
+// -----------------------------------------------------------------------------
+// LE Get Vendor Capabilities Command
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#vendor-specific-capabilities
+// -----------------------------------------------------------------------------
+
+packet LeGetVendorCapabilities : Command (op_code = LE_GET_VENDOR_CAPABILITIES) {
+}
+
+test LeGetVendorCapabilities {
+ "\x53\xfd\x00",
+}
+
+struct BaseVendorCapabilities {
+ max_advt_instances: 8,
+ offloaded_resolution_of_private_address : 8,
+ total_scan_results_storage: 16,
+ max_irk_list_sz: 8,
+ filtering_support: 8,
+ max_filter: 8,
+ activity_energy_info_support: 8,
+}
+
+packet LeGetVendorCapabilitiesComplete : CommandComplete (command_op_code = LE_GET_VENDOR_CAPABILITIES) {
+ status : ErrorCode,
+ base_vendor_capabilities : BaseVendorCapabilities,
+ _payload_,
+}
+
+packet LeGetVendorCapabilitiesComplete095 : LeGetVendorCapabilitiesComplete {
+ version_supported: 16,
+ total_num_of_advt_tracked: 16,
+ extended_scan_support: 8,
+ debug_logging_supported: 8,
+ _payload_,
+}
+
+packet LeGetVendorCapabilitiesComplete096 : LeGetVendorCapabilitiesComplete095 {
+ le_address_generation_offloading_support: 8,
+ _payload_,
+}
+
+packet LeGetVendorCapabilitiesComplete098 : LeGetVendorCapabilitiesComplete096 {
+ a2dp_source_offload_capability_mask: 32,
+ bluetooth_quality_report_support: 8
+}
+
+// -----------------------------------------------------------------------------
+// LE Batch Scan Command
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_batch_scan_command
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#storage-threshold-breach-subevent
+// -----------------------------------------------------------------------------
+
+enum BatchScanOpcode : 8 {
+ ENABLE = 0x01,
+ SET_STORAGE_PARAMETERS = 0x02,
+ SET_SCAN_PARAMETERS = 0x03,
+ READ_RESULT_PARAMETERS = 0x04,
+}
+
+packet LeBatchScan : Command (op_code = LE_BATCH_SCAN) {
+ batch_scan_opcode : BatchScanOpcode,
+ _body_,
+}
+
+packet LeBatchScanComplete : CommandComplete (command_op_code = LE_BATCH_SCAN) {
+ status : ErrorCode,
+ batch_scan_opcode : BatchScanOpcode,
+ _body_,
+}
+
+packet LeBatchScanEnable : LeBatchScan (batch_scan_opcode = ENABLE) {
+ enable : Enable,
+}
+
+packet LeBatchScanEnableComplete : LeBatchScanComplete (batch_scan_opcode = ENABLE) {
+}
+
+packet LeBatchScanSetStorageParameters : LeBatchScan (batch_scan_opcode = SET_STORAGE_PARAMETERS) {
+ batch_scan_full_max_percentage : 8,
+ batch_scan_truncated_max_percentage : 8,
+ batch_scan_notify_threshold_percentage : 8,
+}
+
+packet LeBatchScanSetStorageParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_STORAGE_PARAMETERS) {
+}
+
+enum BatchScanDiscardRule : 8 {
+ OLDEST = 0x00,
+ WEAKEST_RSSI = 0x01,
+}
+
+packet LeBatchScanSetScanParameters : LeBatchScan (batch_scan_opcode = SET_SCAN_PARAMETERS) {
+ truncated_mode_enabled : 1,
+ full_mode_enabled : 1,
+ _reserved_ : 6,
+ duty_cycle_scan_window_slots : 32,
+ duty_cycle_scan_interval_slots : 32,
+ own_address_type : PeerAddressType,
+ batch_scan_discard_rule : BatchScanDiscardRule,
+}
+
+packet LeBatchScanSetScanParametersComplete : LeBatchScanComplete (batch_scan_opcode = SET_SCAN_PARAMETERS) {
+}
+
+enum BatchScanDataRead : 8 {
+ TRUNCATED_MODE_DATA = 0x01,
+ FULL_MODE_DATA = 0x02,
+}
+
+packet LeBatchScanReadResultParameters : LeBatchScan (batch_scan_opcode = READ_RESULT_PARAMETERS) {
+ batch_scan_data_read : BatchScanDataRead,
+}
+
+packet LeBatchScanReadResultParametersCompleteRaw : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
+ batch_scan_data_read : BatchScanDataRead,
+ num_of_records : 8,
+ raw_data : 8[],
+}
+
+packet LeBatchScanReadResultParametersComplete : LeBatchScanComplete (batch_scan_opcode = READ_RESULT_PARAMETERS) {
+ batch_scan_data_read : BatchScanDataRead,
+ _body_,
+}
+
+struct TruncatedResult {
+ bd_addr : Address,
+ address_type : AddressType,
+ tx_power : 8,
+ rssi : 8,
+ timestamp : 16,
+}
+
+packet LeBatchScanReadTruncatedResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = TRUNCATED_MODE_DATA) {
+ _count_(results) : 8,
+ results : TruncatedResult[],
+}
+
+struct FullResult {
+ bd_addr : Address,
+ address_type : AddressType,
+ tx_power : 8,
+ rssi : 8,
+ timestamp : 16,
+ _size_(adv_packet) : 8,
+ adv_packet : 8[],
+ _size_(scan_response) : 8,
+ scan_response : 8[],
+}
+
+packet LeBatchScanReadFullResultParametersComplete : LeBatchScanReadResultParametersComplete (batch_scan_data_read = FULL_MODE_DATA) {
+ _count_(results) : 8,
+ results : FullResult[],
+}
+
+packet StorageThresholdBreachEvent : VendorSpecificEvent (subevent_code = STORAGE_THRESHOLD_BREACH) {
+}
+
+// -----------------------------------------------------------------------------
+// Advertising Packet Content Filter (APCF) Command.
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#advertising-packet-content-filter
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le-advertisement-tracking-sub-event
+// -----------------------------------------------------------------------------
+
+enum ApcfOpcode : 8 {
+ ENABLE = 0x00,
+ SET_FILTERING_PARAMETERS = 0x01,
+ BROADCASTER_ADDRESS = 0x02,
+ SERVICE_UUID = 0x03,
+ SERVICE_SOLICITATION_UUID = 0x04,
+ LOCAL_NAME = 0x05,
+ MANUFACTURER_DATA = 0x06,
+ SERVICE_DATA = 0x07,
+ TRANSPORT_DISCOVERY_SERVICE = 0x08,
+ AD_TYPE_FILTER = 0x09,
+ READ_EXTENDED_FEATURES = 0xFF,
+}
+
+packet LeApcf : Command (op_code = LE_APCF) {
+ apcf_opcode : ApcfOpcode,
+ _payload_,
+}
+
+packet LeApcfComplete : CommandComplete (command_op_code = LE_APCF) {
+ status : ErrorCode,
+ apcf_opcode : ApcfOpcode,
+ _payload_
+}
+
+packet LeApcfEnable : LeApcf (apcf_opcode = ENABLE) {
+ apcf_enable : Enable,
+}
+
+packet LeApcfEnableComplete : LeApcfComplete (apcf_opcode = ENABLE) {
+ apcf_enable : Enable,
+}
+
+enum ApcfAction : 8 {
+ ADD = 0x00,
+ DELETE = 0x01,
+ CLEAR = 0x02,
+}
+
+enum DeliveryMode : 8 {
+ IMMEDIATE = 0x00,
+ ONFOUND = 0x01,
+ BATCHED = 0x02,
+}
+
+// Bit mask for the APCF Feature Selection field.
+enum ApcfFeatureSelection : 8 {
+ BROADCASTER_ADDRESS = 0x00,
+ SERVICE_DATA_CHANGE = 0x01,
+ SERVICE_UUID = 0x02,
+ SERVICE_SOLICITATION_UUID = 0x03,
+ LOCAL_NAME = 0x04,
+ MANUFACTURER_DATA = 0x05,
+ SERVICE_DATA = 0x06,
+ TRANSPORT_DISCOVERY_DATA = 0x07,
+ AD_TYPE = 0x08,
+}
+
+packet LeApcfSetFilteringParameters : LeApcf (apcf_opcode = SET_FILTERING_PARAMETERS) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_feature_selection : 16,
+ apcf_list_logic_type : 16,
+ apcf_filter_logic_type : 8,
+ rssi_high_thresh : 8,
+ delivery_mode : DeliveryMode,
+ onfound_timeout : 16,
+ onfound_timeout_cnt : 8,
+ rssi_low_thresh : 8,
+ onlost_timeout : 16,
+ num_of_tracking_entries : 16,
+}
+
+packet LeApcfSetFilteringParametersComplete : LeApcfComplete (apcf_opcode = SET_FILTERING_PARAMETERS) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+enum ApcfApplicationAddressType : 8 {
+ PUBLIC = 0x00,
+ RANDOM = 0x01,
+ NOT_APPLICABLE = 0x02,
+}
+
+packet LeApcfBroadcasterAddress : LeApcf (apcf_opcode = BROADCASTER_ADDRESS) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_broadcaster_address : Address,
+ apcf_application_address_type : ApcfApplicationAddressType,
+}
+
+packet LeApcfBroadcasterAddressComplete : LeApcfComplete (apcf_opcode = BROADCASTER_ADDRESS) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeApcfServiceUuid : LeApcf (apcf_opcode = SERVICE_UUID) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ acpf_uuid_data : 8[],
+}
+
+packet LeApcfServiceUuidComplete : LeApcfComplete (apcf_opcode = SERVICE_UUID) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeApcfSolicitationUuid : LeApcf (apcf_opcode = SERVICE_SOLICITATION_UUID) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ acpf_uuid_data : 8[],
+}
+
+packet LeApcfSolicitationUuidComplete : LeApcfComplete (apcf_opcode = SERVICE_SOLICITATION_UUID) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeApcfLocalName : LeApcf (apcf_opcode = LOCAL_NAME) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_local_name : 8[],
+}
+
+packet LeApcfLocalNameComplete : LeApcfComplete (apcf_opcode = LOCAL_NAME) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeApcfManufacturerData : LeApcf (apcf_opcode = MANUFACTURER_DATA) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_manufacturer_data : 8[],
+}
+
+packet LeApcfManufacturerDataComplete : LeApcfComplete (apcf_opcode = MANUFACTURER_DATA) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeApcfServiceData : LeApcf (apcf_opcode = SERVICE_DATA) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_service_data : 8[],
+}
+
+packet LeApcfServiceDataComplete : LeApcfComplete (apcf_opcode = SERVICE_DATA) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeApcfADType : LeApcf (apcf_opcode = AD_TYPE_FILTER) {
+ apcf_action : ApcfAction,
+ apcf_filter_index : 8,
+ apcf_ad_type_data : 8[],
+}
+
+packet LeApcfADTypeComplete : LeApcfComplete (apcf_opcode = AD_TYPE_FILTER) {
+ apcf_action : ApcfAction,
+ apcf_available_spaces : 8,
+}
+
+packet LeApcfReadExtendedFeatures : LeApcf (apcf_opcode = READ_EXTENDED_FEATURES) {
+}
+
+test LeApcfReadExtendedFeatures {
+ "\x57\xfd\x01\xff",
+}
+
+packet LeApcfReadExtendedFeaturesComplete : LeApcfComplete (apcf_opcode = READ_EXTENDED_FEATURES) {
+ transport_discovery_data_filter : 1,
+ ad_type_filter : 1,
+ _reserved_ : 14,
+}
+
+test LeApcfReadExtendedFeaturesComplete {
+ "\x0e\x07\x01\x57\xfd\x00\xff\x03\x00",
+}
+
+enum AdvertiserState : 8 {
+ ADVERTISER_FOUND = 0x0,
+ ADVERTISER_LOST = 0x1,
+}
+
+enum AdvtInfoPresent : 8 {
+ ADVT_INFO_PRESENT = 0x0,
+ ADVT_INFO_NOT_PRESENT = 0x1,
+}
+
+struct AdvtInfo {
+ tx_power : 8,
+ rssi : 8,
+ timestamp : 16,
+ _size_(adv_packet) : 8,
+ adv_packet : 8[],
+ _size_(scan_data_resp) : 8,
+ scan_data_resp : 8[],
+}
+
+packet LeAdvertisementTrackingEvent : VendorSpecificEvent (subevent_code = LE_ADVERTISEMENT_TRACKING) {
+ apcf_filter_index : 8,
+ advertiser_state : AdvertiserState,
+ advt_info_present : AdvtInfoPresent,
+ advertiser_address : Address,
+ advertiser_address_type : PeerAddressType,
+ advt_info : AdvtInfo[],
+}
+
+// -----------------------------------------------------------------------------
+// LE Get Controller Activity Energy Info Command
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le_get_controller_activity_energy_info
+// -----------------------------------------------------------------------------
+
+packet LeGetControllerActivityEnergyInfo : Command (op_code = LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO) {
+}
+
+packet LeGetControllerActivityEnergyInfoComplete : CommandComplete (command_op_code = LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO) {
+ status : ErrorCode,
+ total_tx_time_ms : 32,
+ total_rx_time_ms : 32,
+ total_idle_time_ms : 32,
+ total_energy_used : 32,
+}
+
+// -----------------------------------------------------------------------------
+// LE Extended Set Scan Parameters Command
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#le-extended-set-scan-parameters-command
+// -----------------------------------------------------------------------------
+
+enum LeExScanType : 8 {
+ PASSIVE = 0x0,
+ ACTIVE = 0x1,
+}
+
+enum LeExScanFilterPolicy : 8 {
+ ACCEPT_ALL = 0x0,
+ FILTER_ACCEPT_LIST_ONLY = 0x01,
+}
+
+packet LeExSetScanParameters : Command (op_code = LE_EX_SET_SCAN_PARAMETERS) {
+ le_ex_scan_type : LeExScanType,
+ le_ex_scan_interval : 32,
+ le_ex_scan_window : 32,
+ own_address_type : OwnAddressType,
+ le_ex_scan_filter_policy : LeExScanFilterPolicy,
+}
+
+packet LeExSetScanParametersComplete : CommandComplete (command_op_code = LE_EX_SET_SCAN_PARAMETERS) {
+ status : ErrorCode,
+}
+
+// -----------------------------------------------------------------------------
+// Get Controller Debug Info Command
+// https://source.android.com/docs/core/connect/bluetooth/hci_requirements#get-controller-debug-info-command
+// -----------------------------------------------------------------------------
+
+packet GetControllerDebugInfo : Command (op_code = GET_CONTROLLER_DEBUG_INFO) {
+}
+
+packet GetControllerDebugInfoComplete : CommandComplete (command_op_code = GET_CONTROLLER_DEBUG_INFO) {
+ status : ErrorCode,
+}
+
+packet ControllerDebugInfoEvent : VendorSpecificEvent (subevent_code = CONTROLLER_DEBUG_INFO) {
+ debug_block_byte_offset_start : 16,
+ last_block : 8,
+ _size_(debug_data) : 16,
+ debug_data : 8[],
+}
+
+// -----------------------------------------------------------------------------
+// Microsoft Commands
+// https://learn.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events
+// -----------------------------------------------------------------------------
+
+enum MsftSubcommandOpcode : 8 {
+ MSFT_READ_SUPPORTED_FEATURES = 0x00,
+ MSFT_MONITOR_RSSI = 0x01,
+ MSFT_CANCEL_MONITOR_RSSI = 0x02,
+ MSFT_LE_MONITOR_ADV = 0x03,
+ MSFT_LE_CANCEL_MONITOR_ADV = 0x04,
+ MSFT_LE_SET_ADV_FILTER_ENABLE = 0x05,
+ MSFT_READ_ABSOLUTE_RSSI = 0x06,
+}
+
+// MSFT Commands do not have a constant opcode, so leave `op_code` undefined.
+packet MsftCommand : Command {
+ subcommand_opcode: MsftSubcommandOpcode,
+ _payload_,
+}
+
+packet MsftReadSupportedFeatures : MsftCommand (subcommand_opcode = MSFT_READ_SUPPORTED_FEATURES) {}
+
+enum MsftLeMonitorAdvConditionType : 8 {
+ MSFT_CONDITION_TYPE_PATTERNS = 0x01,
+ MSFT_CONDITION_TYPE_UUID = 0x02,
+ MSFT_CONDITION_TYPE_IRK_RESOLUTION = 0x03,
+ MSFT_CONDITION_TYPE_ADDRESS = 0x04,
+}
+
+enum MsftLeMonitorAdvConditionUuidType : 8 {
+ MSFT_CONDITION_UUID_TYPE_16_BIT = 0x01,
+ MSFT_CONDITION_UUID_TYPE_32_BIT = 0x02,
+ MSFT_CONDITION_UUID_TYPE_128_BIT = 0x03,
+}
+
+packet MsftLeMonitorAdv : MsftCommand (subcommand_opcode = MSFT_LE_MONITOR_ADV) {
+ rssi_threshold_high : 8,
+ rssi_threshold_low : 8,
+ rssi_threshold_low_time_interval : 8,
+ rssi_sampling_period : 8,
+ condition_type: MsftLeMonitorAdvConditionType,
+ _payload_,
+}
+
+struct MsftLeMonitorAdvConditionPattern {
+ _size_(pattern) : 8, // including one byte for ad_type and one byte for start_of_pattern
+ ad_type: 8,
+ start_of_pattern: 8,
+ pattern: 8[+2],
+}
+
+packet MsftLeMonitorAdvConditionPatterns : MsftLeMonitorAdv (condition_type = MSFT_CONDITION_TYPE_PATTERNS) {
+ _count_(patterns): 8,
+ patterns: MsftLeMonitorAdvConditionPattern[],
+}
+
+test MsftLeMonitorAdvConditionPatterns {
+ "\x1e\xfc\x0e\x03\x10\x05\x04\xaa\x01\x01\x06\x03\x00\x80\x81\x82\x83", // 1 pattern
+ "\x70\xfd\x13\x03\x15\x04\x02\xbb\x01\x02\x04\x03\x00\x80\x81\x06\x0f\x00\x90\x91\x92\x93", // 2 patterns
+}
+
+packet MsftLeMonitorAdvConditionUuid : MsftLeMonitorAdv (condition_type = MSFT_CONDITION_TYPE_UUID) {
+ uuid_type: MsftLeMonitorAdvConditionUuidType,
+ _payload_,
+}
+
+packet MsftLeMonitorAdvConditionUuid2 : MsftLeMonitorAdvConditionUuid (uuid_type = MSFT_CONDITION_UUID_TYPE_16_BIT) {
+ uuid2: 8[2],
+}
+
+test MsftLeMonitorAdvConditionUuid2 {
+ "\x1e\xfc\x09\x03\x10\x11\x12\x13\x02\x01\x70\x71", // opcode = fc1e for Intel
+ "\x70\xfd\x09\x03\x10\x11\x12\x13\x02\x01\x70\x71", // opcode = fd70 for Qualcomm
+}
+
+packet MsftLeMonitorAdvConditionUuid4 : MsftLeMonitorAdvConditionUuid (uuid_type = MSFT_CONDITION_UUID_TYPE_32_BIT) {
+ uuid4: 8[4],
+}
+
+test MsftLeMonitorAdvConditionUuid4 {
+ "\x1e\xfc\x0b\x03\x10\x11\x12\x13\x02\x02\x70\x71\x72\x73",
+ "\x70\xfd\x0b\x03\x10\x11\x12\x13\x02\x02\x70\x71\x72\x73",
+}
+
+packet MsftLeMonitorAdvConditionUuid16 : MsftLeMonitorAdvConditionUuid (uuid_type = MSFT_CONDITION_UUID_TYPE_128_BIT) {
+ uuid16: 8[16],
+}
+
+test MsftLeMonitorAdvConditionUuid16 {
+ "\x1e\xfc\x17\x03\x10\x11\x12\x13\x02\x03\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
+ "\x70\xfd\x17\x03\x10\x11\x12\x13\x02\x03\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
+}
+
+packet MsftLeCancelMonitorAdv: MsftCommand (subcommand_opcode = MSFT_LE_CANCEL_MONITOR_ADV) {
+ monitor_handle: 8,
+}
+
+test MsftLeCancelMonitorAdv {
+ "\x1e\xfc\x02\x04\x01", // cancel handle 0x01
+}
+
+packet MsftLeSetAdvFilterEnable : MsftCommand (subcommand_opcode = MSFT_LE_SET_ADV_FILTER_ENABLE) {
+ enable: 8,
+}
+
+test MsftLeSetAdvFilterEnable {
+ "\x1e\xfc\x02\x05\x01", // disable
+ "\x70\xfd\x02\x05\x01", // enable
+}
+
+packet MsftCommandComplete : CommandComplete {
+ status: ErrorCode,
+ subcommand_opcode: MsftSubcommandOpcode,
+ _payload_,
+}
+
+packet MsftReadSupportedFeaturesCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_READ_SUPPORTED_FEATURES) {
+ supported_features: 64,
+ _size_(prefix) : 8,
+ prefix: 8[],
+}
+
+test MsftReadSupportedFeaturesCommandComplete {
+ "\x0e\x10\x01\x1e\xfc\x00\x00\x7f\x00\x00\x00\x00\x00\x00\x00\x02\x87\x80", // Msft opcode by Intel
+ "\x0e\x12\x01\x70\xfd\x00\x00\x1f\x00\x00\x00\x00\x00\x00\x00\x04\x4d\x53\x46\x54", // Msft opcode by Qualcomm
+}
+
+packet MsftLeMonitorAdvCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_LE_MONITOR_ADV) {
+ monitor_handle: 8,
+}
+
+test MsftLeMonitorAdvCommandComplete {
+ "\x0e\x06\x01\x1e\xfc\x00\x03\x05", // succeeded
+ "\x0e\x06\x01\x70\xfd\x01\x03\x06", // failed
+}
+
+packet MsftLeCancelMonitorAdvCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_LE_CANCEL_MONITOR_ADV) {}
+
+packet MsftLeSetAdvFilterEnableCommandComplete : MsftCommandComplete (subcommand_opcode = MSFT_LE_SET_ADV_FILTER_ENABLE) {}
+
+enum MsftEventCode : 8 {
+ MSFT_RSSI_EVENT = 0x01,
+ MSFT_LE_MONITOR_DEVICE_EVENT = 0x02,
+}
+
+enum MsftEventStatus : 8 {
+ MSFT_EVENT_STATUS_SUCCESS = 0x00,
+ MSFT_EVENT_STATUS_FAILURE = 0x01,
+}
+
+// It is not possible to define MSFT Event packet by deriving `Event` packet
+// because it starts with variable-length event prefix which can only be determined
+// at run-time (after receiving return of MSFT Read Supported Features).
+// Therefore we only define the payload which is located after the event prefix.
+packet MsftEventPayload {
+ msft_event_code : MsftEventCode,
+ _payload_,
+}
+
+packet MsftRssiEventPayload : MsftEventPayload (msft_event_code = MSFT_RSSI_EVENT) {
+ status: MsftEventStatus,
+ connection_handle: 16,
+ rssi: 8,
+}
+
+test MsftRssiEventPayload {
+ "\x01\x00\x01\x10\xf0", // MSFT_RSSI_EVENT succeeded
+ "\x01\x01\x02\x02\x08", // MSFT_RSSI_EVENT failed
+}
+
+packet MsftLeMonitorDeviceEventPayload : MsftEventPayload (msft_event_code = MSFT_LE_MONITOR_DEVICE_EVENT) {
+ address_type: 8,
+ bd_addr: Address,
+ monitor_handle: 8,
+ monitor_state: 8,
+}
+
+test MsftLeMonitorDeviceEventPayload {
+ "\x02\x01\x00\x01\x02\x03\x04\x05\x10\x00",
+ "\x02\x02\xf0\xf1\xf2\xf3\xf4\xf5\xaa\x02",
+}
\ No newline at end of file
diff --git a/rust/src/internal/hci/tests.rs b/rust/src/internal/hci/tests.rs
new file mode 100644
index 0000000..7962c88
--- /dev/null
+++ b/rust/src/internal/hci/tests.rs
@@ -0,0 +1,94 @@
+// Copyright 2023 Google LLC
+//
+// 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.
+
+use crate::internal::hci::{
+ packets::{Event, EventBuilder, EventCode, Sco},
+ parse_with_expected_packet_type, prepend_packet_type, Error, Packet, PacketType,
+ PacketTypeParseError, WithPacketType,
+};
+use bytes::Bytes;
+
+#[test]
+fn prepends_packet_type() {
+ let packet_type = PacketType::Event;
+ let packet_bytes = vec![0x00, 0x00, 0x00, 0x00];
+ let actual = prepend_packet_type(packet_type, packet_bytes);
+ assert_eq!(vec![0x04, 0x00, 0x00, 0x00, 0x00], actual);
+}
+
+#[test]
+fn parse_empty_slice_should_error() {
+ let actual = parse_with_expected_packet_type(FakePacket::parse, PacketType::Event, &[]);
+ assert_eq!(Err(PacketTypeParseError::EmptySlice), actual);
+}
+
+#[test]
+fn parse_invalid_packet_type_should_error() {
+ let actual = parse_with_expected_packet_type(FakePacket::parse, PacketType::Event, &[0xFF]);
+ assert_eq!(
+ Err(PacketTypeParseError::InvalidPacketType { value: 0xFF }),
+ actual
+ );
+}
+
+#[test]
+fn parse_mismatched_packet_type_should_error() {
+ let actual = parse_with_expected_packet_type(FakePacket::parse, PacketType::Acl, &[0x01]);
+ assert_eq!(
+ Err(PacketTypeParseError::PacketTypeMismatch {
+ expected: PacketType::Acl,
+ actual: PacketType::Command
+ }),
+ actual
+ );
+}
+
+#[test]
+fn parse_invalid_packet_should_error() {
+ let actual = parse_with_expected_packet_type(Sco::parse, PacketType::Sco, &[0x03]);
+ assert!(actual.is_err());
+}
+
+#[test]
+fn test_packet_roundtrip_with_type() {
+ let event_packet = EventBuilder {
+ event_code: EventCode::InquiryComplete,
+ payload: None,
+ }
+ .build();
+ let event_packet_bytes = event_packet.clone().to_vec_with_packet_type();
+ let actual =
+ parse_with_expected_packet_type(Event::parse, PacketType::Event, &event_packet_bytes)
+ .unwrap();
+ assert_eq!(event_packet, actual);
+}
+
+#[derive(Debug, PartialEq)]
+struct FakePacket;
+
+impl FakePacket {
+ fn parse(_bytes: &[u8]) -> Result<Self, Error> {
+ Ok(Self)
+ }
+}
+
+impl Packet for FakePacket {
+ fn to_bytes(self) -> Bytes {
+ Bytes::new()
+ }
+
+ fn to_vec(self) -> Vec<u8> {
+ Vec::new()
+ }
+}
diff --git a/rust/src/internal/mod.rs b/rust/src/internal/mod.rs
index f474c2d..9514ed6 100644
--- a/rust/src/internal/mod.rs
+++ b/rust/src/internal/mod.rs
@@ -18,3 +18,4 @@
//! to discover.
pub(crate) mod drivers;
+pub(crate) mod hci;
diff --git a/rust/src/wrapper/assigned_numbers/company_ids.rs b/rust/src/wrapper/assigned_numbers/company_ids.rs
index 2eebcd5..1c558b1 100644
--- a/rust/src/wrapper/assigned_numbers/company_ids.rs
+++ b/rust/src/wrapper/assigned_numbers/company_ids.rs
@@ -21,7 +21,7 @@
lazy_static! {
/// Assigned company IDs
pub static ref COMPANY_IDS: collections::HashMap<Uuid16, &'static str> = [
- (0_u16, r#"Ericsson Technology Licensing"#),
+ (0_u16, r#"Ericsson AB"#),
(1_u16, r#"Nokia Mobile Phones"#),
(2_u16, r#"Intel Corp."#),
(3_u16, r#"IBM Corp."#),
@@ -42,7 +42,7 @@
(18_u16, r#"Zeevo, Inc."#),
(19_u16, r#"Atmel Corporation"#),
(20_u16, r#"Mitsubishi Electric Corporation"#),
- (21_u16, r#"RTX Telecom A/S"#),
+ (21_u16, r#"RTX A/S"#),
(22_u16, r#"KC Technology Inc."#),
(23_u16, r#"Newlogic"#),
(24_u16, r#"Transilica, Inc."#),
@@ -58,7 +58,7 @@
(34_u16, r#"NEC Corporation"#),
(35_u16, r#"WavePlus Technology Co., Ltd."#),
(36_u16, r#"Alcatel"#),
- (37_u16, r#"NXP Semiconductors (formerly Philips Semiconductors)"#),
+ (37_u16, r#"NXP B.V."#),
(38_u16, r#"C Technologies"#),
(39_u16, r#"Open Interface"#),
(40_u16, r#"R F Micro Devices"#),
@@ -79,9 +79,9 @@
(55_u16, r#"Mobilian Corporation"#),
(56_u16, r#"Syntronix Corporation"#),
(57_u16, r#"Integrated System Solution Corp."#),
- (58_u16, r#"Panasonic Corporation (formerly Matsushita Electric Industrial Co., Ltd.)"#),
+ (58_u16, r#"Panasonic Holdings Corporation"#),
(59_u16, r#"Gennum Corporation"#),
- (60_u16, r#"BlackBerry Limited (formerly Research In Motion)"#),
+ (60_u16, r#"BlackBerry Limited"#),
(61_u16, r#"IPextreme, Inc."#),
(62_u16, r#"Systems and Chips, Inc"#),
(63_u16, r#"Bluetooth SIG, Inc"#),
@@ -122,9 +122,9 @@
(98_u16, r#"Gibson Guitars"#),
(99_u16, r#"MiCommand Inc."#),
(100_u16, r#"Band XI International, LLC"#),
- (101_u16, r#"Hewlett-Packard Company"#),
+ (101_u16, r#"HP, Inc."#),
(102_u16, r#"9Solutions Oy"#),
- (103_u16, r#"GN Netcom A/S"#),
+ (103_u16, r#"GN Audio A/S"#),
(104_u16, r#"General Motors"#),
(105_u16, r#"A&D Engineering, Inc."#),
(106_u16, r#"MindTree Ltd."#),
@@ -140,7 +140,7 @@
(116_u16, r#"Zomm, LLC"#),
(117_u16, r#"Samsung Electronics Co. Ltd."#),
(118_u16, r#"Creative Technology Ltd."#),
- (119_u16, r#"Laird Technologies"#),
+ (119_u16, r#"Laird Connectivity LLC"#),
(120_u16, r#"Nike, Inc."#),
(121_u16, r#"lesswire AG"#),
(122_u16, r#"MStar Semiconductor, Inc."#),
@@ -151,20 +151,20 @@
(127_u16, r#"Autonet Mobile"#),
(128_u16, r#"DeLorme Publishing Company, Inc."#),
(129_u16, r#"WuXi Vimicro"#),
- (130_u16, r#"Sennheiser Communications A/S"#),
+ (130_u16, r#"DSEA A/S"#),
(131_u16, r#"TimeKeeping Systems, Inc."#),
(132_u16, r#"Ludus Helsinki Ltd."#),
(133_u16, r#"BlueRadios, Inc."#),
(134_u16, r#"Equinux AG"#),
(135_u16, r#"Garmin International, Inc."#),
(136_u16, r#"Ecotest"#),
- (137_u16, r#"GN ReSound A/S"#),
+ (137_u16, r#"GN Hearing A/S"#),
(138_u16, r#"Jawbone"#),
(139_u16, r#"Topcon Positioning Systems, LLC"#),
- (140_u16, r#"Gimbal Inc. (formerly Qualcomm Labs, Inc. and Qualcomm Retail Solutions, Inc.)"#),
+ (140_u16, r#"Gimbal Inc."#),
(141_u16, r#"Zscan Software"#),
(142_u16, r#"Quintic Corp"#),
- (143_u16, r#"Telit Wireless Solutions GmbH (formerly Stollmann E+V GmbH)"#),
+ (143_u16, r#"Telit Wireless Solutions GmbH"#),
(144_u16, r#"Funai Electric Co., Ltd."#),
(145_u16, r#"Advanced PANMOBIL systems GmbH & Co. KG"#),
(146_u16, r#"ThinkOptics, Inc."#),
@@ -190,7 +190,7 @@
(166_u16, r#"Panda Ocean Inc."#),
(167_u16, r#"Visteon Corporation"#),
(168_u16, r#"ARP Devices Limited"#),
- (169_u16, r#"MARELLI EUROPE S.P.A. (formerly Magneti Marelli S.p.A.)"#),
+ (169_u16, r#"MARELLI EUROPE S.P.A."#),
(170_u16, r#"CAEN RFID srl"#),
(171_u16, r#"Ingenieur-Systemgruppe Zahn GmbH"#),
(172_u16, r#"Green Throttle Games"#),
@@ -207,7 +207,7 @@
(183_u16, r#"TreLab Ltd"#),
(184_u16, r#"Qualcomm Innovation Center, Inc. (QuIC)"#),
(185_u16, r#"Johnson Controls, Inc."#),
- (186_u16, r#"Starkey Laboratories Inc."#),
+ (186_u16, r#"Starkey Hearing Technologies"#),
(187_u16, r#"S-Power Electronics Limited"#),
(188_u16, r#"Ace Sensor Inc"#),
(189_u16, r#"Aplix Corporation"#),
@@ -227,7 +227,7 @@
(203_u16, r#"Binauric SE"#),
(204_u16, r#"Beats Electronics"#),
(205_u16, r#"Microchip Technology Inc."#),
- (206_u16, r#"Elgato Systems GmbH"#),
+ (206_u16, r#"Eve Systems GmbH"#),
(207_u16, r#"ARCHOS SA"#),
(208_u16, r#"Dexcom, Inc."#),
(209_u16, r#"Polar Electro Europe B.V."#),
@@ -240,7 +240,7 @@
(216_u16, r#"Qualcomm Connected Experiences, Inc."#),
(217_u16, r#"Voyetra Turtle Beach"#),
(218_u16, r#"txtr GmbH"#),
- (219_u16, r#"Biosentronics"#),
+ (219_u16, r#"Snuza (Pty) Ltd"#),
(220_u16, r#"Procter & Gamble"#),
(221_u16, r#"Hosiden Corporation"#),
(222_u16, r#"Muzik LLC"#),
@@ -249,13 +249,13 @@
(225_u16, r#"Danlers Ltd"#),
(226_u16, r#"Semilink Inc"#),
(227_u16, r#"inMusic Brands, Inc"#),
- (228_u16, r#"Laird Connectivity, Inc. formerly L.S. Research Inc."#),
+ (228_u16, r#"L.S. Research, Inc."#),
(229_u16, r#"Eden Software Consultants Ltd."#),
(230_u16, r#"Freshtemp"#),
(231_u16, r#"KS Technologies"#),
(232_u16, r#"ACTS Technologies"#),
(233_u16, r#"Vtrack Systems"#),
- (234_u16, r#"Nielsen-Kellerman Company"#),
+ (234_u16, r#"www.vtracksystems.com"#),
(235_u16, r#"Server Technology Inc."#),
(236_u16, r#"BioResearch Associates"#),
(237_u16, r#"Jolly Logic, LLC"#),
@@ -271,7 +271,7 @@
(247_u16, r#"VSN Technologies, Inc."#),
(248_u16, r#"AceUni Corp., Ltd."#),
(249_u16, r#"StickNFind"#),
- (250_u16, r#"Crystal Code AB"#),
+ (250_u16, r#"Crystal Alarm AB"#),
(251_u16, r#"KOUKAAM a.s."#),
(252_u16, r#"Delphi Corporation"#),
(253_u16, r#"ValenceTech Limited"#),
@@ -284,13 +284,13 @@
(260_u16, r#"PLUS Location Systems Pty Ltd"#),
(261_u16, r#"Ubiquitous Computing Technology Corporation"#),
(262_u16, r#"Innovative Yachtter Solutions"#),
- (263_u16, r#"William Demant Holding A/S"#),
+ (263_u16, r#"Demant A/S"#),
(264_u16, r#"Chicony Electronics Co., Ltd."#),
(265_u16, r#"Atus BV"#),
(266_u16, r#"Codegate Ltd"#),
(267_u16, r#"ERi, Inc"#),
(268_u16, r#"Transducers Direct, LLC"#),
- (269_u16, r#"DENSO TEN LIMITED (formerly Fujitsu Ten LImited)"#),
+ (269_u16, r#"DENSO TEN Limited"#),
(270_u16, r#"Audi AG"#),
(271_u16, r#"HiSilicon Technologies CO., LIMITED"#),
(272_u16, r#"Nippon Seiki Co., Ltd."#),
@@ -373,24 +373,24 @@
(349_u16, r#"Estimote, Inc."#),
(350_u16, r#"Unikey Technologies, Inc."#),
(351_u16, r#"Timer Cap Co."#),
- (352_u16, r#"Awox formerly AwoX"#),
+ (352_u16, r#"AwoX"#),
(353_u16, r#"yikes"#),
(354_u16, r#"MADSGlobalNZ Ltd."#),
(355_u16, r#"PCH International"#),
(356_u16, r#"Qingdao Yeelink Information Technology Co., Ltd."#),
- (357_u16, r#"Milwaukee Tool (Formally Milwaukee Electric Tools)"#),
+ (357_u16, r#"Milwaukee Electric Tools"#),
(358_u16, r#"MISHIK Pte Ltd"#),
(359_u16, r#"Ascensia Diabetes Care US Inc."#),
(360_u16, r#"Spicebox LLC"#),
(361_u16, r#"emberlight"#),
- (362_u16, r#"Cooper-Atkins Corporation"#),
+ (362_u16, r#"Emerson Digital Cold Chain, Inc."#),
(363_u16, r#"Qblinks"#),
(364_u16, r#"MYSPHERA"#),
(365_u16, r#"LifeScan Inc"#),
(366_u16, r#"Volantic AB"#),
(367_u16, r#"Podo Labs, Inc"#),
(368_u16, r#"Roche Diabetes Care AG"#),
- (369_u16, r#"Amazon.com Services, LLC (formerly Amazon Fulfillment Service)"#),
+ (369_u16, r#"Amazon.com Services LLC"#),
(370_u16, r#"Connovate Technology Private Limited"#),
(371_u16, r#"Kocomojo, LLC"#),
(372_u16, r#"Everykey Inc."#),
@@ -398,10 +398,10 @@
(374_u16, r#"SentriLock"#),
(375_u16, r#"I-SYST inc."#),
(376_u16, r#"CASIO COMPUTER CO., LTD."#),
- (377_u16, r#"LAPIS Technology Co., Ltd. formerly LAPIS Semiconductor Co., Ltd."#),
+ (377_u16, r#"LAPIS Semiconductor Co.,Ltd"#),
(378_u16, r#"Telemonitor, Inc."#),
(379_u16, r#"taskit GmbH"#),
- (380_u16, r#"Daimler AG"#),
+ (380_u16, r#"Mercedes-Benz Group AG"#),
(381_u16, r#"BatAndCat"#),
(382_u16, r#"BluDotz Ltd"#),
(383_u16, r#"XTel Wireless ApS"#),
@@ -431,7 +431,7 @@
(407_u16, r#"WiSilica Inc."#),
(408_u16, r#"VENGIT Korlatolt Felelossegu Tarsasag"#),
(409_u16, r#"SALTO SYSTEMS S.L."#),
- (410_u16, r#"TRON Forum (formerly T-Engine Forum)"#),
+ (410_u16, r#"TRON Forum"#),
(411_u16, r#"CUBETECH s.r.o."#),
(412_u16, r#"Cokiya Incorporated"#),
(413_u16, r#"CVS Health"#),
@@ -441,14 +441,14 @@
(417_u16, r#"FIAMM"#),
(418_u16, r#"GIGALANE.CO.,LTD"#),
(419_u16, r#"EROAD"#),
- (420_u16, r#"Mine Safety Appliances"#),
+ (420_u16, r#"MSA Innovation, LLC"#),
(421_u16, r#"Icon Health and Fitness"#),
- (422_u16, r#"Wille Engineering (formely as Asandoo GmbH)"#),
+ (422_u16, r#"Wille Engineering"#),
(423_u16, r#"ENERGOUS CORPORATION"#),
(424_u16, r#"Taobao"#),
(425_u16, r#"Canon Inc."#),
(426_u16, r#"Geophysical Technology Inc."#),
- (427_u16, r#"Facebook, Inc."#),
+ (427_u16, r#"Meta Platforms, Inc."#),
(428_u16, r#"Trividia Health, Inc."#),
(429_u16, r#"FlightSafety International"#),
(430_u16, r#"Earlens Corporation"#),
@@ -468,7 +468,7 @@
(444_u16, r#"SenionLab AB"#),
(445_u16, r#"Syszone Co., Ltd"#),
(446_u16, r#"Pulsate Mobile Ltd."#),
- (447_u16, r#"Hong Kong HunterSun Electronic Limited"#),
+ (447_u16, r#"Hongkong OnMicro Electronics Limited"#),
(448_u16, r#"pironex GmbH"#),
(449_u16, r#"BRADATECH Corp."#),
(450_u16, r#"Transenergooil AG"#),
@@ -498,7 +498,7 @@
(474_u16, r#"Logitech International SA"#),
(475_u16, r#"Innblue Consulting"#),
(476_u16, r#"iParking Ltd."#),
- (477_u16, r#"Koninklijke Philips Electronics N.V."#),
+ (477_u16, r#"Koninklijke Philips N.V."#),
(478_u16, r#"Minelab Electronics Pty Limited"#),
(479_u16, r#"Bison Group Ltd."#),
(480_u16, r#"Widex A/S"#),
@@ -576,7 +576,7 @@
(552_u16, r#"Twocanoes Labs, LLC"#),
(553_u16, r#"Muoverti Limited"#),
(554_u16, r#"Stamer Musikanlagen GMBH"#),
- (555_u16, r#"Tesla Motors"#),
+ (555_u16, r#"Tesla, Inc."#),
(556_u16, r#"Pharynks Corporation"#),
(557_u16, r#"Lupine"#),
(558_u16, r#"Siemens AG"#),
@@ -602,7 +602,7 @@
(578_u16, r#"16Lab Inc"#),
(579_u16, r#"Masimo Corp"#),
(580_u16, r#"Iotera Inc"#),
- (581_u16, r#"Endress+Hauser "#),
+ (581_u16, r#"Endress+Hauser"#),
(582_u16, r#"ACKme Networks, Inc."#),
(583_u16, r#"FiftyThree Inc."#),
(584_u16, r#"Parker Hannifin Corp"#),
@@ -666,7 +666,7 @@
(642_u16, r#"Sonova AG"#),
(643_u16, r#"Maven Machines, Inc."#),
(644_u16, r#"Synapse Electronics"#),
- (645_u16, r#"Standard Innovation Inc."#),
+ (645_u16, r#"WOWTech Canada Ltd."#),
(646_u16, r#"RF Code, Inc."#),
(647_u16, r#"Wally Ventures S.L."#),
(648_u16, r#"Willowbank Electronics Ltd"#),
@@ -695,10 +695,10 @@
(671_u16, r#"Areus Engineering GmbH"#),
(672_u16, r#"Impossible Camera GmbH"#),
(673_u16, r#"InventureTrack Systems"#),
- (674_u16, r#"LockedUp"#),
+ (674_u16, r#"Sera4 Ltd."#),
(675_u16, r#"Itude"#),
(676_u16, r#"Pacific Lock Company"#),
- (677_u16, r#"Tendyron Corporation ( 天地融科技股份有限公司 )"#),
+ (677_u16, r#"Tendyron Corporation"#),
(678_u16, r#"Robert Bosch GmbH"#),
(679_u16, r#"Illuxtron international B.V."#),
(680_u16, r#"miSport Ltd."#),
@@ -711,7 +711,7 @@
(687_u16, r#"Technicolor USA Inc."#),
(688_u16, r#"Bestechnic(Shanghai),Ltd"#),
(689_u16, r#"Raden Inc"#),
- (690_u16, r#"JouZen Oy"#),
+ (690_u16, r#"Oura Health Oy"#),
(691_u16, r#"CLABER S.P.A."#),
(692_u16, r#"Hyginex, Inc."#),
(693_u16, r#"HANSHIN ELECTRIC RAILWAY CO.,LTD."#),
@@ -730,10 +730,10 @@
(706_u16, r#"Guillemot Corporation"#),
(707_u16, r#"Techtronic Power Tools Technology Limited"#),
(708_u16, r#"Wilson Sporting Goods"#),
- (709_u16, r#"Lenovo (Singapore) Pte Ltd. ( 联想(新加坡) )"#),
+ (709_u16, r#"Lenovo (Singapore) Pte Ltd."#),
(710_u16, r#"Ayatan Sensors"#),
(711_u16, r#"Electronics Tomorrow Limited"#),
- (712_u16, r#"VASCO Data Security International, Inc."#),
+ (712_u16, r#"OneSpan"#),
(713_u16, r#"PayRange Inc."#),
(714_u16, r#"ABOV Semiconductor"#),
(715_u16, r#"AINA-Wireless Inc."#),
@@ -762,7 +762,7 @@
(738_u16, r#"NTT docomo"#),
(739_u16, r#"Carmanah Technologies Corp."#),
(740_u16, r#"Bytestorm Ltd."#),
- (741_u16, r#"Espressif Incorporated ( 乐鑫信息科技(上海)有限公司 )"#),
+ (741_u16, r#"Espressif Systems (Shanghai) Co., Ltd."#),
(742_u16, r#"Unwire"#),
(743_u16, r#"Connected Yard, Inc."#),
(744_u16, r#"American Music Environments"#),
@@ -770,10 +770,10 @@
(746_u16, r#"Fujitsu Limited"#),
(747_u16, r#"Ardic Technology"#),
(748_u16, r#"Delta Systems, Inc"#),
- (749_u16, r#"HTC Corporation "#),
- (750_u16, r#"Citizen Holdings Co., Ltd. "#),
+ (749_u16, r#"HTC Corporation"#),
+ (750_u16, r#"Citizen Holdings Co., Ltd."#),
(751_u16, r#"SMART-INNOVATION.inc"#),
- (752_u16, r#"Blackrat Software "#),
+ (752_u16, r#"Blackrat Software"#),
(753_u16, r#"The Idea Cave, LLC"#),
(754_u16, r#"GoPro, Inc."#),
(755_u16, r#"AuthAir, Inc"#),
@@ -782,7 +782,7 @@
(758_u16, r#"Intemo Technologies"#),
(759_u16, r#"DreamVisions co., Ltd."#),
(760_u16, r#"Runteq Oy Ltd"#),
- (761_u16, r#"IMAGINATION TECHNOLOGIES LTD "#),
+ (761_u16, r#"IMAGINATION TECHNOLOGIES LTD"#),
(762_u16, r#"CoSTAR TEchnologies"#),
(763_u16, r#"Clarius Mobile Health Corp."#),
(764_u16, r#"Shanghai Frequen Microelectronics Co., Ltd."#),
@@ -795,7 +795,7 @@
(771_u16, r#"IACA electronique"#),
(772_u16, r#"Proxy Technologies, Inc."#),
(773_u16, r#"Swipp ApS"#),
- (774_u16, r#"Life Laboratory Inc. "#),
+ (774_u16, r#"Life Laboratory Inc."#),
(775_u16, r#"FUJI INDUSTRIAL CO.,LTD."#),
(776_u16, r#"Surefire, LLC"#),
(777_u16, r#"Dolby Labs"#),
@@ -807,101 +807,101 @@
(783_u16, r#"Shortcut Labs"#),
(784_u16, r#"SGL Italia S.r.l."#),
(785_u16, r#"PEEQ DATA"#),
- (786_u16, r#"Ducere Technologies Pvt Ltd "#),
- (787_u16, r#"DiveNav, Inc. "#),
+ (786_u16, r#"Ducere Technologies Pvt Ltd"#),
+ (787_u16, r#"DiveNav, Inc."#),
(788_u16, r#"RIIG AI Sp. z o.o."#),
- (789_u16, r#"Thermo Fisher Scientific "#),
- (790_u16, r#"AG Measurematics Pvt. Ltd. "#),
- (791_u16, r#"CHUO Electronics CO., LTD. "#),
- (792_u16, r#"Aspenta International "#),
- (793_u16, r#"Eugster Frismag AG "#),
- (794_u16, r#"Amber wireless GmbH "#),
- (795_u16, r#"HQ Inc "#),
- (796_u16, r#"Lab Sensor Solutions "#),
- (797_u16, r#"Enterlab ApS "#),
+ (789_u16, r#"Thermo Fisher Scientific"#),
+ (790_u16, r#"AG Measurematics Pvt. Ltd."#),
+ (791_u16, r#"CHUO Electronics CO., LTD."#),
+ (792_u16, r#"Aspenta International"#),
+ (793_u16, r#"Eugster Frismag AG"#),
+ (794_u16, r#"Wurth Elektronik eiSos GmbH & Co. KG"#),
+ (795_u16, r#"HQ Inc"#),
+ (796_u16, r#"Lab Sensor Solutions"#),
+ (797_u16, r#"Enterlab ApS"#),
(798_u16, r#"Eyefi, Inc."#),
- (799_u16, r#"MetaSystem S.p.A. "#),
- (800_u16, r#"SONO ELECTRONICS. CO., LTD "#),
- (801_u16, r#"Jewelbots "#),
- (802_u16, r#"Compumedics Limited "#),
- (803_u16, r#"Rotor Bike Components "#),
- (804_u16, r#"Astro, Inc. "#),
- (805_u16, r#"Amotus Solutions "#),
- (806_u16, r#"Healthwear Technologies (Changzhou)Ltd "#),
- (807_u16, r#"Essex Electronics "#),
+ (799_u16, r#"MetaSystem S.p.A."#),
+ (800_u16, r#"SONO ELECTRONICS. CO., LTD"#),
+ (801_u16, r#"Jewelbots"#),
+ (802_u16, r#"Compumedics Limited"#),
+ (803_u16, r#"Rotor Bike Components"#),
+ (804_u16, r#"Astro, Inc."#),
+ (805_u16, r#"Amotus Solutions"#),
+ (806_u16, r#"Healthwear Technologies (Changzhou)Ltd"#),
+ (807_u16, r#"Essex Electronics"#),
(808_u16, r#"Grundfos A/S"#),
- (809_u16, r#"Eargo, Inc. "#),
- (810_u16, r#"Electronic Design Lab "#),
- (811_u16, r#"ESYLUX "#),
+ (809_u16, r#"Eargo, Inc."#),
+ (810_u16, r#"Electronic Design Lab"#),
+ (811_u16, r#"ESYLUX"#),
(812_u16, r#"NIPPON SMT.CO.,Ltd"#),
- (813_u16, r#"BM innovations GmbH "#),
+ (813_u16, r#"BM innovations GmbH"#),
(814_u16, r#"indoormap"#),
- (815_u16, r#"OttoQ Inc "#),
- (816_u16, r#"North Pole Engineering "#),
+ (815_u16, r#"OttoQ Inc"#),
+ (816_u16, r#"North Pole Engineering"#),
(817_u16, r#"3flares Technologies Inc."#),
- (818_u16, r#"Electrocompaniet A.S. "#),
+ (818_u16, r#"Electrocompaniet A.S."#),
(819_u16, r#"Mul-T-Lock"#),
- (820_u16, r#"Corentium AS "#),
+ (820_u16, r#"Airthings ASA"#),
(821_u16, r#"Enlighted Inc"#),
(822_u16, r#"GISTIC"#),
(823_u16, r#"AJP2 Holdings, LLC"#),
- (824_u16, r#"COBI GmbH "#),
- (825_u16, r#"Blue Sky Scientific, LLC "#),
+ (824_u16, r#"COBI GmbH"#),
+ (825_u16, r#"Blue Sky Scientific, LLC"#),
(826_u16, r#"Appception, Inc."#),
- (827_u16, r#"Courtney Thorne Limited "#),
+ (827_u16, r#"Courtney Thorne Limited"#),
(828_u16, r#"Virtuosys"#),
- (829_u16, r#"TPV Technology Limited "#),
+ (829_u16, r#"TPV Technology Limited"#),
(830_u16, r#"Monitra SA"#),
- (831_u16, r#"Automation Components, Inc. "#),
- (832_u16, r#"Letsense s.r.l. "#),
- (833_u16, r#"Etesian Technologies LLC "#),
- (834_u16, r#"GERTEC BRASIL LTDA. "#),
+ (831_u16, r#"Automation Components, Inc."#),
+ (832_u16, r#"Letsense s.r.l."#),
+ (833_u16, r#"Etesian Technologies LLC"#),
+ (834_u16, r#"GERTEC BRASIL LTDA."#),
(835_u16, r#"Drekker Development Pty. Ltd."#),
- (836_u16, r#"Whirl Inc "#),
- (837_u16, r#"Locus Positioning "#),
- (838_u16, r#"Acuity Brands Lighting, Inc "#),
- (839_u16, r#"Prevent Biometrics "#),
+ (836_u16, r#"Whirl Inc"#),
+ (837_u16, r#"Locus Positioning"#),
+ (838_u16, r#"Acuity Brands Lighting, Inc"#),
+ (839_u16, r#"Prevent Biometrics"#),
(840_u16, r#"Arioneo"#),
- (841_u16, r#"VersaMe "#),
- (842_u16, r#"Vaddio "#),
- (843_u16, r#"Libratone A/S "#),
- (844_u16, r#"HM Electronics, Inc. "#),
+ (841_u16, r#"VersaMe"#),
+ (842_u16, r#"Vaddio"#),
+ (843_u16, r#"Libratone A/S"#),
+ (844_u16, r#"HM Electronics, Inc."#),
(845_u16, r#"TASER International, Inc."#),
- (846_u16, r#"SafeTrust Inc. "#),
- (847_u16, r#"Heartland Payment Systems "#),
- (848_u16, r#"Bitstrata Systems Inc. "#),
- (849_u16, r#"Pieps GmbH "#),
+ (846_u16, r#"SafeTrust Inc."#),
+ (847_u16, r#"Heartland Payment Systems"#),
+ (848_u16, r#"Bitstrata Systems Inc."#),
+ (849_u16, r#"Pieps GmbH"#),
(850_u16, r#"iRiding(Xiamen)Technology Co.,Ltd."#),
- (851_u16, r#"Alpha Audiotronics, Inc. "#),
- (852_u16, r#"TOPPAN FORMS CO.,LTD. "#),
- (853_u16, r#"Sigma Designs, Inc. "#),
- (854_u16, r#"Spectrum Brands, Inc. "#),
- (855_u16, r#"Polymap Wireless "#),
+ (851_u16, r#"Alpha Audiotronics, Inc."#),
+ (852_u16, r#"TOPPAN FORMS CO.,LTD."#),
+ (853_u16, r#"Sigma Designs, Inc."#),
+ (854_u16, r#"Spectrum Brands, Inc."#),
+ (855_u16, r#"Polymap Wireless"#),
(856_u16, r#"MagniWare Ltd."#),
- (857_u16, r#"Novotec Medical GmbH "#),
- (858_u16, r#"Medicom Innovation Partner a/s "#),
- (859_u16, r#"Matrix Inc. "#),
- (860_u16, r#"Eaton Corporation "#),
+ (857_u16, r#"Novotec Medical GmbH"#),
+ (858_u16, r#"Phillips-Medisize A/S"#),
+ (859_u16, r#"Matrix Inc."#),
+ (860_u16, r#"Eaton Corporation"#),
(861_u16, r#"KYS"#),
- (862_u16, r#"Naya Health, Inc. "#),
- (863_u16, r#"Acromag "#),
- (864_u16, r#"Insulet Corporation "#),
- (865_u16, r#"Wellinks Inc. "#),
+ (862_u16, r#"Naya Health, Inc."#),
+ (863_u16, r#"Acromag"#),
+ (864_u16, r#"Insulet Corporation"#),
+ (865_u16, r#"Wellinks Inc."#),
(866_u16, r#"ON Semiconductor"#),
- (867_u16, r#"FREELAP SA "#),
- (868_u16, r#"Favero Electronics Srl "#),
- (869_u16, r#"BioMech Sensor LLC "#),
+ (867_u16, r#"FREELAP SA"#),
+ (868_u16, r#"Favero Electronics Srl"#),
+ (869_u16, r#"BioMech Sensor LLC"#),
(870_u16, r#"BOLTT Sports technologies Private limited"#),
- (871_u16, r#"Saphe International "#),
- (872_u16, r#"Metormote AB "#),
- (873_u16, r#"littleBits "#),
- (874_u16, r#"SetPoint Medical "#),
- (875_u16, r#"BRControls Products BV "#),
- (876_u16, r#"Zipcar "#),
- (877_u16, r#"AirBolt Pty Ltd "#),
- (878_u16, r#"KeepTruckin Inc "#),
- (879_u16, r#"Motiv, Inc. "#),
- (880_u16, r#"Wazombi Labs OÜ "#),
+ (871_u16, r#"Saphe International"#),
+ (872_u16, r#"Metormote AB"#),
+ (873_u16, r#"littleBits"#),
+ (874_u16, r#"SetPoint Medical"#),
+ (875_u16, r#"BRControls Products BV"#),
+ (876_u16, r#"Zipcar"#),
+ (877_u16, r#"AirBolt Pty Ltd"#),
+ (878_u16, r#"MOTIVE TECHNOLOGIES, INC."#),
+ (879_u16, r#"Motiv, Inc."#),
+ (880_u16, r#"Wazombi Labs OÜ"#),
(881_u16, r#"ORBCOMM"#),
(882_u16, r#"Nixie Labs, Inc."#),
(883_u16, r#"AppNearMe Ltd"#),
@@ -916,7 +916,7 @@
(892_u16, r#"Cronologics Corporation"#),
(893_u16, r#"MICRODIA Ltd."#),
(894_u16, r#"lulabytes S.L."#),
- (895_u16, r#"Société des Produits Nestlé S.A. (formerly Nestec S.A.)"#),
+ (895_u16, r#"Société des Produits Nestlé S.A."#),
(896_u16, r#"LLC "MEGA-F service""#),
(897_u16, r#"Sharp Corporation"#),
(898_u16, r#"Precision Outcomes Ltd"#),
@@ -1024,7 +1024,7 @@
(1000_u16, r#"Reiner Kartengeraete GmbH & Co. KG."#),
(1001_u16, r#"SHENZHEN LEMONJOY TECHNOLOGY CO., LTD."#),
(1002_u16, r#"Hello Inc."#),
- (1003_u16, r#"Evollve Inc."#),
+ (1003_u16, r#"Ozo Edu, Inc."#),
(1004_u16, r#"Jigowatts Inc."#),
(1005_u16, r#"BASIC MICRO.COM,INC."#),
(1006_u16, r#"CUBE TECHNOLOGIES"#),
@@ -1042,7 +1042,7 @@
(1018_u16, r#"Vyassoft Technologies Inc"#),
(1019_u16, r#"Nox Medical"#),
(1020_u16, r#"Kimberly-Clark"#),
- (1021_u16, r#"Trimble Navigation Ltd."#),
+ (1021_u16, r#"Trimble Inc."#),
(1022_u16, r#"Littelfuse"#),
(1023_u16, r#"Withings"#),
(1024_u16, r#"i-developer IT Beratung UG"#),
@@ -1054,8 +1054,8 @@
(1030_u16, r#"Airtago"#),
(1031_u16, r#"Swiss Audio SA"#),
(1032_u16, r#"ToGetHome Inc."#),
- (1033_u16, r#"AXIS"#),
- (1034_u16, r#"Openmatics"#),
+ (1033_u16, r#"RYSE INC."#),
+ (1034_u16, r#"ZF OPENMATICS s.r.o."#),
(1035_u16, r#"Jana Care Inc."#),
(1036_u16, r#"Senix Corporation"#),
(1037_u16, r#"NorthStar Battery Company, LLC"#),
@@ -1094,7 +1094,7 @@
(1070_u16, r#"MemCachier Inc."#),
(1071_u16, r#"Danfoss A/S"#),
(1072_u16, r#"SnapStyk Inc."#),
- (1073_u16, r#"Amway Corporation"#),
+ (1073_u16, r#"Alticor Inc."#),
(1074_u16, r#"Silk Labs, Inc."#),
(1075_u16, r#"Pillsy Inc."#),
(1076_u16, r#"Hatch Baby, Inc."#),
@@ -1128,7 +1128,7 @@
(1104_u16, r#"Teenage Engineering AB"#),
(1105_u16, r#"Tunstall Nordic AB"#),
(1106_u16, r#"Svep Design Center AB"#),
- (1107_u16, r#"Qorvo Utrecht B.V. formerly GreenPeak Technologies BV"#),
+ (1107_u16, r#"Qorvo Utrecht B.V."#),
(1108_u16, r#"Sphinx Electronics GmbH & Co KG"#),
(1109_u16, r#"Atomation"#),
(1110_u16, r#"Nemik Consulting Inc"#),
@@ -1204,7 +1204,7 @@
(1180_u16, r#"DyOcean"#),
(1181_u16, r#"Uhlmann & Zacher GmbH"#),
(1182_u16, r#"AND!XOR LLC"#),
- (1183_u16, r#"tictote AB"#),
+ (1183_u16, r#"Popper Pay AB"#),
(1184_u16, r#"Vypin, LLC"#),
(1185_u16, r#"PNI Sensor Corporation"#),
(1186_u16, r#"ovrEngineered, LLC"#),
@@ -1262,13 +1262,13 @@
(1238_u16, r#"LUGLOC LLC"#),
(1239_u16, r#"Blincam, Inc."#),
(1240_u16, r#"FUJIFILM Corporation"#),
- (1241_u16, r#"RandMcNally"#),
+ (1241_u16, r#"RM Acquisition LLC"#),
(1242_u16, r#"Franceschi Marina snc"#),
(1243_u16, r#"Engineered Audio, LLC."#),
(1244_u16, r#"IOTTIVE (OPC) PRIVATE LIMITED"#),
(1245_u16, r#"4MOD Technology"#),
(1246_u16, r#"Lutron Electronics Co., Inc."#),
- (1247_u16, r#"Emerson"#),
+ (1247_u16, r#"Emerson Electric Co."#),
(1248_u16, r#"Guardtec, Inc."#),
(1249_u16, r#"REACTEC LIMITED"#),
(1250_u16, r#"EllieGrid"#),
@@ -1292,7 +1292,7 @@
(1268_u16, r#"ZanCompute Inc."#),
(1269_u16, r#"Pirelli Tyre S.P.A."#),
(1270_u16, r#"McLear Limited"#),
- (1271_u16, r#"Shenzhen Huiding Technology Co.,Ltd."#),
+ (1271_u16, r#"Shenzhen Goodix Technology Co., Ltd"#),
(1272_u16, r#"Convergence Systems Limited"#),
(1273_u16, r#"Interactio"#),
(1274_u16, r#"Androtec GmbH"#),
@@ -1306,11 +1306,11 @@
(1282_u16, r#"Specifi-Kali LLC"#),
(1283_u16, r#"Locoroll, Inc"#),
(1284_u16, r#"PHYPLUS Inc"#),
- (1285_u16, r#"Inplay Technologies LLC"#),
+ (1285_u16, r#"InPlay, Inc."#),
(1286_u16, r#"Hager"#),
(1287_u16, r#"Yellowcog"#),
(1288_u16, r#"Axes System sp. z o. o."#),
- (1289_u16, r#"myLIFTER Inc."#),
+ (1289_u16, r#"Garage Smart, Inc."#),
(1290_u16, r#"Shake-on B.V."#),
(1291_u16, r#"Vibrissa Inc."#),
(1292_u16, r#"OSRAM GmbH"#),
@@ -1324,7 +1324,7 @@
(1300_u16, r#"FIBRO GmbH"#),
(1301_u16, r#"RB Controls Co., Ltd."#),
(1302_u16, r#"Footmarks"#),
- (1303_u16, r#"Amtronic Sverige AB (formerly Amcore AB)"#),
+ (1303_u16, r#"Amtronic Sverige AB"#),
(1304_u16, r#"MAMORIO.inc"#),
(1305_u16, r#"Tyto Life LLC"#),
(1306_u16, r#"Leica Camera AG"#),
@@ -1372,7 +1372,7 @@
(1348_u16, r#"OrthoSensor, Inc."#),
(1349_u16, r#"Candy Hoover Group s.r.l"#),
(1350_u16, r#"Apexar Technologies S.A."#),
- (1351_u16, r#"LOGICDATA d.o.o."#),
+ (1351_u16, r#"LOGICDATA Electronic & Software Entwicklungs GmbH"#),
(1352_u16, r#"Knick Elektronische Messgeraete GmbH & Co. KG"#),
(1353_u16, r#"Smart Technologies and Investment Limited"#),
(1354_u16, r#"Linough Inc."#),
@@ -1397,7 +1397,7 @@
(1373_u16, r#"Valve Corporation"#),
(1374_u16, r#"Hekatron Vertriebs GmbH"#),
(1375_u16, r#"PROTECH S.A.S. DI GIRARDI ANDREA & C."#),
- (1376_u16, r#"Sarita CareTech APS (formerly Sarita CareTech IVS)"#),
+ (1376_u16, r#"Sarita CareTech APS"#),
(1377_u16, r#"Finder S.p.A."#),
(1378_u16, r#"Thalmic Labs Inc."#),
(1379_u16, r#"Steinel Vertrieb GmbH"#),
@@ -1441,9 +1441,9 @@
(1417_u16, r#"Star Technologies"#),
(1418_u16, r#"START TODAY CO.,LTD."#),
(1419_u16, r#"Maxim Integrated Products"#),
- (1420_u16, r#"MERCK Kommanditgesellschaft auf Aktien"#),
+ (1420_u16, r#"Fracarro Radioindustrie SRL"#),
(1421_u16, r#"Jungheinrich Aktiengesellschaft"#),
- (1422_u16, r#"Oculus VR, LLC"#),
+ (1422_u16, r#"Meta Platforms Technologies, LLC"#),
(1423_u16, r#"HENDON SEMICONDUCTORS PTY LTD"#),
(1424_u16, r#"Pur3 Ltd"#),
(1425_u16, r#"Viasat Group S.p.A."#),
@@ -1476,7 +1476,7 @@
(1452_u16, r#"GoerTek Dynaudio Co., Ltd."#),
(1453_u16, r#"INIA"#),
(1454_u16, r#"CARMATE MFG.CO.,LTD"#),
- (1455_u16, r#"OV LOOP, INC. (formerly ONvocal)"#),
+ (1455_u16, r#"OV LOOP, INC."#),
(1456_u16, r#"NewTec GmbH"#),
(1457_u16, r#"Medallion Instrumentation Systems"#),
(1458_u16, r#"CAREL INDUSTRIES S.P.A."#),
@@ -1539,7 +1539,7 @@
(1515_u16, r#"Bayerische Motoren Werke AG"#),
(1516_u16, r#"Gycom Svenska AB"#),
(1517_u16, r#"Fuji Xerox Co., Ltd"#),
- (1518_u16, r#"Glide Inc."#),
+ (1518_u16, r#"Wristcam Inc."#),
(1519_u16, r#"SIKOM AS"#),
(1520_u16, r#"beken"#),
(1521_u16, r#"The Linux Foundation"#),
@@ -1572,7 +1572,7 @@
(1548_u16, r#"Vuzix Corporation"#),
(1549_u16, r#"TDK Corporation"#),
(1550_u16, r#"Blueair AB"#),
- (1551_u16, r#"Signify Netherlands"#),
+ (1551_u16, r#"Signify Netherlands B.V."#),
(1552_u16, r#"ADH GUARDIAN USA LLC"#),
(1553_u16, r#"Beurer GmbH"#),
(1554_u16, r#"Playfinity AS"#),
@@ -1621,7 +1621,7 @@
(1597_u16, r#"Xradio Technology Co.,Ltd."#),
(1598_u16, r#"The Indoor Lab, LLC"#),
(1599_u16, r#"LDL TECHNOLOGY"#),
- (1600_u16, r#"Parkifi"#),
+ (1600_u16, r#"Dish Network LLC"#),
(1601_u16, r#"Revenue Collection Systems FRANCE SAS"#),
(1602_u16, r#"Bluetrum Technology Co.,Ltd"#),
(1603_u16, r#"makita corporation"#),
@@ -1711,7 +1711,7 @@
(1687_u16, r#"Stemco Products Inc"#),
(1688_u16, r#"Wood IT Security, LLC"#),
(1689_u16, r#"RandomLab SAS"#),
- (1690_u16, r#"Adero, Inc. (formerly as TrackR, Inc.)"#),
+ (1690_u16, r#"Adero, Inc."#),
(1691_u16, r#"Dragonchip Limited"#),
(1692_u16, r#"Noomi AB"#),
(1693_u16, r#"Vakaros LLC"#),
@@ -1721,7 +1721,7 @@
(1697_u16, r#"Cardo Systems, Ltd"#),
(1698_u16, r#"Globalworx GmbH"#),
(1699_u16, r#"Nymbus, LLC"#),
- (1700_u16, r#"Sanyo Techno Solutions Tottori Co., Ltd."#),
+ (1700_u16, r#"LIMNO Co. Ltd."#),
(1701_u16, r#"TEKZITEL PTY LTD"#),
(1702_u16, r#"Roambee Corporation"#),
(1703_u16, r#"Chipsea Technologies (ShenZhen) Corp."#),
@@ -1780,8 +1780,8 @@
(1756_u16, r#"Industrial Network Controls, LLC"#),
(1757_u16, r#"Intellithings Ltd."#),
(1758_u16, r#"Navcast, Inc."#),
- (1759_u16, r#"Hubbell Lighting, Inc."#),
- (1760_u16, r#"Avaya "#),
+ (1759_u16, r#"HLI Solutions Inc."#),
+ (1760_u16, r#"Avaya Inc."#),
(1761_u16, r#"Milestone AV Technologies LLC"#),
(1762_u16, r#"Alango Technologies Ltd"#),
(1763_u16, r#"Spinlock Ltd"#),
@@ -1801,7 +1801,7 @@
(1777_u16, r#"Shibutani Co., Ltd."#),
(1778_u16, r#"Trapper Data AB"#),
(1779_u16, r#"Alfred International Inc."#),
- (1780_u16, r#"Near Field Solutions Ltd"#),
+ (1780_u16, r#"Touché Technology Ltd"#),
(1781_u16, r#"Vigil Technologies Inc."#),
(1782_u16, r#"Vitulo Plus BV"#),
(1783_u16, r#"WILKA Schliesstechnik GmbH"#),
@@ -1834,11 +1834,11 @@
(1810_u16, r#"Bull Group Company Limited"#),
(1811_u16, r#"Respiri Limited"#),
(1812_u16, r#"MindPeace Safety LLC"#),
- (1813_u16, r#"Vgyan Solutions"#),
+ (1813_u16, r#"MBARC LABS Inc"#),
(1814_u16, r#"Altonics"#),
(1815_u16, r#"iQsquare BV"#),
(1816_u16, r#"IDIBAIX enginneering"#),
- (1817_u16, r#"ECSG"#),
+ (1817_u16, r#"COREIOT PTY LTD"#),
(1818_u16, r#"REVSMART WEARABLE HK CO LTD"#),
(1819_u16, r#"Precor"#),
(1820_u16, r#"F5 Sports, Inc"#),
@@ -1867,7 +1867,7 @@
(1843_u16, r#"Iguanavation, Inc."#),
(1844_u16, r#"DiUS Computing Pty Ltd"#),
(1845_u16, r#"UpRight Technologies LTD"#),
- (1846_u16, r#"FrancisFund, LLC"#),
+ (1846_u16, r#"Luna XIO, Inc."#),
(1847_u16, r#"LLC Navitek"#),
(1848_u16, r#"Glass Security Pte Ltd"#),
(1849_u16, r#"Jiangsu Qinheng Co., Ltd."#),
@@ -1950,6 +1950,7 @@
(1926_u16, r#"HLP Controls Pty Limited"#),
(1927_u16, r#"Pangaea Solution"#),
(1928_u16, r#"BubblyNet, LLC"#),
+ (1929_u16, r#"PCB Piezotronics, Inc."#),
(1930_u16, r#"The Wildflower Foundation"#),
(1931_u16, r#"Optikam Tech Inc."#),
(1932_u16, r#"MINIBREW HOLDING B.V"#),
@@ -1958,7 +1959,7 @@
(1935_u16, r#"Hanna Instruments, Inc."#),
(1936_u16, r#"KOMPAN A/S"#),
(1937_u16, r#"Scosche Industries, Inc."#),
- (1938_u16, r#"Provo Craft"#),
+ (1938_u16, r#"Cricut, Inc."#),
(1939_u16, r#"AEV spol. s r.o."#),
(1940_u16, r#"The Coca-Cola Company"#),
(1941_u16, r#"GASTEC CORPORATION"#),
@@ -2032,17 +2033,17 @@
(2009_u16, r#"Micro-Design, Inc."#),
(2010_u16, r#"STARLITE Co., Ltd."#),
(2011_u16, r#"Remedee Labs"#),
- (2012_u16, r#"ThingOS GmbH"#),
+ (2012_u16, r#"ThingOS GmbH & Co KG"#),
(2013_u16, r#"Linear Circuits"#),
(2014_u16, r#"Unlimited Engineering SL"#),
(2015_u16, r#"Snap-on Incorporated"#),
(2016_u16, r#"Edifier International Limited"#),
(2017_u16, r#"Lucie Labs"#),
(2018_u16, r#"Alfred Kaercher SE & Co. KG"#),
- (2019_u16, r#"Audiowise Technology Inc."#),
+ (2019_u16, r#"Airoha Technology Corp."#),
(2020_u16, r#"Geeksme S.L."#),
(2021_u16, r#"Minut, Inc."#),
- (2022_u16, r#"Autogrow Systems Limited"#),
+ (2022_u16, r#"Waybeyond Limited"#),
(2023_u16, r#"Komfort IQ, Inc."#),
(2024_u16, r#"Packetcraft, Inc."#),
(2025_u16, r#"Häfele GmbH & Co KG"#),
@@ -2053,7 +2054,7 @@
(2030_u16, r#"KidzTek LLC"#),
(2031_u16, r#"Aktiebolaget Sandvik Coromant"#),
(2032_u16, r#"e-moola.com Pty Ltd"#),
- (2033_u16, r#"GSM Innovations Pty Ltd"#),
+ (2033_u16, r#"Zimi Innovations Pty Ltd"#),
(2034_u16, r#"SERENE GROUP, INC"#),
(2035_u16, r#"DIGISINE ENERGYTECH CO. LTD."#),
(2036_u16, r#"MEDIRLAB Orvosbiologiai Fejleszto Korlatolt Felelossegu Tarsasag"#),
@@ -2109,7 +2110,7 @@
(2086_u16, r#"Hyundai Motor Company"#),
(2087_u16, r#"Kickmaker"#),
(2088_u16, r#"Shanghai Suisheng Information Technology Co., Ltd."#),
- (2089_u16, r#"HEXAGON"#),
+ (2089_u16, r#"HEXAGON METROLOGY DIVISION ROMER"#),
(2090_u16, r#"Mitutoyo Corporation"#),
(2091_u16, r#"shenzhen fitcare electronics Co.,Ltd"#),
(2092_u16, r#"INGICS TECHNOLOGY CO., LTD."#),
@@ -2153,20 +2154,20 @@
(2130_u16, r#"Cognosos, Inc."#),
(2131_u16, r#"Pektron Group Limited"#),
(2132_u16, r#"Tap Sound System"#),
- (2133_u16, r#"Helios Hockey, Inc."#),
+ (2133_u16, r#"Helios Sports, Inc."#),
(2134_u16, r#"Canopy Growth Corporation"#),
(2135_u16, r#"Parsyl Inc"#),
(2136_u16, r#"SOUNDBOKS"#),
(2137_u16, r#"BlueUp"#),
(2138_u16, r#"DAKATECH"#),
- (2139_u16, r#"RICOH ELECTRONIC DEVICES CO., LTD."#),
+ (2139_u16, r#"Nisshinbo Micro Devices Inc."#),
(2140_u16, r#"ACOS CO.,LTD."#),
(2141_u16, r#"Guilin Zhishen Information Technology Co.,Ltd."#),
(2142_u16, r#"Krog Systems LLC"#),
(2143_u16, r#"COMPEGPS TEAM,SOCIEDAD LIMITADA"#),
(2144_u16, r#"Alflex Products B.V."#),
(2145_u16, r#"SmartSensor Labs Ltd"#),
- (2146_u16, r#"SmartDrive Inc."#),
+ (2146_u16, r#"SmartDrive"#),
(2147_u16, r#"Yo-tronics Technology Co., Ltd."#),
(2148_u16, r#"Rafaelmicro"#),
(2149_u16, r#"Emergency Lighting Products Limited"#),
@@ -2181,7 +2182,7 @@
(2158_u16, r#"Vorwerk Elektrowerke GmbH & Co. KG"#),
(2159_u16, r#"Trackunit A/S"#),
(2160_u16, r#"Wyze Labs, Inc"#),
- (2161_u16, r#"Dension Elektronikai Kft. (formerly: Dension Audio Systems Ltd.)"#),
+ (2161_u16, r#"Dension Elektronikai Kft."#),
(2162_u16, r#"11 Health & Technologies Limited"#),
(2163_u16, r#"Innophase Incorporated"#),
(2164_u16, r#"Treegreen Limited"#),
@@ -2201,8 +2202,8 @@
(2178_u16, r#"PSYONIC, Inc."#),
(2179_u16, r#"Wintersteiger AG"#),
(2180_u16, r#"Controlid Industria, Comercio de Hardware e Servicos de Tecnologia Ltda"#),
- (2181_u16, r#"LEVOLOR, INC."#),
- (2182_u16, r#"Xsens Technologies B.V."#),
+ (2181_u16, r#"LEVOLOR INC"#),
+ (2182_u16, r#"Movella Technologies B.V."#),
(2183_u16, r#"Hydro-Gear Limited Partnership"#),
(2184_u16, r#"EnPointe Fencing Pty Ltd"#),
(2185_u16, r#"XANTHIO"#),
@@ -2290,7 +2291,7 @@
(2267_u16, r#"Tertium Technology"#),
(2268_u16, r#"SHENZHEN AUKEY E BUSINESS CO., LTD"#),
(2269_u16, r#"code-Q"#),
- (2270_u16, r#"Tyco Electronics Corporation a TE Connectivity Ltd Company"#),
+ (2270_u16, r#"TE Connectivity Corporation"#),
(2271_u16, r#"IRIS OHYAMA CO.,LTD."#),
(2272_u16, r#"Philia Technology"#),
(2273_u16, r#"KOZO KEIKAKU ENGINEERING Inc."#),
@@ -2299,14 +2300,14 @@
(2276_u16, r#"Rashidov ltd"#),
(2277_u16, r#"Crowd Connected Ltd"#),
(2278_u16, r#"Eneso Tecnologia de Adaptacion S.L."#),
- (2279_u16, r#"Barrot Technology Limited"#),
+ (2279_u16, r#"Barrot Technology Co.,Ltd."#),
(2280_u16, r#"Naonext"#),
(2281_u16, r#"Taiwan Intelligent Home Corp."#),
(2282_u16, r#"COWBELL ENGINEERING CO.,LTD."#),
(2283_u16, r#"Beijing Big Moment Technology Co., Ltd."#),
(2284_u16, r#"Denso Corporation"#),
(2285_u16, r#"IMI Hydronic Engineering International SA"#),
- (2286_u16, r#"ASKEY"#),
+ (2286_u16, r#"Askey Computer Corp."#),
(2287_u16, r#"Cumulus Digital Systems, Inc"#),
(2288_u16, r#"Joovv, Inc."#),
(2289_u16, r#"The L.S. Starrett Company"#),
@@ -2391,7 +2392,7 @@
(2368_u16, r#"Hero Workout GmbH"#),
(2369_u16, r#"Rivian Automotive, LLC"#),
(2370_u16, r#"TRANSSION HOLDINGS LIMITED"#),
- (2371_u16, r#"Inovonics Corp."#),
+ (2371_u16, r#"Reserved"#),
(2372_u16, r#"Agitron d.o.o."#),
(2373_u16, r#"Globe (Jiangsu) Co., Ltd"#),
(2374_u16, r#"AMC International Alfa Metalcraft Corporation AG"#),
@@ -2417,7 +2418,7 @@
(2394_u16, r#"Selekt Bilgisayar, lletisim Urunleri lnsaat Sanayi ve Ticaret Limited Sirketi"#),
(2395_u16, r#"Lismore Instruments Limited"#),
(2396_u16, r#"LogiLube, LLC"#),
- (2397_u16, r#"ETC"#),
+ (2397_u16, r#"Electronic Theatre Controls"#),
(2398_u16, r#"BioEchoNet inc."#),
(2399_u16, r#"NUANCE HEARING LTD"#),
(2400_u16, r#"Sena Technologies Inc."#),
@@ -2492,7 +2493,7 @@
(2469_u16, r#"Security Enhancement Systems, LLC"#),
(2470_u16, r#"BEIJING ELECTRIC VEHICLE CO.,LTD"#),
(2471_u16, r#"Paybuddy ApS"#),
- (2472_u16, r#"KHN Solutions Inc"#),
+ (2472_u16, r#"KHN Solutions LLC"#),
(2473_u16, r#"Nippon Ceramic Co.,Ltd."#),
(2474_u16, r#"PHOTODYNAMIC INCORPORATED"#),
(2475_u16, r#"DashLogic, Inc."#),
@@ -2620,7 +2621,7 @@
(2597_u16, r#"Eran Financial Services LLC"#),
(2598_u16, r#"Louis Vuitton"#),
(2599_u16, r#"AYU DEVICES PRIVATE LIMITED"#),
- (2600_u16, r#"NanoFlex"#),
+ (2600_u16, r#"NanoFlex Power Corporation"#),
(2601_u16, r#"Worthcloud Technology Co.,Ltd"#),
(2602_u16, r#"Yamaha Corporation"#),
(2603_u16, r#"PaceBait IVS"#),
@@ -2699,15 +2700,654 @@
(2676_u16, r#"MICROSON S.A."#),
(2677_u16, r#"Delta Cycle Corporation"#),
(2678_u16, r#"Synaptics Incorporated"#),
- (2679_u16, r#"JMD PACIFIC PTE. LTD."#),
+ (2679_u16, r#"AXTRO PTE. LTD."#),
(2680_u16, r#"Shenzhen Sunricher Technology Limited"#),
(2681_u16, r#"Webasto SE"#),
(2682_u16, r#"Emlid Limited"#),
(2683_u16, r#"UniqAir Oy"#),
(2684_u16, r#"WAFERLOCK"#),
(2685_u16, r#"Freedman Electronics Pty Ltd"#),
- (2686_u16, r#"Keba AG"#),
+ (2686_u16, r#"KEBA Handover Automation GmbH"#),
(2687_u16, r#"Intuity Medical"#),
+ (2688_u16, r#"Cleer Limited"#),
+ (2689_u16, r#"Universal Biosensors Pty Ltd"#),
+ (2690_u16, r#"Corsair"#),
+ (2691_u16, r#"Rivata, Inc."#),
+ (2692_u16, r#"Greennote Inc,"#),
+ (2693_u16, r#"Snowball Technology Co., Ltd."#),
+ (2694_u16, r#"ALIZENT International"#),
+ (2695_u16, r#"Shanghai Smart System Technology Co., Ltd"#),
+ (2696_u16, r#"PSA Peugeot Citroen"#),
+ (2697_u16, r#"SES-Imagotag"#),
+ (2698_u16, r#"HAINBUCH GMBH SPANNENDE TECHNIK"#),
+ (2699_u16, r#"SANlight GmbH"#),
+ (2700_u16, r#"DelpSys, s.r.o."#),
+ (2701_u16, r#"JCM TECHNOLOGIES S.A."#),
+ (2702_u16, r#"Perfect Company"#),
+ (2703_u16, r#"TOTO LTD."#),
+ (2704_u16, r#"Shenzhen Grandsun Electronic Co.,Ltd."#),
+ (2705_u16, r#"Monarch International Inc."#),
+ (2706_u16, r#"Carestream Dental LLC"#),
+ (2707_u16, r#"GiPStech S.r.l."#),
+ (2708_u16, r#"OOBIK Inc."#),
+ (2709_u16, r#"Pamex Inc."#),
+ (2710_u16, r#"Lightricity Ltd"#),
+ (2711_u16, r#"SensTek"#),
+ (2712_u16, r#"Foil, Inc."#),
+ (2713_u16, r#"Shanghai high-flying electronics technology Co.,Ltd"#),
+ (2714_u16, r#"TEMKIN ASSOCIATES, LLC"#),
+ (2715_u16, r#"Eello LLC"#),
+ (2716_u16, r#"Xi'an Fengyu Information Technology Co., Ltd."#),
+ (2717_u16, r#"Canon Finetech Nisca Inc."#),
+ (2718_u16, r#"LifePlus, Inc."#),
+ (2719_u16, r#"ista International GmbH"#),
+ (2720_u16, r#"Loy Tec electronics GmbH"#),
+ (2721_u16, r#"LINCOGN TECHNOLOGY CO. LIMITED"#),
+ (2722_u16, r#"Care Bloom, LLC"#),
+ (2723_u16, r#"DIC Corporation"#),
+ (2724_u16, r#"FAZEPRO LLC"#),
+ (2725_u16, r#"Shenzhen Uascent Technology Co., Ltd"#),
+ (2726_u16, r#"Realityworks, inc."#),
+ (2727_u16, r#"Urbanista AB"#),
+ (2728_u16, r#"Zencontrol Pty Ltd"#),
+ (2729_u16, r#"Spintly, Inc."#),
+ (2730_u16, r#"Computime International Ltd"#),
+ (2731_u16, r#"Anhui Listenai Co"#),
+ (2732_u16, r#"OSM HK Limited"#),
+ (2733_u16, r#"Adevo Consulting AB"#),
+ (2734_u16, r#"PS Engineering, Inc."#),
+ (2735_u16, r#"AIAIAI ApS"#),
+ (2736_u16, r#"Visiontronic s.r.o."#),
+ (2737_u16, r#"InVue Security Products Inc"#),
+ (2738_u16, r#"TouchTronics, Inc."#),
+ (2739_u16, r#"INNER RANGE PTY. LTD."#),
+ (2740_u16, r#"Ellenby Technologies, Inc."#),
+ (2741_u16, r#"Elstat Electronics Ltd."#),
+ (2742_u16, r#"Xenter, Inc."#),
+ (2743_u16, r#"LogTag North America Inc."#),
+ (2744_u16, r#"Sens.ai Incorporated"#),
+ (2745_u16, r#"STL"#),
+ (2746_u16, r#"Open Bionics Ltd."#),
+ (2747_u16, r#"R-DAS, s.r.o."#),
+ (2748_u16, r#"KCCS Mobile Engineering Co., Ltd."#),
+ (2749_u16, r#"Inventas AS"#),
+ (2750_u16, r#"Robkoo Information & Technologies Co., Ltd."#),
+ (2751_u16, r#"PAUL HARTMANN AG"#),
+ (2752_u16, r#"Omni-ID USA, INC."#),
+ (2753_u16, r#"Shenzhen Jingxun Technology Co., Ltd."#),
+ (2754_u16, r#"RealMega Microelectronics technology (Shanghai) Co. Ltd."#),
+ (2755_u16, r#"Kenzen, Inc."#),
+ (2756_u16, r#"CODIUM"#),
+ (2757_u16, r#"Flexoptix GmbH"#),
+ (2758_u16, r#"Barnes Group Inc."#),
+ (2759_u16, r#"Chengdu Aich Technology Co.,Ltd"#),
+ (2760_u16, r#"Keepin Co., Ltd."#),
+ (2761_u16, r#"Swedlock AB"#),
+ (2762_u16, r#"Shenzhen CoolKit Technology Co., Ltd"#),
+ (2763_u16, r#"ise Individuelle Software und Elektronik GmbH"#),
+ (2764_u16, r#"Nuvoton"#),
+ (2765_u16, r#"Visuallex Sport International Limited"#),
+ (2766_u16, r#"KOBATA GAUGE MFG. CO., LTD."#),
+ (2767_u16, r#"CACI Technologies"#),
+ (2768_u16, r#"Nordic Strong ApS"#),
+ (2769_u16, r#"EAGLE KINGDOM TECHNOLOGIES LIMITED"#),
+ (2770_u16, r#"Lautsprecher Teufel GmbH"#),
+ (2771_u16, r#"SSV Software Systems GmbH"#),
+ (2772_u16, r#"Zhuhai Pantum Electronisc Co., Ltd"#),
+ (2773_u16, r#"Streamit B.V."#),
+ (2774_u16, r#"nymea GmbH"#),
+ (2775_u16, r#"AL-KO Geraete GmbH"#),
+ (2776_u16, r#"Franz Kaldewei GmbH&Co KG"#),
+ (2777_u16, r#"Shenzhen Aimore. Co.,Ltd"#),
+ (2778_u16, r#"Codefabrik GmbH"#),
+ (2779_u16, r#"Reelables, Inc."#),
+ (2780_u16, r#"Duravit AG"#),
+ (2781_u16, r#"Boss Audio"#),
+ (2782_u16, r#"Vocera Communications, Inc."#),
+ (2783_u16, r#"Douglas Dynamics L.L.C."#),
+ (2784_u16, r#"Viceroy Devices Corporation"#),
+ (2785_u16, r#"ChengDu ForThink Technology Co., Ltd."#),
+ (2786_u16, r#"IMATRIX SYSTEMS, INC."#),
+ (2787_u16, r#"GlobalMed"#),
+ (2788_u16, r#"DALI Alliance"#),
+ (2789_u16, r#"unu GmbH"#),
+ (2790_u16, r#"Hexology"#),
+ (2791_u16, r#"Sunplus Technology Co., Ltd."#),
+ (2792_u16, r#"LEVEL, s.r.o."#),
+ (2793_u16, r#"FLIR Systems AB"#),
+ (2794_u16, r#"Borda Technology"#),
+ (2795_u16, r#"Square, Inc."#),
+ (2796_u16, r#"FUTEK ADVANCED SENSOR TECHNOLOGY, INC"#),
+ (2797_u16, r#"Saxonar GmbH"#),
+ (2798_u16, r#"Velentium, LLC"#),
+ (2799_u16, r#"GLP German Light Products GmbH"#),
+ (2800_u16, r#"Leupold & Stevens, Inc."#),
+ (2801_u16, r#"CRADERS,CO.,LTD"#),
+ (2802_u16, r#"Shanghai All Link Microelectronics Co.,Ltd"#),
+ (2803_u16, r#"701x Inc."#),
+ (2804_u16, r#"Radioworks Microelectronics PTY LTD"#),
+ (2805_u16, r#"Unitech Electronic Inc."#),
+ (2806_u16, r#"AMETEK, Inc."#),
+ (2807_u16, r#"Irdeto"#),
+ (2808_u16, r#"First Design System Inc."#),
+ (2809_u16, r#"Unisto AG"#),
+ (2810_u16, r#"Chengdu Ambit Technology Co., Ltd."#),
+ (2811_u16, r#"SMT ELEKTRONIK GmbH"#),
+ (2812_u16, r#"Cerebrum Sensor Technologies Inc."#),
+ (2813_u16, r#"Weber Sensors, LLC"#),
+ (2814_u16, r#"Earda Technologies Co.,Ltd"#),
+ (2815_u16, r#"FUSEAWARE LIMITED"#),
+ (2816_u16, r#"Flaircomm Microelectronics Inc."#),
+ (2817_u16, r#"RESIDEO TECHNOLOGIES, INC."#),
+ (2818_u16, r#"IORA Technology Development Ltd. Sti."#),
+ (2819_u16, r#"Precision Triathlon Systems Limited"#),
+ (2820_u16, r#"I-PERCUT"#),
+ (2821_u16, r#"Marquardt GmbH"#),
+ (2822_u16, r#"FAZUA GmbH"#),
+ (2823_u16, r#"Workaround Gmbh"#),
+ (2824_u16, r#"Shenzhen Qianfenyi Intelligent Technology Co., LTD"#),
+ (2825_u16, r#"soonisys"#),
+ (2826_u16, r#"Belun Technology Company Limited"#),
+ (2827_u16, r#"Sanistaal A/S"#),
+ (2828_u16, r#"BluPeak"#),
+ (2829_u16, r#"SANYO DENKO Co.,Ltd."#),
+ (2830_u16, r#"Honda Lock Mfg. Co.,Ltd."#),
+ (2831_u16, r#"B.E.A. S.A."#),
+ (2832_u16, r#"Alfa Laval Corporate AB"#),
+ (2833_u16, r#"ThermoWorks, Inc."#),
+ (2834_u16, r#"ToughBuilt Industries LLC"#),
+ (2835_u16, r#"IOTOOLS"#),
+ (2836_u16, r#"Olumee"#),
+ (2837_u16, r#"NAOS JAPAN K.K."#),
+ (2838_u16, r#"Guard RFID Solutions Inc."#),
+ (2839_u16, r#"SIG SAUER, INC."#),
+ (2840_u16, r#"DECATHLON SE"#),
+ (2841_u16, r#"WBS PROJECT H PTY LTD"#),
+ (2842_u16, r#"Roca Sanitario, S.A."#),
+ (2843_u16, r#"Enerpac Tool Group Corp."#),
+ (2844_u16, r#"Nanoleq AG"#),
+ (2845_u16, r#"Accelerated Systems"#),
+ (2846_u16, r#"PB INC."#),
+ (2847_u16, r#"Beijing ESWIN Computing Technology Co., Ltd."#),
+ (2848_u16, r#"TKH Security B.V."#),
+ (2849_u16, r#"ams AG"#),
+ (2850_u16, r#"Hygiene IQ, LLC."#),
+ (2851_u16, r#"iRhythm Technologies, Inc."#),
+ (2852_u16, r#"BeiJing ZiJie TiaoDong KeJi Co.,Ltd."#),
+ (2853_u16, r#"NIBROTECH LTD"#),
+ (2854_u16, r#"Baracoda Daily Healthtech."#),
+ (2855_u16, r#"Lumi United Technology Co., Ltd"#),
+ (2856_u16, r#"CHACON"#),
+ (2857_u16, r#"Tech-Venom Entertainment Private Limited"#),
+ (2858_u16, r#"ACL Airshop B.V."#),
+ (2859_u16, r#"MAINBOT"#),
+ (2860_u16, r#"ILLUMAGEAR, Inc."#),
+ (2861_u16, r#"REDARC ELECTRONICS PTY LTD"#),
+ (2862_u16, r#"MOCA System Inc."#),
+ (2863_u16, r#"Duke Manufacturing Co"#),
+ (2864_u16, r#"ART SPA"#),
+ (2865_u16, r#"Silver Wolf Vehicles Inc."#),
+ (2866_u16, r#"Hala Systems, Inc."#),
+ (2867_u16, r#"ARMATURA LLC"#),
+ (2868_u16, r#"CONZUMEX INDUSTRIES PRIVATE LIMITED"#),
+ (2869_u16, r#"BH SENS"#),
+ (2870_u16, r#"SINTEF"#),
+ (2871_u16, r#"Omnivoltaic Energy Solutions Limited Company"#),
+ (2872_u16, r#"WISYCOM S.R.L."#),
+ (2873_u16, r#"Red 100 Lighting Co., ltd."#),
+ (2874_u16, r#"Impact Biosystems, Inc."#),
+ (2875_u16, r#"AIC semiconductor (Shanghai) Co., Ltd."#),
+ (2876_u16, r#"Dodge Industrial, Inc."#),
+ (2877_u16, r#"REALTIMEID AS"#),
+ (2878_u16, r#"ISEO Serrature S.p.a."#),
+ (2879_u16, r#"MindRhythm, Inc."#),
+ (2880_u16, r#"Havells India Limited"#),
+ (2881_u16, r#"Sentrax GmbH"#),
+ (2882_u16, r#"TSI"#),
+ (2883_u16, r#"INCITAT ENVIRONNEMENT"#),
+ (2884_u16, r#"nFore Technology Co., Ltd."#),
+ (2885_u16, r#"Electronic Sensors, Inc."#),
+ (2886_u16, r#"Bird Rides, Inc."#),
+ (2887_u16, r#"Gentex Corporation"#),
+ (2888_u16, r#"NIO USA, Inc."#),
+ (2889_u16, r#"SkyHawke Technologies"#),
+ (2890_u16, r#"Nomono AS"#),
+ (2891_u16, r#"EMS Integrators, LLC"#),
+ (2892_u16, r#"BiosBob.Biz"#),
+ (2893_u16, r#"Adam Hall GmbH"#),
+ (2894_u16, r#"ICP Systems B.V."#),
+ (2895_u16, r#"Breezi.io, Inc."#),
+ (2896_u16, r#"Mesh Systems LLC"#),
+ (2897_u16, r#"FUN FACTORY GmbH"#),
+ (2898_u16, r#"ZIIP Inc"#),
+ (2899_u16, r#"SHENZHEN KAADAS INTELLIGENT TECHNOLOGY CO.,Ltd"#),
+ (2900_u16, r#"Emotion Fitness GmbH & Co. KG"#),
+ (2901_u16, r#"H G M Automotive Electronics, Inc."#),
+ (2902_u16, r#"BORA - Vertriebs GmbH & Co KG"#),
+ (2903_u16, r#"CONVERTRONIX TECHNOLOGIES AND SERVICES LLP"#),
+ (2904_u16, r#"TOKAI-DENSHI INC"#),
+ (2905_u16, r#"Versa Group B.V."#),
+ (2906_u16, r#"H.P. Shelby Manufacturing, LLC."#),
+ (2907_u16, r#"Shenzhen ImagineVision Technology Limited"#),
+ (2908_u16, r#"Exponential Power, Inc."#),
+ (2909_u16, r#"Fujian Newland Auto-ID Tech. Co., Ltd."#),
+ (2910_u16, r#"CELLCONTROL, INC."#),
+ (2911_u16, r#"Rivieh, Inc."#),
+ (2912_u16, r#"RATOC Systems, Inc."#),
+ (2913_u16, r#"Sentek Pty Ltd"#),
+ (2914_u16, r#"NOVEA ENERGIES"#),
+ (2915_u16, r#"Innolux Corporation"#),
+ (2916_u16, r#"NingBo klite Electric Manufacture Co.,LTD"#),
+ (2917_u16, r#"The Apache Software Foundation"#),
+ (2918_u16, r#"MITSUBISHI ELECTRIC AUTOMATION (THAILAND) COMPANY LIMITED"#),
+ (2919_u16, r#"CleanSpace Technology Pty Ltd"#),
+ (2920_u16, r#"Quha oy"#),
+ (2921_u16, r#"Addaday"#),
+ (2922_u16, r#"Dymo"#),
+ (2923_u16, r#"Samsara Networks, Inc"#),
+ (2924_u16, r#"Sensitech, Inc."#),
+ (2925_u16, r#"SOLUM CO., LTD"#),
+ (2926_u16, r#"React Mobile"#),
+ (2927_u16, r#"Shenzhen Malide Technology Co.,Ltd"#),
+ (2928_u16, r#"JDRF Electromag Engineering Inc"#),
+ (2929_u16, r#"lilbit ODM AS"#),
+ (2930_u16, r#"Geeknet, Inc."#),
+ (2931_u16, r#"HARADA INDUSTRY CO., LTD."#),
+ (2932_u16, r#"BQN"#),
+ (2933_u16, r#"Triple W Japan Inc."#),
+ (2934_u16, r#"MAX-co., ltd"#),
+ (2935_u16, r#"Aixlink(Chengdu) Co., Ltd."#),
+ (2936_u16, r#"FIELD DESIGN INC."#),
+ (2937_u16, r#"Sankyo Air Tech Co.,Ltd."#),
+ (2938_u16, r#"Shenzhen KTC Technology Co.,Ltd."#),
+ (2939_u16, r#"Hardcoder Oy"#),
+ (2940_u16, r#"Scangrip A/S"#),
+ (2941_u16, r#"FoundersLane GmbH"#),
+ (2942_u16, r#"Offcode Oy"#),
+ (2943_u16, r#"ICU tech GmbH"#),
+ (2944_u16, r#"AXELIFE"#),
+ (2945_u16, r#"SCM Group"#),
+ (2946_u16, r#"Mammut Sports Group AG"#),
+ (2947_u16, r#"Taiga Motors Inc."#),
+ (2948_u16, r#"Presidio Medical, Inc."#),
+ (2949_u16, r#"VIMANA TECH PTY LTD"#),
+ (2950_u16, r#"Trek Bicycle"#),
+ (2951_u16, r#"Ampetronic Ltd"#),
+ (2952_u16, r#"Muguang (Guangdong) Intelligent Lighting Technology Co., Ltd"#),
+ (2953_u16, r#"Rotronic AG"#),
+ (2954_u16, r#"Seiko Instruments Inc."#),
+ (2955_u16, r#"American Technology Components, Incorporated"#),
+ (2956_u16, r#"MOTREX"#),
+ (2957_u16, r#"Pertech Industries Inc"#),
+ (2958_u16, r#"Gentle Energy Corp."#),
+ (2959_u16, r#"Senscomm Semiconductor Co., Ltd."#),
+ (2960_u16, r#"Ineos Automotive Limited"#),
+ (2961_u16, r#"Alfen ICU B.V."#),
+ (2962_u16, r#"Citisend Solutions, SL"#),
+ (2963_u16, r#"Hangzhou BroadLink Technology Co., Ltd."#),
+ (2964_u16, r#"Dreem SAS"#),
+ (2965_u16, r#"Netwake GmbH"#),
+ (2966_u16, r#"Telecom Design"#),
+ (2967_u16, r#"SILVER TREE LABS, INC."#),
+ (2968_u16, r#"Gymstory B.V."#),
+ (2969_u16, r#"The Goodyear Tire & Rubber Company"#),
+ (2970_u16, r#"Beijing Wisepool Infinite Intelligence Technology Co.,Ltd"#),
+ (2971_u16, r#"GISMAN"#),
+ (2972_u16, r#"Komatsu Ltd."#),
+ (2973_u16, r#"Sensoria Holdings LTD"#),
+ (2974_u16, r#"Audio Partnership Plc"#),
+ (2975_u16, r#"Group Lotus Limited"#),
+ (2976_u16, r#"Data Sciences International"#),
+ (2977_u16, r#"Bunn-O-Matic Corporation"#),
+ (2978_u16, r#"TireCheck GmbH"#),
+ (2979_u16, r#"Sonova Consumer Hearing GmbH"#),
+ (2980_u16, r#"Vervent Audio Group"#),
+ (2981_u16, r#"SONICOS ENTERPRISES, LLC"#),
+ (2982_u16, r#"Nissan Motor Co., Ltd."#),
+ (2983_u16, r#"hearX Group (Pty) Ltd"#),
+ (2984_u16, r#"GLOWFORGE INC."#),
+ (2985_u16, r#"Allterco Robotics ltd"#),
+ (2986_u16, r#"Infinitegra, Inc."#),
+ (2987_u16, r#"Grandex International Corporation"#),
+ (2988_u16, r#"Machfu Inc."#),
+ (2989_u16, r#"Roambotics, Inc."#),
+ (2990_u16, r#"Soma Labs LLC"#),
+ (2991_u16, r#"NITTO KOGYO CORPORATION"#),
+ (2992_u16, r#"Ecolab Inc."#),
+ (2993_u16, r#"Beijing ranxin intelligence technology Co.,LTD"#),
+ (2994_u16, r#"Fjorden Electra AS"#),
+ (2995_u16, r#"Flender GmbH"#),
+ (2996_u16, r#"New Cosmos USA, Inc."#),
+ (2997_u16, r#"Xirgo Technologies, LLC"#),
+ (2998_u16, r#"Build With Robots Inc."#),
+ (2999_u16, r#"IONA Tech LLC"#),
+ (3000_u16, r#"INNOVAG PTY. LTD."#),
+ (3001_u16, r#"SaluStim Group Oy"#),
+ (3002_u16, r#"Huso, INC"#),
+ (3003_u16, r#"SWISSINNO SOLUTIONS AG"#),
+ (3004_u16, r#"T2REALITY SOLUTIONS PRIVATE LIMITED"#),
+ (3005_u16, r#"ETHEORY PTY LTD"#),
+ (3006_u16, r#"SAAB Aktiebolag"#),
+ (3007_u16, r#"HIMSA II K/S"#),
+ (3008_u16, r#"READY FOR SKY LLP"#),
+ (3009_u16, r#"Miele & Cie. KG"#),
+ (3010_u16, r#"EntWick Co."#),
+ (3011_u16, r#"MCOT INC."#),
+ (3012_u16, r#"TECHTICS ENGINEERING B.V."#),
+ (3013_u16, r#"Aperia Technologies, Inc."#),
+ (3014_u16, r#"TCL COMMUNICATION EQUIPMENT CO.,LTD."#),
+ (3015_u16, r#"Signtle Inc."#),
+ (3016_u16, r#"OTF Distribution, LLC"#),
+ (3017_u16, r#"Neuvatek Inc."#),
+ (3018_u16, r#"Perimeter Technologies, Inc."#),
+ (3019_u16, r#"Divesoft s.r.o."#),
+ (3020_u16, r#"Sylvac sa"#),
+ (3021_u16, r#"Amiko srl"#),
+ (3022_u16, r#"Neurosity, Inc."#),
+ (3023_u16, r#"LL Tec Group LLC"#),
+ (3024_u16, r#"Durag GmbH"#),
+ (3025_u16, r#"Hubei Yuan Times Technology Co., Ltd."#),
+ (3026_u16, r#"IDEC"#),
+ (3027_u16, r#"Procon Analytics, LLC"#),
+ (3028_u16, r#"ndd Medizintechnik AG"#),
+ (3029_u16, r#"Super B Lithium Power B.V."#),
+ (3030_u16, r#"Shenzhen Injoinic Technology Co., Ltd."#),
+ (3031_u16, r#"VINFAST TRADING AND PRODUCTION JOINT STOCK COMPANY"#),
+ (3032_u16, r#"PURA SCENTS, INC."#),
+ (3033_u16, r#"Elics Basis Ltd."#),
+ (3034_u16, r#"Aardex Ltd."#),
+ (3035_u16, r#"CHAR-BROIL, LLC"#),
+ (3036_u16, r#"Ledworks S.r.l."#),
+ (3037_u16, r#"Coroflo Limited"#),
+ (3038_u16, r#"Yale"#),
+ (3039_u16, r#"WINKEY ENTERPRISE (HONG KONG) LIMITED"#),
+ (3040_u16, r#"Koizumi Lighting Technology corp."#),
+ (3041_u16, r#"Back40 Precision"#),
+ (3042_u16, r#"OTC engineering"#),
+ (3043_u16, r#"Comtel Systems Ltd."#),
+ (3044_u16, r#"Deepfield Connect GmbH"#),
+ (3045_u16, r#"ZWILLING J.A. Henckels Aktiengesellschaft"#),
+ (3046_u16, r#"Puratap Pty Ltd"#),
+ (3047_u16, r#"Fresnel Technologies, Inc."#),
+ (3048_u16, r#"Sensormate AG"#),
+ (3049_u16, r#"Shindengen Electric Manufacturing Co., Ltd."#),
+ (3050_u16, r#"Twenty Five Seven, prodaja in storitve, d.o.o."#),
+ (3051_u16, r#"Luna Health, Inc."#),
+ (3052_u16, r#"Miracle-Ear, Inc."#),
+ (3053_u16, r#"CORAL-TAIYI Co. Ltd."#),
+ (3054_u16, r#"LINKSYS USA, INC."#),
+ (3055_u16, r#"Safetytest GmbH"#),
+ (3056_u16, r#"KIDO SPORTS CO., LTD."#),
+ (3057_u16, r#"Site IQ LLC"#),
+ (3058_u16, r#"Angel Medical Systems, Inc."#),
+ (3059_u16, r#"PONE BIOMETRICS AS"#),
+ (3060_u16, r#"ER Lab LLC"#),
+ (3061_u16, r#"T5 tek, Inc."#),
+ (3062_u16, r#"greenTEG AG"#),
+ (3063_u16, r#"Wacker Neuson SE"#),
+ (3064_u16, r#"Innovacionnye Resheniya"#),
+ (3065_u16, r#"Alio, Inc"#),
+ (3066_u16, r#"CleanBands Systems Ltd."#),
+ (3067_u16, r#"Dodam Enersys Co., Ltd"#),
+ (3068_u16, r#"T+A elektroakustik GmbH & Co.KG"#),
+ (3069_u16, r#"Esmé Solutions"#),
+ (3070_u16, r#"Media-Cartec GmbH"#),
+ (3071_u16, r#"Ratio Electric BV"#),
+ (3072_u16, r#"MQA Limited"#),
+ (3073_u16, r#"NEOWRK SISTEMAS INTELIGENTES S.A."#),
+ (3074_u16, r#"Loomanet, Inc."#),
+ (3075_u16, r#"Puff Corp"#),
+ (3076_u16, r#"Happy Health, Inc."#),
+ (3077_u16, r#"Montage Connect, Inc."#),
+ (3078_u16, r#"LED Smart Inc."#),
+ (3079_u16, r#"CONSTRUKTS, INC."#),
+ (3080_u16, r#"limited liability company "Red""#),
+ (3081_u16, r#"Senic Inc."#),
+ (3082_u16, r#"Automated Pet Care Products, LLC"#),
+ (3083_u16, r#"aconno GmbH"#),
+ (3084_u16, r#"Mendeltron, Inc."#),
+ (3085_u16, r#"Mereltron bv"#),
+ (3086_u16, r#"ALEX DENKO CO.,LTD."#),
+ (3087_u16, r#"AETERLINK"#),
+ (3088_u16, r#"Cosmed s.r.l."#),
+ (3089_u16, r#"Gordon Murray Design Limited"#),
+ (3090_u16, r#"IoSA"#),
+ (3091_u16, r#"Scandinavian Health Limited"#),
+ (3092_u16, r#"Fasetto, Inc."#),
+ (3093_u16, r#"Geva Sol B.V."#),
+ (3094_u16, r#"TYKEE PTY. LTD."#),
+ (3095_u16, r#"SomnoMed Limited"#),
+ (3096_u16, r#"CORROHM"#),
+ (3097_u16, r#"Arlo Technologies, Inc."#),
+ (3098_u16, r#"Catapult Group International Ltd"#),
+ (3099_u16, r#"Rockchip Electronics Co., Ltd."#),
+ (3100_u16, r#"GEMU"#),
+ (3101_u16, r#"OFF Line Japan Co., Ltd."#),
+ (3102_u16, r#"EC sense co., Ltd"#),
+ (3103_u16, r#"LVI Co."#),
+ (3104_u16, r#"COMELIT GROUP S.P.A."#),
+ (3105_u16, r#"Foshan Viomi Electrical Technology Co., Ltd"#),
+ (3106_u16, r#"Glamo Inc."#),
+ (3107_u16, r#"KEYTEC,Inc."#),
+ (3108_u16, r#"SMARTD TECHNOLOGIES INC."#),
+ (3109_u16, r#"JURA Elektroapparate AG"#),
+ (3110_u16, r#"Performance Electronics, Ltd."#),
+ (3111_u16, r#"Pal Electronics"#),
+ (3112_u16, r#"Embecta Corp."#),
+ (3113_u16, r#"DENSO AIRCOOL CORPORATION"#),
+ (3114_u16, r#"Caresix Inc."#),
+ (3115_u16, r#"GigaDevice Semiconductor Inc."#),
+ (3116_u16, r#"Zeku Technology (Shanghai) Corp., Ltd."#),
+ (3117_u16, r#"OTF Product Sourcing, LLC"#),
+ (3118_u16, r#"Easee AS"#),
+ (3119_u16, r#"BEEHERO, INC."#),
+ (3120_u16, r#"McIntosh Group Inc"#),
+ (3121_u16, r#"KINDOO LLP"#),
+ (3122_u16, r#"Xian Yisuobao Electronic Technology Co., Ltd."#),
+ (3123_u16, r#"Exeger Operations AB"#),
+ (3124_u16, r#"BYD Company Limited"#),
+ (3125_u16, r#"Thermokon-Sensortechnik GmbH"#),
+ (3126_u16, r#"Cosmicnode BV"#),
+ (3127_u16, r#"SignalQuest, LLC"#),
+ (3128_u16, r#"Noritz Corporation."#),
+ (3129_u16, r#"TIGER CORPORATION"#),
+ (3130_u16, r#"Equinosis, LLC"#),
+ (3131_u16, r#"ORB Innovations Ltd"#),
+ (3132_u16, r#"Classified Cycling"#),
+ (3133_u16, r#"Wrmth Corp."#),
+ (3134_u16, r#"BELLDESIGN Inc."#),
+ (3135_u16, r#"Stinger Equipment, Inc."#),
+ (3136_u16, r#"HORIBA, Ltd."#),
+ (3137_u16, r#"Control Solutions LLC"#),
+ (3138_u16, r#"Heath Consultants Inc."#),
+ (3139_u16, r#"Berlinger & Co. AG"#),
+ (3140_u16, r#"ONCELABS LLC"#),
+ (3141_u16, r#"Brose Verwaltung SE, Bamberg"#),
+ (3142_u16, r#"Granwin IoT Technology (Guangzhou) Co.,Ltd"#),
+ (3143_u16, r#"Epsilon Electronics,lnc"#),
+ (3144_u16, r#"VALEO MANAGEMENT SERVICES"#),
+ (3145_u16, r#"twopounds gmbh"#),
+ (3146_u16, r#"atSpiro ApS"#),
+ (3147_u16, r#"ADTRAN, Inc."#),
+ (3148_u16, r#"Orpyx Medical Technologies Inc."#),
+ (3149_u16, r#"Seekwave Technology Co.,ltd."#),
+ (3150_u16, r#"Tactile Engineering, Inc."#),
+ (3151_u16, r#"SharkNinja Operating LLC"#),
+ (3152_u16, r#"Imostar Technologies Inc."#),
+ (3153_u16, r#"INNOVA S.R.L."#),
+ (3154_u16, r#"ESCEA LIMITED"#),
+ (3155_u16, r#"Taco, Inc."#),
+ (3156_u16, r#"HiViz Lighting, Inc."#),
+ (3157_u16, r#"Zintouch B.V."#),
+ (3158_u16, r#"Rheem Sales Company, Inc."#),
+ (3159_u16, r#"UNEEG medical A/S"#),
+ (3160_u16, r#"Hykso Inc."#),
+ (3161_u16, r#"CYBERDYNE Inc."#),
+ (3162_u16, r#"Lockswitch Sdn Bhd"#),
+ (3163_u16, r#"Alban Giacomo S.P.A."#),
+ (3164_u16, r#"MGM WIRELESSS HOLDINGS PTY LTD"#),
+ (3165_u16, r#"StepUp Solutions ApS"#),
+ (3166_u16, r#"BlueID GmbH"#),
+ (3167_u16, r#"Nanjing Linkpower Microelectronics Co.,Ltd"#),
+ (3168_u16, r#"KEBA Energy Automation GmbH"#),
+ (3169_u16, r#"NNOXX, Inc"#),
+ (3170_u16, r#"Phiaton Corporation"#),
+ (3171_u16, r#"phg Peter Hengstler GmbH + Co. KG"#),
+ (3172_u16, r#"dormakaba Holding AG"#),
+ (3173_u16, r#"WAKO CO,.LTD"#),
+ (3174_u16, r#"DEN Smart Home B.V."#),
+ (3175_u16, r#"TRACKTING S.R.L."#),
+ (3176_u16, r#"Emerja Corporation"#),
+ (3177_u16, r#"BLITZ electric motors. LTD"#),
+ (3178_u16, r#"CONSORCIO TRUST CONTROL - NETTEL"#),
+ (3179_u16, r#"GILSON SAS"#),
+ (3180_u16, r#"SNIFF LOGIC LTD"#),
+ (3181_u16, r#"Fidure Corp."#),
+ (3182_u16, r#"Sensa LLC"#),
+ (3183_u16, r#"Parakey AB"#),
+ (3184_u16, r#"SCARAB SOLUTIONS LTD"#),
+ (3185_u16, r#"BitGreen Technolabz (OPC) Private Limited"#),
+ (3186_u16, r#"StreetCar ORV, LLC"#),
+ (3187_u16, r#"Truma Gerätetechnik GmbH & Co. KG"#),
+ (3188_u16, r#"yupiteru"#),
+ (3189_u16, r#"Embedded Engineering Solutions LLC"#),
+ (3190_u16, r#"Shenzhen Gwell Times Technology Co. , Ltd"#),
+ (3191_u16, r#"TEAC Corporation"#),
+ (3192_u16, r#"CHARGTRON IOT PRIVATE LIMITED"#),
+ (3193_u16, r#"Zhuhai Smartlink Technology Co., Ltd"#),
+ (3194_u16, r#"Triductor Technology (Suzhou), Inc."#),
+ (3195_u16, r#"PT SADAMAYA GRAHA TEKNOLOGI"#),
+ (3196_u16, r#"Mopeka Products LLC"#),
+ (3197_u16, r#"3ALogics, Inc."#),
+ (3198_u16, r#"BOOMING OF THINGS"#),
+ (3199_u16, r#"Rochester Sensors, LLC"#),
+ (3200_u16, r#"CARDIOID - TECHNOLOGIES, LDA"#),
+ (3201_u16, r#"Carrier Corporation"#),
+ (3202_u16, r#"NACON"#),
+ (3203_u16, r#"Watchdog Systems LLC"#),
+ (3204_u16, r#"MAXON INDUSTRIES, INC."#),
+ (3205_u16, r#"Amlogic, Inc."#),
+ (3206_u16, r#"Qingdao Eastsoft Communication Technology Co.,Ltd"#),
+ (3207_u16, r#"Weltek Technologies Company Limited"#),
+ (3208_u16, r#"Nextivity Inc."#),
+ (3209_u16, r#"AGZZX OPTOELECTRONICS TECHNOLOGY CO., LTD"#),
+ (3210_u16, r#"ARTISTIC&CO.GLOBAL Ltd."#),
+ (3211_u16, r#"Heavys Inc"#),
+ (3212_u16, r#"T-Mobile USA"#),
+ (3213_u16, r#"tonies GmbH"#),
+ (3214_u16, r#"Technocon Engineering Ltd."#),
+ (3215_u16, r#"Radar Automobile Sales(Shandong)Co.,Ltd."#),
+ (3216_u16, r#"WESCO AG"#),
+ (3217_u16, r#"Yashu Systems"#),
+ (3218_u16, r#"Kesseböhmer Ergonomietechnik GmbH"#),
+ (3219_u16, r#"Movesense Oy"#),
+ (3220_u16, r#"Baxter Healthcare Corporation"#),
+ (3221_u16, r#"Gemstone Lights Canada Ltd."#),
+ (3222_u16, r#"H+B Hightech GmbH"#),
+ (3223_u16, r#"Deako"#),
+ (3224_u16, r#"MiX Telematics International (PTY) LTD"#),
+ (3225_u16, r#"Vire Health Oy"#),
+ (3226_u16, r#"ALF Inc."#),
+ (3227_u16, r#"NTT sonority, Inc."#),
+ (3228_u16, r#"Sunstone-RTLS Ipari Szolgaltato Korlatolt Felelossegu Tarsasag"#),
+ (3229_u16, r#"Ribbiot, INC."#),
+ (3230_u16, r#"ECCEL CORPORATION SAS"#),
+ (3231_u16, r#"Dragonfly Energy Corp."#),
+ (3232_u16, r#"BIGBEN"#),
+ (3233_u16, r#"YAMAHA MOTOR CO.,LTD."#),
+ (3234_u16, r#"XSENSE LTD"#),
+ (3235_u16, r#"MAQUET GmbH"#),
+ (3236_u16, r#"MITSUBISHI ELECTRIC LIGHTING CO, LTD"#),
+ (3237_u16, r#"Princess Cruise Lines, Ltd."#),
+ (3238_u16, r#"Megger Ltd"#),
+ (3239_u16, r#"Verve InfoTec Pty Ltd"#),
+ (3240_u16, r#"Sonas, Inc."#),
+ (3241_u16, r#"Mievo Technologies Private Limited"#),
+ (3242_u16, r#"Shenzhen Poseidon Network Technology Co., Ltd"#),
+ (3243_u16, r#"HERUTU ELECTRONICS CORPORATION"#),
+ (3244_u16, r#"Shenzhen Shokz Co.,Ltd."#),
+ (3245_u16, r#"Shenzhen Openhearing Tech CO., LTD ."#),
+ (3246_u16, r#"Evident Corporation"#),
+ (3247_u16, r#"NEURINNOV"#),
+ (3248_u16, r#"SwipeSense, Inc."#),
+ (3249_u16, r#"RF Creations"#),
+ (3250_u16, r#"SHINKAWA Sensor Technology, Inc."#),
+ (3251_u16, r#"janova GmbH"#),
+ (3252_u16, r#"Eberspaecher Climate Control Systems GmbH"#),
+ (3253_u16, r#"Racketry, d. o. o."#),
+ (3254_u16, r#"THE EELECTRIC MACARON LLC"#),
+ (3255_u16, r#"Cucumber Lighting Controls Limited"#),
+ (3256_u16, r#"Shanghai Proxy Network Technology Co., Ltd."#),
+ (3257_u16, r#"seca GmbH & Co. KG"#),
+ (3258_u16, r#"Ameso Tech (OPC) Private Limited"#),
+ (3259_u16, r#"Emlid Tech Kft."#),
+ (3260_u16, r#"TROX GmbH"#),
+ (3261_u16, r#"Pricer AB"#),
+ (3263_u16, r#"Forward Thinking Systems LLC."#),
+ (3264_u16, r#"Garnet Instruments Ltd."#),
+ (3265_u16, r#"CLEIO Inc."#),
+ (3266_u16, r#"Anker Innovations Limited"#),
+ (3267_u16, r#"HMD Global Oy"#),
+ (3268_u16, r#"ABUS August Bremicker Soehne Kommanditgesellschaft"#),
+ (3269_u16, r#"Open Road Solutions, Inc."#),
+ (3270_u16, r#"Serial Technology Corporation"#),
+ (3271_u16, r#"SB C&S Corp."#),
+ (3272_u16, r#"TrikThom"#),
+ (3273_u16, r#"Innocent Technology Co., Ltd."#),
+ (3274_u16, r#"Cyclops Marine Ltd"#),
+ (3275_u16, r#"NOTHING TECHNOLOGY LIMITED"#),
+ (3276_u16, r#"Kord Defence Pty Ltd"#),
+ (3277_u16, r#"YanFeng Visteon(Chongqing) Automotive Electronic Co.,Ltd"#),
+ (3278_u16, r#"SENOSPACE LLC"#),
+ (3279_u16, r#"Shenzhen CESI Information Technology Co., Ltd."#),
+ (3280_u16, r#"MooreSilicon Semiconductor Technology (Shanghai) Co., LTD."#),
+ (3281_u16, r#"Imagine Marketing Limited"#),
+ (3282_u16, r#"EQOM SSC B.V."#),
+ (3283_u16, r#"TechSwipe"#),
+ (3284_u16, r#"Shenzhen Zhiduotun IoT Technology Co., Ltd"#),
+ (3285_u16, r#"Numa Products, LLC"#),
+ (3286_u16, r#"HHO (Hangzhou) Digital Technology Co., Ltd."#),
+ (3287_u16, r#"Maztech Industries, LLC"#),
+ (3288_u16, r#"SIA Mesh Group"#),
+ (3289_u16, r#"Minami acoustics Limited"#),
+ (3290_u16, r#"Wolf Steel ltd"#),
+ (3291_u16, r#"Circus World Displays Limited"#),
+ (3292_u16, r#"Ypsomed AG"#),
+ (3293_u16, r#"Alif Semiconductor, Inc."#),
+ (3294_u16, r#"RESPONSE TECHNOLOGIES, LTD."#),
+ (3295_u16, r#"SHENZHEN CHENYUN ELECTRONICS CO., LTD"#),
+ (3296_u16, r#"VODALOGIC PTY LTD"#),
+ (3297_u16, r#"Regal Beloit America, Inc."#),
+ (3298_u16, r#"CORVENT MEDICAL, INC."#),
+ (3299_u16, r#"Taiwan Fuhsing"#),
+ (3300_u16, r#"Off-Highway Powertrain Services Germany GmbH"#),
+ (3301_u16, r#"Amina Distribution AS"#),
+ (3302_u16, r#"McWong International, Inc."#),
+ (3303_u16, r#"TAG HEUER SA"#),
+ (3304_u16, r#"Dongguan Yougo Electronics Co.,Ltd."#),
+ (3305_u16, r#"PEAG, LLC dba JLab Audio"#),
+ (3306_u16, r#"HAYWARD INDUSTRIES, INC."#),
+ (3307_u16, r#"Shenzhen Tingting Technology Co. LTD"#),
+ (3308_u16, r#"Pacific Coast Fishery Services (2003) Inc."#),
+ (3309_u16, r#"CV. NURI TEKNIK"#),
+ (3310_u16, r#"MadgeTech, Inc"#),
+ (3311_u16, r#"POGS B.V."#),
+ (3312_u16, r#"THOTAKA TEKHNOLOGIES INDIA PRIVATE LIMITED"#),
+ (3313_u16, r#"Midmark"#),
+ (3314_u16, r#"BestSens AG"#),
+ (3315_u16, r#"Radio Sound"#),
+ (3316_u16, r#"SOLUX PTY LTD"#),
+ (3317_u16, r#"BOS Balance of Storage Systems AG"#),
+ (3318_u16, r#"OJ Electronics A/S"#),
+ (3319_u16, r#"TVS Motor Company Ltd."#),
+ (3320_u16, r#"core sensing GmbH"#),
+ (3321_u16, r#"Tamblue Oy"#),
+ (3322_u16, r#"Protect Animals With Satellites LLC"#),
+ (3323_u16, r#"Tyromotion GmbH"#),
+ (3324_u16, r#"ElectronX design"#),
+ (3325_u16, r#"Wuhan Woncan Construction Technologies Co., Ltd."#),
+ (3326_u16, r#"Thule Group AB"#),
+ (3327_u16, r#"Ergodriven Inc"#),
]
.into_iter()
.map(|(id, name)| (Uuid16::from_be_bytes(id.to_be_bytes()), name))
diff --git a/rust/src/wrapper/common.rs b/rust/src/wrapper/common.rs
new file mode 100644
index 0000000..4947560
--- /dev/null
+++ b/rust/src/wrapper/common.rs
@@ -0,0 +1,34 @@
+// Copyright 2023 Google LLC
+//
+// 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.
+
+//! Shared resources found under bumble's common.py
+use pyo3::{PyObject, Python, ToPyObject};
+
+/// Represents the sink for some transport mechanism
+pub struct TransportSink(pub(crate) PyObject);
+
+impl ToPyObject for TransportSink {
+ fn to_object(&self, _py: Python<'_>) -> PyObject {
+ self.0.clone()
+ }
+}
+
+/// Represents the source for some transport mechanism
+pub struct TransportSource(pub(crate) PyObject);
+
+impl ToPyObject for TransportSource {
+ fn to_object(&self, _py: Python<'_>) -> PyObject {
+ self.0.clone()
+ }
+}
diff --git a/rust/src/wrapper/controller.rs b/rust/src/wrapper/controller.rs
new file mode 100644
index 0000000..4f19dd6
--- /dev/null
+++ b/rust/src/wrapper/controller.rs
@@ -0,0 +1,66 @@
+// Copyright 2023 Google LLC
+//
+// 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.
+
+//! Controller components
+use crate::wrapper::{
+ common::{TransportSink, TransportSource},
+ hci::Address,
+ link::Link,
+ wrap_python_async, PyDictExt,
+};
+use pyo3::{
+ intern,
+ types::{PyDict, PyModule},
+ PyObject, PyResult, Python,
+};
+use pyo3_asyncio::tokio::into_future;
+
+/// A controller that can send and receive HCI frames via some link
+#[derive(Clone)]
+pub struct Controller(pub(crate) PyObject);
+
+impl Controller {
+ /// Creates a new [Controller] object. When optional arguments are not specified, the Python
+ /// module specifies the defaults. Must be called from a thread with a Python event loop, which
+ /// should be true on `tokio::main` and `async_std::main`.
+ ///
+ /// For more info, see https://awestlake87.github.io/pyo3-asyncio/master/doc/pyo3_asyncio/#event-loop-references-and-contextvars.
+ pub async fn new(
+ name: &str,
+ host_source: Option<TransportSource>,
+ host_sink: Option<TransportSink>,
+ link: Option<Link>,
+ public_address: Option<Address>,
+ ) -> PyResult<Self> {
+ Python::with_gil(|py| {
+ let controller_ctr = PyModule::import(py, intern!(py, "bumble.controller"))?
+ .getattr(intern!(py, "Controller"))?;
+
+ let kwargs = PyDict::new(py);
+ kwargs.set_item("name", name)?;
+ kwargs.set_opt_item("host_source", host_source)?;
+ kwargs.set_opt_item("host_sink", host_sink)?;
+ kwargs.set_opt_item("link", link)?;
+ kwargs.set_opt_item("public_address", public_address)?;
+
+ // Controller constructor (`__init__`) is not (and can't be) marked async, but calls
+ // `get_running_loop`, and thus needs wrapped in an async function.
+ wrap_python_async(py, controller_ctr)?
+ .call((), Some(kwargs))
+ .and_then(into_future)
+ })?
+ .await
+ .map(Self)
+ }
+}
diff --git a/rust/src/wrapper/device.rs b/rust/src/wrapper/device.rs
index be5e4fa..6bf958a 100644
--- a/rust/src/wrapper/device.rs
+++ b/rust/src/wrapper/device.rs
@@ -14,12 +14,16 @@
//! Devices and connections to them
+use crate::internal::hci::WithPacketType;
use crate::{
adv::AdvertisementDataBuilder,
wrapper::{
core::AdvertisingData,
gatt_client::{ProfileServiceProxy, ServiceProxy},
- hci::{Address, HciErrorCode},
+ hci::{
+ packets::{Command, ErrorCode, Event},
+ Address, HciCommandWrapper,
+ },
host::Host,
l2cap::LeConnectionOrientedChannel,
transport::{Sink, Source},
@@ -27,18 +31,73 @@
},
};
use pyo3::{
+ exceptions::PyException,
intern,
types::{PyDict, PyModule},
- IntoPy, PyObject, PyResult, Python, ToPyObject,
+ IntoPy, PyErr, PyObject, PyResult, Python, ToPyObject,
};
use pyo3_asyncio::tokio::into_future;
use std::path;
+/// Represents the various properties of some device
+pub struct DeviceConfiguration(PyObject);
+
+impl DeviceConfiguration {
+ /// Creates a new configuration, letting the internal Python object set all the defaults
+ pub fn new() -> PyResult<DeviceConfiguration> {
+ Python::with_gil(|py| {
+ PyModule::import(py, intern!(py, "bumble.device"))?
+ .getattr(intern!(py, "DeviceConfiguration"))?
+ .call0()
+ .map(|any| Self(any.into()))
+ })
+ }
+
+ /// Creates a new configuration from the specified file
+ pub fn load_from_file(&mut self, device_config: &path::Path) -> PyResult<()> {
+ Python::with_gil(|py| {
+ self.0
+ .call_method1(py, intern!(py, "load_from_file"), (device_config,))
+ })
+ .map(|_| ())
+ }
+}
+
+impl ToPyObject for DeviceConfiguration {
+ fn to_object(&self, _py: Python<'_>) -> PyObject {
+ self.0.clone()
+ }
+}
+
/// A device that can send/receive HCI frames.
#[derive(Clone)]
pub struct Device(PyObject);
impl Device {
+ /// Creates a Device. When optional arguments are not specified, the Python object specifies the
+ /// defaults.
+ pub fn new(
+ name: Option<&str>,
+ address: Option<Address>,
+ config: Option<DeviceConfiguration>,
+ host: Option<Host>,
+ generic_access_service: Option<bool>,
+ ) -> PyResult<Self> {
+ Python::with_gil(|py| {
+ let kwargs = PyDict::new(py);
+ kwargs.set_opt_item("name", name)?;
+ kwargs.set_opt_item("address", address)?;
+ kwargs.set_opt_item("config", config)?;
+ kwargs.set_opt_item("host", host)?;
+ kwargs.set_opt_item("generic_access_service", generic_access_service)?;
+
+ PyModule::import(py, intern!(py, "bumble.device"))?
+ .getattr(intern!(py, "Device"))?
+ .call((), Some(kwargs))
+ .map(|any| Self(any.into()))
+ })
+ }
+
/// Create a Device per the provided file configured to communicate with a controller through an HCI source/sink
pub fn from_config_file_with_hci(
device_config: &path::Path,
@@ -66,6 +125,29 @@
})
}
+ /// Sends an HCI command on this Device, returning the command's event result.
+ pub async fn send_command(&self, command: &Command, check_result: bool) -> PyResult<Event> {
+ Python::with_gil(|py| {
+ self.0
+ .call_method1(
+ py,
+ intern!(py, "send_command"),
+ (HciCommandWrapper(command.clone()), check_result),
+ )
+ .and_then(|coroutine| into_future(coroutine.as_ref(py)))
+ })?
+ .await
+ .and_then(|event| {
+ Python::with_gil(|py| {
+ let py_bytes = event.call_method0(py, intern!(py, "__bytes__"))?;
+ let bytes: &[u8] = py_bytes.extract(py)?;
+ let event = Event::parse_with_packet_type(bytes)
+ .map_err(|e| PyErr::new::<PyException, _>(e.to_string()))?;
+ Ok(event)
+ })
+ })
+ }
+
/// Turn the device on
pub async fn power_on(&self) -> PyResult<()> {
Python::with_gil(|py| {
@@ -236,7 +318,7 @@
kwargs.set_opt_item("mps", mps)?;
self.0
.call_method(py, intern!(py, "open_l2cap_channel"), (), Some(kwargs))
- .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py)))
+ .and_then(|coroutine| into_future(coroutine.as_ref(py)))
})?
.await
.map(LeConnectionOrientedChannel::from)
@@ -244,13 +326,13 @@
/// Disconnect from device with provided reason. When optional arguments are not specified, the
/// Python module specifies the defaults.
- pub async fn disconnect(&mut self, reason: Option<HciErrorCode>) -> PyResult<()> {
+ pub async fn disconnect(&mut self, reason: Option<ErrorCode>) -> PyResult<()> {
Python::with_gil(|py| {
let kwargs = PyDict::new(py);
kwargs.set_opt_item("reason", reason)?;
self.0
.call_method(py, intern!(py, "disconnect"), (), Some(kwargs))
- .and_then(|coroutine| pyo3_asyncio::tokio::into_future(coroutine.as_ref(py)))
+ .and_then(|coroutine| into_future(coroutine.as_ref(py)))
})?
.await
.map(|_| ())
@@ -259,7 +341,7 @@
/// Register a callback to be called on disconnection.
pub fn on_disconnection(
&mut self,
- callback: impl Fn(Python, HciErrorCode) -> PyResult<()> + Send + 'static,
+ callback: impl Fn(Python, ErrorCode) -> PyResult<()> + Send + 'static,
) -> PyResult<()> {
let boxed = ClosureCallback::new(move |py, args, _kwargs| {
callback(py, args.get_item(0)?.extract()?)
diff --git a/rust/src/wrapper/hci.rs b/rust/src/wrapper/hci.rs
index 41dcbf3..b029a65 100644
--- a/rust/src/wrapper/hci.rs
+++ b/rust/src/wrapper/hci.rs
@@ -14,84 +14,62 @@
//! HCI
+pub use crate::internal::hci::{packets, Error, Packet};
+
+use crate::{
+ internal::hci::WithPacketType,
+ wrapper::hci::packets::{AddressType, Command, ErrorCode},
+};
use itertools::Itertools as _;
use pyo3::{
- exceptions::PyException, intern, types::PyModule, FromPyObject, PyAny, PyErr, PyObject,
- PyResult, Python, ToPyObject,
+ exceptions::PyException,
+ intern, pyclass, pymethods,
+ types::{PyBytes, PyModule},
+ FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject,
};
-/// HCI error code.
-pub struct HciErrorCode(u8);
-
-impl<'source> FromPyObject<'source> for HciErrorCode {
- fn extract(ob: &'source PyAny) -> PyResult<Self> {
- Ok(HciErrorCode(ob.extract()?))
- }
-}
-
-impl ToPyObject for HciErrorCode {
- fn to_object(&self, py: Python<'_>) -> PyObject {
- self.0.to_object(py)
- }
-}
-
/// Provides helpers for interacting with HCI
pub struct HciConstant;
impl HciConstant {
/// Human-readable error name
- pub fn error_name(status: HciErrorCode) -> PyResult<String> {
+ pub fn error_name(status: ErrorCode) -> PyResult<String> {
Python::with_gil(|py| {
PyModule::import(py, intern!(py, "bumble.hci"))?
.getattr(intern!(py, "HCI_Constant"))?
- .call_method1(intern!(py, "error_name"), (status.0,))?
+ .call_method1(intern!(py, "error_name"), (status.to_object(py),))?
.extract()
})
}
}
/// A Bluetooth address
+#[derive(Clone)]
pub struct Address(pub(crate) PyObject);
impl Address {
+ /// Creates a new [Address] object
+ pub fn new(address: &str, address_type: &AddressType) -> PyResult<Self> {
+ Python::with_gil(|py| {
+ PyModule::import(py, intern!(py, "bumble.device"))?
+ .getattr(intern!(py, "Address"))?
+ .call1((address, address_type.to_object(py)))
+ .map(|any| Self(any.into()))
+ })
+ }
+
/// The type of address
pub fn address_type(&self) -> PyResult<AddressType> {
Python::with_gil(|py| {
- let addr_type = self
- .0
+ self.0
.getattr(py, intern!(py, "address_type"))?
- .extract::<u32>(py)?;
-
- let module = PyModule::import(py, intern!(py, "bumble.hci"))?;
- let klass = module.getattr(intern!(py, "Address"))?;
-
- if addr_type
- == klass
- .getattr(intern!(py, "PUBLIC_DEVICE_ADDRESS"))?
- .extract::<u32>()?
- {
- Ok(AddressType::PublicDevice)
- } else if addr_type
- == klass
- .getattr(intern!(py, "RANDOM_DEVICE_ADDRESS"))?
- .extract::<u32>()?
- {
- Ok(AddressType::RandomDevice)
- } else if addr_type
- == klass
- .getattr(intern!(py, "PUBLIC_IDENTITY_ADDRESS"))?
- .extract::<u32>()?
- {
- Ok(AddressType::PublicIdentity)
- } else if addr_type
- == klass
- .getattr(intern!(py, "RANDOM_IDENTITY_ADDRESS"))?
- .extract::<u32>()?
- {
- Ok(AddressType::RandomIdentity)
- } else {
- Err(PyErr::new::<PyException, _>("Invalid address type"))
- }
+ .extract::<u8>(py)?
+ .try_into()
+ .map_err(|addr_type| {
+ PyErr::new::<PyException, _>(format!(
+ "Failed to convert {addr_type} to AddressType"
+ ))
+ })
})
}
@@ -134,12 +112,45 @@
}
}
-/// BT address types
-#[allow(missing_docs)]
-#[derive(PartialEq, Eq, Debug)]
-pub enum AddressType {
- PublicDevice,
- RandomDevice,
- PublicIdentity,
- RandomIdentity,
+impl ToPyObject for Address {
+ fn to_object(&self, _py: Python<'_>) -> PyObject {
+ self.0.clone()
+ }
+}
+
+/// Implements minimum necessary interface to be treated as bumble's [HCI_Command].
+/// While pyo3's macros do not support generics, this could probably be refactored to allow multiple
+/// implementations of the HCI_Command methods in the future, if needed.
+#[pyclass]
+pub(crate) struct HciCommandWrapper(pub(crate) Command);
+
+#[pymethods]
+impl HciCommandWrapper {
+ fn __bytes__(&self, py: Python) -> PyResult<PyObject> {
+ let bytes = PyBytes::new(py, &self.0.clone().to_vec_with_packet_type());
+ Ok(bytes.into_py(py))
+ }
+
+ #[getter]
+ fn op_code(&self) -> u16 {
+ self.0.get_op_code().into()
+ }
+}
+
+impl ToPyObject for AddressType {
+ fn to_object(&self, py: Python<'_>) -> PyObject {
+ u8::from(self).to_object(py)
+ }
+}
+
+impl<'source> FromPyObject<'source> for ErrorCode {
+ fn extract(ob: &'source PyAny) -> PyResult<Self> {
+ ob.extract()
+ }
+}
+
+impl ToPyObject for ErrorCode {
+ fn to_object(&self, py: Python<'_>) -> PyObject {
+ u8::from(self).to_object(py)
+ }
}
diff --git a/rust/src/wrapper/host.rs b/rust/src/wrapper/host.rs
index ab81450..8295664 100644
--- a/rust/src/wrapper/host.rs
+++ b/rust/src/wrapper/host.rs
@@ -14,8 +14,12 @@
//! Host-side types
-use crate::wrapper::transport::{Sink, Source};
-use pyo3::{intern, prelude::PyModule, types::PyDict, PyObject, PyResult, Python};
+use crate::wrapper::{
+ transport::{Sink, Source},
+ wrap_python_async,
+};
+use pyo3::{intern, prelude::PyModule, types::PyDict, PyObject, PyResult, Python, ToPyObject};
+use pyo3_asyncio::tokio::into_future;
/// Host HCI commands
pub struct Host {
@@ -29,13 +33,23 @@
}
/// Create a new Host
- pub fn new(source: Source, sink: Sink) -> PyResult<Self> {
+ pub async fn new(source: Source, sink: Sink) -> PyResult<Self> {
Python::with_gil(|py| {
- PyModule::import(py, intern!(py, "bumble.host"))?
- .getattr(intern!(py, "Host"))?
- .call((source.0, sink.0), None)
- .map(|any| Self { obj: any.into() })
- })
+ let host_ctr =
+ PyModule::import(py, intern!(py, "bumble.host"))?.getattr(intern!(py, "Host"))?;
+
+ let kwargs = PyDict::new(py);
+ kwargs.set_item("controller_source", source.0)?;
+ kwargs.set_item("controller_sink", sink.0)?;
+
+ // Needed for Python 3.8-3.9, in which the Semaphore object, when constructed, calls
+ // `get_event_loop`.
+ wrap_python_async(py, host_ctr)?
+ .call((), Some(kwargs))
+ .and_then(into_future)
+ })?
+ .await
+ .map(|any| Self { obj: any })
}
/// Send a reset command and perform other reset tasks.
@@ -61,6 +75,12 @@
}
}
+impl ToPyObject for Host {
+ fn to_object(&self, _py: Python<'_>) -> PyObject {
+ self.obj.clone()
+ }
+}
+
/// Driver factory to use when initializing a host
#[derive(Debug, Clone)]
pub enum DriverFactory {
diff --git a/rust/src/wrapper/link.rs b/rust/src/wrapper/link.rs
new file mode 100644
index 0000000..7169ef5
--- /dev/null
+++ b/rust/src/wrapper/link.rs
@@ -0,0 +1,38 @@
+// Copyright 2023 Google LLC
+//
+// 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.
+
+//! Link components
+use pyo3::{intern, types::PyModule, PyObject, PyResult, Python, ToPyObject};
+
+/// Link bus for controllers to communicate with each other
+#[derive(Clone)]
+pub struct Link(pub(crate) PyObject);
+
+impl Link {
+ /// Creates a [Link] object that transports messages locally
+ pub fn new_local_link() -> PyResult<Self> {
+ Python::with_gil(|py| {
+ PyModule::import(py, intern!(py, "bumble.link"))?
+ .getattr(intern!(py, "LocalLink"))?
+ .call0()
+ .map(|any| Self(any.into()))
+ })
+ }
+}
+
+impl ToPyObject for Link {
+ fn to_object(&self, _py: Python<'_>) -> PyObject {
+ self.0.clone()
+ }
+}
diff --git a/rust/src/wrapper/mod.rs b/rust/src/wrapper/mod.rs
index 94ac15a..27b86d9 100644
--- a/rust/src/wrapper/mod.rs
+++ b/rust/src/wrapper/mod.rs
@@ -22,13 +22,17 @@
// Re-exported to make it easy for users to depend on the same `PyObject`, etc
pub use pyo3;
+pub use pyo3_asyncio;
+
use pyo3::{
+ intern,
prelude::*,
types::{PyDict, PyTuple},
};
-pub use pyo3_asyncio;
pub mod assigned_numbers;
+pub mod common;
+pub mod controller;
pub mod core;
pub mod device;
pub mod drivers;
@@ -36,6 +40,7 @@
pub mod hci;
pub mod host;
pub mod l2cap;
+pub mod link;
pub mod logging;
pub mod profile;
pub mod transport;
@@ -119,3 +124,11 @@
(self.callback)(py, args, kwargs).map(|_| py.None())
}
}
+
+/// Wraps the Python function in a Python async function. `pyo3_asyncio` needs functions to be
+/// marked async to properly inject a running loop.
+pub(crate) fn wrap_python_async<'a>(py: Python<'a>, function: &'a PyAny) -> PyResult<&'a PyAny> {
+ PyModule::import(py, intern!(py, "bumble.utils"))?
+ .getattr(intern!(py, "wrap_async"))?
+ .call1((function,))
+}
diff --git a/rust/src/wrapper/transport.rs b/rust/src/wrapper/transport.rs
index 6c9468d..a7ec9e9 100644
--- a/rust/src/wrapper/transport.rs
+++ b/rust/src/wrapper/transport.rs
@@ -14,6 +14,7 @@
//! HCI packet transport
+use crate::wrapper::controller::Controller;
use pyo3::{intern, types::PyModule, PyObject, PyResult, Python};
/// A source/sink pair for HCI packet I/O.
@@ -67,6 +68,18 @@
#[derive(Clone)]
pub struct Source(pub(crate) PyObject);
+impl From<Controller> for Source {
+ fn from(value: Controller) -> Self {
+ Self(value.0)
+ }
+}
+
/// The sink side of a [Transport].
#[derive(Clone)]
pub struct Sink(pub(crate) PyObject);
+
+impl From<Controller> for Sink {
+ fn from(value: Controller) -> Self {
+ Self(value.0)
+ }
+}
diff --git a/setup.cfg b/setup.cfg
index 1ca73c7..5cdf35a 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -91,9 +91,13 @@
mypy == 1.5.0
nox >= 2022
pylint == 2.15.8
+ pyyaml >= 6.0
types-appdirs >= 1.4.3
types-invoke >= 1.7.3
types-protobuf >= 4.21.0
+avatar =
+ pandora-avatar == 0.0.5
+ rootcanal == 1.3.0 ; python_version>='3.10'
documentation =
mkdocs >= 1.4.0
mkdocs-material >= 8.5.6
diff --git a/tests/a2dp_test.py b/tests/a2dp_test.py
index 92f7915..ca59890 100644
--- a/tests/a2dp_test.py
+++ b/tests/a2dp_test.py
@@ -185,7 +185,7 @@
sink.on('rtp_packet', on_rtp_packet)
# Create a listener to wait for AVDTP connections
- listener = Listener(Listener.create_registrar(two_devices.devices[1]))
+ listener = Listener.for_device(two_devices.devices[1])
listener.on('connection', on_avdtp_connection)
async def make_connection():
diff --git a/tests/avdtp_test.py b/tests/avdtp_test.py
index 1ca5254..666a84c 100644
--- a/tests/avdtp_test.py
+++ b/tests/avdtp_test.py
@@ -45,12 +45,14 @@
]
message = Get_Capabilities_Response(capabilities)
parsed = Message.create(
- AVDTP_GET_CAPABILITIES, Message.RESPONSE_ACCEPT, message.payload
+ AVDTP_GET_CAPABILITIES, Message.MessageType.RESPONSE_ACCEPT, message.payload
)
assert message.payload == parsed.payload
message = Set_Configuration_Command(3, 4, capabilities)
- parsed = Message.create(AVDTP_SET_CONFIGURATION, Message.COMMAND, message.payload)
+ parsed = Message.create(
+ AVDTP_SET_CONFIGURATION, Message.MessageType.COMMAND, message.payload
+ )
assert message.payload == parsed.payload
diff --git a/tests/keystore_test.py b/tests/keystore_test.py
index 2a3d48d..34a2cb2 100644
--- a/tests/keystore_test.py
+++ b/tests/keystore_test.py
@@ -94,6 +94,7 @@
# -----------------------------------------------------------------------------
[email protected]
async def test_basic(temporary_file):
with open(temporary_file, mode='w', encoding='utf-8') as file:
file.write("{}")
@@ -125,6 +126,7 @@
# -----------------------------------------------------------------------------
[email protected]
async def test_parsing(temporary_file):
with open(temporary_file, mode='w', encoding='utf-8') as file:
file.write(JSON1)
@@ -137,6 +139,7 @@
# -----------------------------------------------------------------------------
[email protected]
async def test_default_namespace(temporary_file):
with open(temporary_file, mode='w', encoding='utf-8') as file:
file.write(JSON1)
diff --git a/tests/l2cap_test.py b/tests/l2cap_test.py
index c6b2340..5cb285c 100644
--- a/tests/l2cap_test.py
+++ b/tests/l2cap_test.py
@@ -22,7 +22,11 @@
import pytest
from bumble.core import ProtocolError
-from bumble.l2cap import L2CAP_Connection_Request
+from bumble.l2cap import (
+ L2CAP_Connection_Request,
+ ClassicChannelSpec,
+ LeCreditBasedChannelSpec,
+)
from .test_utils import TwoDevices
@@ -80,7 +84,9 @@
# Check that if there's no one listening, we can't connect
with pytest.raises(ProtocolError):
- l2cap_channel = await devices.connections[0].open_l2cap_channel(psm)
+ l2cap_channel = await devices.connections[0].create_l2cap_channel(
+ spec=LeCreditBasedChannelSpec(psm)
+ )
# Now add a listener
incoming_channel = None
@@ -95,8 +101,12 @@
channel.sink = on_data
- devices.devices[1].register_l2cap_channel_server(psm, on_coc)
- l2cap_channel = await devices.connections[0].open_l2cap_channel(psm)
+ devices.devices[1].create_l2cap_server(
+ spec=LeCreditBasedChannelSpec(psm=1234), handler=on_coc
+ )
+ l2cap_channel = await devices.connections[0].create_l2cap_channel(
+ spec=LeCreditBasedChannelSpec(psm)
+ )
messages = (bytes([1, 2, 3]), bytes([4, 5, 6]), bytes(10000))
for message in messages:
@@ -138,10 +148,13 @@
channel.sink = on_data
- psm = devices.devices[1].register_l2cap_channel_server(
- psm=0, server=on_coc, max_credits=max_credits, mtu=mtu, mps=mps
+ server = devices.devices[1].create_l2cap_server(
+ spec=LeCreditBasedChannelSpec(max_credits=max_credits, mtu=mtu, mps=mps),
+ handler=on_coc,
)
- l2cap_channel = await devices.connections[0].open_l2cap_channel(psm)
+ l2cap_channel = await devices.connections[0].create_l2cap_channel(
+ spec=LeCreditBasedChannelSpec(server.psm)
+ )
messages = [bytes([1, 2, 3, 4, 5, 6, 7]) * x for x in (3, 10, 100, 789)]
for message in messages:
@@ -189,8 +202,12 @@
def on_client_data(data):
client_received.append(data)
- psm = devices.devices[1].register_l2cap_channel_server(psm=0, server=on_server_coc)
- client_channel = await devices.connections[0].open_l2cap_channel(psm)
+ server = devices.devices[1].create_l2cap_server(
+ spec=LeCreditBasedChannelSpec(), handler=on_server_coc
+ )
+ client_channel = await devices.connections[0].create_l2cap_channel(
+ spec=LeCreditBasedChannelSpec(server.psm)
+ )
client_channel.sink = on_client_data
messages = [bytes([1, 2, 3, 4, 5, 6, 7]) * x for x in (3, 10, 100)]
diff --git a/tests/sdp_test.py b/tests/sdp_test.py
index 090e7b2..29db875 100644
--- a/tests/sdp_test.py
+++ b/tests/sdp_test.py
@@ -18,6 +18,7 @@
import asyncio
import logging
import os
+import pytest
from bumble.core import UUID, BT_L2CAP_PROTOCOL_ID, BT_RFCOMM_PROTOCOL_ID
from bumble.sdp import (
@@ -99,13 +100,13 @@
e = DataElement(DataElement.UUID, UUID('61A3512C-09BE-4DDC-A6A6-0B03667AAFC6'))
basic_check(e)
- e = DataElement(DataElement.TEXT_STRING, 'hello')
+ e = DataElement(DataElement.TEXT_STRING, b'hello')
basic_check(e)
- e = DataElement(DataElement.TEXT_STRING, 'hello' * 60)
+ e = DataElement(DataElement.TEXT_STRING, b'hello' * 60)
basic_check(e)
- e = DataElement(DataElement.TEXT_STRING, 'hello' * 20000)
+ e = DataElement(DataElement.TEXT_STRING, b'hello' * 20000)
basic_check(e)
e = DataElement(DataElement.BOOLEAN, True)
@@ -121,7 +122,7 @@
DataElement.SEQUENCE,
[
DataElement(DataElement.BOOLEAN, True),
- DataElement(DataElement.TEXT_STRING, 'hello'),
+ DataElement(DataElement.TEXT_STRING, b'hello'),
],
)
basic_check(e)
@@ -133,7 +134,7 @@
DataElement.ALTERNATIVE,
[
DataElement(DataElement.BOOLEAN, True),
- DataElement(DataElement.TEXT_STRING, 'hello'),
+ DataElement(DataElement.TEXT_STRING, b'hello'),
],
)
basic_check(e)
@@ -151,19 +152,19 @@
e = DataElement.uuid(UUID.from_16_bits(1234))
basic_check(e)
- e = DataElement.text_string('hello')
+ e = DataElement.text_string(b'hello')
basic_check(e)
e = DataElement.boolean(True)
basic_check(e)
e = DataElement.sequence(
- [DataElement.signed_integer(0, 1), DataElement.text_string('hello')]
+ [DataElement.signed_integer(0, 1), DataElement.text_string(b'hello')]
)
basic_check(e)
e = DataElement.alternative(
- [DataElement.signed_integer(0, 1), DataElement.text_string('hello')]
+ [DataElement.signed_integer(0, 1), DataElement.text_string(b'hello')]
)
basic_check(e)
@@ -202,6 +203,7 @@
# -----------------------------------------------------------------------------
[email protected]
async def test_service_search():
# Setup connections
devices = TwoDevices()
@@ -224,6 +226,7 @@
# -----------------------------------------------------------------------------
[email protected]
async def test_service_attribute():
# Setup connections
devices = TwoDevices()
@@ -244,6 +247,7 @@
# -----------------------------------------------------------------------------
[email protected]
async def test_service_search_attribute():
# Setup connections
devices = TwoDevices()
diff --git a/tools/generate_company_id_list.py b/tools/generate_company_id_list.py
index bba42b8..d79a6d8 100644
--- a/tools/generate_company_id_list.py
+++ b/tools/generate_company_id_list.py
@@ -14,25 +14,25 @@
# -----------------------------------------------------------------------------
# This script generates a python-syntax list of dictionary entries for the
-# company IDs listed at: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers/
-# The input to this script is the CSV file that can be obtained at that URL
+# company IDs listed at:
+# https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml
+# The input to this script is the YAML file that can be obtained at that URL
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Imports
# -----------------------------------------------------------------------------
import sys
-import csv
+import yaml
# -----------------------------------------------------------------------------
-with open(sys.argv[1], newline='') as csvfile:
- reader = csv.reader(csvfile, delimiter=',', quotechar='"')
- lines = []
- for row in reader:
- if len(row) == 3 and row[1].startswith('0x'):
- company_id = row[1]
- company_name = row[2]
- escaped_company_name = company_name.replace('"', '\\"')
- lines.append(f' {company_id}: "{escaped_company_name}"')
+with open(sys.argv[1], "r") as yaml_file:
+ root = yaml.safe_load(yaml_file)
+ companies = {}
+ for company in root["company_identifiers"]:
+ companies[company["value"]] = company["name"]
- print(',\n'.join(reversed(lines)))
+ for company_id in sorted(companies.keys()):
+ company_name = companies[company_id]
+ escaped_company_name = company_name.replace('"', '\\"')
+ print(f' 0x{company_id:04X}: "{escaped_company_name}",')
diff --git a/web/speaker/speaker.py b/web/speaker/speaker.py
index d9293a4..d9488ce 100644
--- a/web/speaker/speaker.py
+++ b/web/speaker/speaker.py
@@ -296,7 +296,7 @@
self.device.on('key_store_update', self.on_key_store_update)
# Create a listener to wait for AVDTP connections
- self.listener = Listener(Listener.create_registrar(self.device))
+ self.listener = Listener.for_device(self.device)
self.listener.on('connection', self.on_avdtp_connection)
print(f'Speaker ready to play, codec={self.codec}')