Compare commits
53 Commits
gbg/multi-
...
gbg/androi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc6b466a42 | ||
|
|
5e5c9c2580 | ||
|
|
246b11925c | ||
|
|
dfa9131192 | ||
|
|
88c801b4c2 | ||
|
|
a1b55b94e0 | ||
|
|
80db9e2e2f | ||
|
|
ce74690420 | ||
|
|
50de4dfb5d | ||
|
|
9bcdf860f4 | ||
|
|
511ab4b630 | ||
|
|
6f2b623e3c | ||
|
|
fa12165cd3 | ||
|
|
c0c6f3329d | ||
|
|
406a932467 | ||
|
|
cc96d4245f | ||
|
|
c6cdca8923 | ||
|
|
7e331c2944 | ||
|
|
10347765cb | ||
|
|
c12dee4e76 | ||
|
|
772c188674 | ||
|
|
7c1a3bb8f9 | ||
|
|
8c3c0b1e13 | ||
|
|
1ad84ad51c | ||
|
|
64937c3f77 | ||
|
|
50fd2218fa | ||
|
|
4c29a16271 | ||
|
|
762d3e92de | ||
|
|
2f97531d78 | ||
|
|
f6c7cae661 | ||
|
|
f1777a5bd2 | ||
|
|
78a06ae8cf | ||
|
|
d290df4aa9 | ||
|
|
e559744f32 | ||
|
|
67418e649a | ||
|
|
5adf9fab53 | ||
|
|
2491b686fa | ||
|
|
efd02b2f3e | ||
|
|
3b14078646 | ||
|
|
eb9d5632bc | ||
|
|
45f60edbb6 | ||
|
|
393ea6a7bb | ||
|
|
6ec6f1efe5 | ||
|
|
5d9598ea51 | ||
|
|
0d36d99a73 | ||
|
|
d8a9f5a724 | ||
|
|
2c66e1a042 | ||
|
|
d5eccdb00f | ||
|
|
32626573a6 | ||
|
|
caa82b8f7e | ||
|
|
5af347b499 | ||
|
|
4ed5bb5a9e | ||
|
|
f39f5f531c |
2
.github/workflows/python-build-test.yml
vendored
@@ -65,6 +65,8 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
components: clippy,rustfmt
|
components: clippy,rustfmt
|
||||||
toolchain: ${{ matrix.rust-version }}
|
toolchain: ${{ matrix.rust-version }}
|
||||||
|
- name: Check License Headers
|
||||||
|
run: cd rust && cargo run --features dev-tools --bin file-header check-all
|
||||||
- name: Rust Build
|
- name: Rust Build
|
||||||
run: cd rust && cargo build --all-targets && cargo build --all-features --all-targets
|
run: cd rust && cargo build --all-targets && cargo build --all-features --all-targets
|
||||||
# Lints after build so what clippy needs is already built
|
# Lints after build so what clippy needs is already built
|
||||||
|
|||||||
2
.vscode/settings.json
vendored
@@ -39,10 +39,12 @@
|
|||||||
"libusb",
|
"libusb",
|
||||||
"MITM",
|
"MITM",
|
||||||
"NDIS",
|
"NDIS",
|
||||||
|
"netsim",
|
||||||
"NONBLOCK",
|
"NONBLOCK",
|
||||||
"NONCONN",
|
"NONCONN",
|
||||||
"OXIMETER",
|
"OXIMETER",
|
||||||
"popleft",
|
"popleft",
|
||||||
|
"protobuf",
|
||||||
"psms",
|
"psms",
|
||||||
"pyee",
|
"pyee",
|
||||||
"pyusb",
|
"pyusb",
|
||||||
|
|||||||
@@ -1172,7 +1172,7 @@ class ScanResult:
|
|||||||
name = ''
|
name = ''
|
||||||
|
|
||||||
# Remove any '/P' qualifier suffix from the address string
|
# Remove any '/P' qualifier suffix from the address string
|
||||||
address_str = str(self.address).replace('/P', '')
|
address_str = self.address.to_string(with_type_qualifier=False)
|
||||||
|
|
||||||
# RSSI bar
|
# RSSI bar
|
||||||
bar_string = rssi_bar(self.rssi)
|
bar_string = rssi_bar(self.rssi)
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ async def get_classic_info(host):
|
|||||||
if command_succeeded(response):
|
if command_succeeded(response):
|
||||||
print()
|
print()
|
||||||
print(
|
print(
|
||||||
color('Classic Address:', 'yellow'), response.return_parameters.bd_addr
|
color('Classic Address:', 'yellow'),
|
||||||
|
response.return_parameters.bd_addr.to_string(False),
|
||||||
)
|
)
|
||||||
|
|
||||||
if host.supports_command(HCI_READ_LOCAL_NAME_COMMAND):
|
if host.supports_command(HCI_READ_LOCAL_NAME_COMMAND):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import click
|
|||||||
import logging
|
import logging
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from bumble.pandora import PandoraDevice, serve
|
from bumble.pandora import PandoraDevice, Config, serve
|
||||||
from typing import Dict, Any
|
from typing import Dict, Any
|
||||||
|
|
||||||
BUMBLE_SERVER_GRPC_PORT = 7999
|
BUMBLE_SERVER_GRPC_PORT = 7999
|
||||||
@@ -29,12 +29,14 @@ def main(grpc_port: int, rootcanal_port: int, transport: str, config: str) -> No
|
|||||||
transport = transport.replace('<rootcanal-port>', str(rootcanal_port))
|
transport = transport.replace('<rootcanal-port>', str(rootcanal_port))
|
||||||
|
|
||||||
bumble_config = retrieve_config(config)
|
bumble_config = retrieve_config(config)
|
||||||
if 'transport' not in bumble_config.keys():
|
bumble_config.setdefault('transport', transport)
|
||||||
bumble_config.update({'transport': transport})
|
|
||||||
device = PandoraDevice(bumble_config)
|
device = PandoraDevice(bumble_config)
|
||||||
|
|
||||||
|
server_config = Config()
|
||||||
|
server_config.load_from_dict(bumble_config.get('server', {}))
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
asyncio.run(serve(device, port=grpc_port))
|
asyncio.run(serve(device, config=server_config, port=grpc_port))
|
||||||
|
|
||||||
|
|
||||||
def retrieve_config(config: str) -> Dict[str, Any]:
|
def retrieve_config(config: str) -> Dict[str, Any]:
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ class WebSocketOutput(QueuedOutput):
|
|||||||
except HCI_StatusError:
|
except HCI_StatusError:
|
||||||
pass
|
pass
|
||||||
peer_name = '' if connection.peer_name is None else connection.peer_name
|
peer_name = '' if connection.peer_name is None else connection.peer_name
|
||||||
peer_address = str(connection.peer_address).replace('/P', '')
|
peer_address = connection.peer_address.to_string(False)
|
||||||
await self.send_message(
|
await self.send_message(
|
||||||
'connection',
|
'connection',
|
||||||
peer_address=peer_address,
|
peer_address=peer_address,
|
||||||
@@ -376,7 +376,7 @@ class UiServer:
|
|||||||
if connection := self.speaker().connection:
|
if connection := self.speaker().connection:
|
||||||
await self.send_message(
|
await self.send_message(
|
||||||
'connection',
|
'connection',
|
||||||
peer_address=str(connection.peer_address).replace('/P', ''),
|
peer_address=connection.peer_address.to_string(False),
|
||||||
peer_name=connection.peer_name,
|
peer_name=connection.peer_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -23,13 +23,14 @@
|
|||||||
# Imports
|
# Imports
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
import enum
|
||||||
import functools
|
import functools
|
||||||
import struct
|
import struct
|
||||||
from pyee import EventEmitter
|
from pyee import EventEmitter
|
||||||
from typing import Dict, Type, TYPE_CHECKING
|
from typing import Dict, Type, List, Protocol, Union, Optional, Any, TYPE_CHECKING
|
||||||
|
|
||||||
from bumble.core import UUID, name_or_number, get_dict_key_by_value, ProtocolError
|
from bumble.core import UUID, name_or_number, ProtocolError
|
||||||
from bumble.hci import HCI_Object, key_with_value, HCI_Constant
|
from bumble.hci import HCI_Object, key_with_value
|
||||||
from bumble.colors import color
|
from bumble.colors import color
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@@ -182,6 +183,7 @@ UUID_2_FIELD_SPEC = lambda x, y: UUID.parse_uuid_2(x, y) # noqa: E731
|
|||||||
# pylint: enable=line-too-long
|
# pylint: enable=line-too-long
|
||||||
# pylint: disable=invalid-name
|
# pylint: disable=invalid-name
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Exceptions
|
# Exceptions
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -209,7 +211,7 @@ class ATT_PDU:
|
|||||||
|
|
||||||
pdu_classes: Dict[int, Type[ATT_PDU]] = {}
|
pdu_classes: Dict[int, Type[ATT_PDU]] = {}
|
||||||
op_code = 0
|
op_code = 0
|
||||||
name = None
|
name: str
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_bytes(pdu):
|
def from_bytes(pdu):
|
||||||
@@ -719,9 +721,18 @@ class ATT_Handle_Value_Confirmation(ATT_PDU):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
class ConnectionValue(Protocol):
|
||||||
|
def read(self, connection) -> bytes:
|
||||||
|
...
|
||||||
|
|
||||||
|
def write(self, connection, value: bytes) -> None:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Attribute(EventEmitter):
|
class Attribute(EventEmitter):
|
||||||
# Permission flags
|
class Permissions(enum.IntFlag):
|
||||||
READABLE = 0x01
|
READABLE = 0x01
|
||||||
WRITEABLE = 0x02
|
WRITEABLE = 0x02
|
||||||
READ_REQUIRES_ENCRYPTION = 0x04
|
READ_REQUIRES_ENCRYPTION = 0x04
|
||||||
@@ -731,36 +742,47 @@ class Attribute(EventEmitter):
|
|||||||
READ_REQUIRES_AUTHORIZATION = 0x40
|
READ_REQUIRES_AUTHORIZATION = 0x40
|
||||||
WRITE_REQUIRES_AUTHORIZATION = 0x80
|
WRITE_REQUIRES_AUTHORIZATION = 0x80
|
||||||
|
|
||||||
PERMISSION_NAMES = {
|
@classmethod
|
||||||
READABLE: 'READABLE',
|
def from_string(cls, permissions_str: str) -> Attribute.Permissions:
|
||||||
WRITEABLE: 'WRITEABLE',
|
|
||||||
READ_REQUIRES_ENCRYPTION: 'READ_REQUIRES_ENCRYPTION',
|
|
||||||
WRITE_REQUIRES_ENCRYPTION: 'WRITE_REQUIRES_ENCRYPTION',
|
|
||||||
READ_REQUIRES_AUTHENTICATION: 'READ_REQUIRES_AUTHENTICATION',
|
|
||||||
WRITE_REQUIRES_AUTHENTICATION: 'WRITE_REQUIRES_AUTHENTICATION',
|
|
||||||
READ_REQUIRES_AUTHORIZATION: 'READ_REQUIRES_AUTHORIZATION',
|
|
||||||
WRITE_REQUIRES_AUTHORIZATION: 'WRITE_REQUIRES_AUTHORIZATION',
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def string_to_permissions(permissions_str: str):
|
|
||||||
try:
|
try:
|
||||||
return functools.reduce(
|
return functools.reduce(
|
||||||
lambda x, y: x | get_dict_key_by_value(Attribute.PERMISSION_NAMES, y),
|
lambda x, y: x | Attribute.Permissions[y],
|
||||||
permissions_str.split(","),
|
permissions_str.replace('|', ',').split(","),
|
||||||
0,
|
Attribute.Permissions(0),
|
||||||
)
|
)
|
||||||
except TypeError as exc:
|
except TypeError as exc:
|
||||||
|
# The check for `p.name is not None` here is needed because for InFlag
|
||||||
|
# enums, the .name property can be None, when the enum value is 0,
|
||||||
|
# so the type hint for .name is Optional[str].
|
||||||
|
enum_list: List[str] = [p.name for p in cls if p.name is not None]
|
||||||
|
enum_list_str = ",".join(enum_list)
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
f"Attribute::permissions error:\nExpected a string containing any of the keys, separated by commas: {','.join(Attribute.PERMISSION_NAMES.values())}\nGot: {permissions_str}"
|
f"Attribute::permissions error:\nExpected a string containing any of the keys, separated by commas: {enum_list_str }\nGot: {permissions_str}"
|
||||||
) from exc
|
) from exc
|
||||||
|
|
||||||
def __init__(self, attribute_type, permissions, value=b''):
|
# Permission flags(legacy-use only)
|
||||||
|
READABLE = Permissions.READABLE
|
||||||
|
WRITEABLE = Permissions.WRITEABLE
|
||||||
|
READ_REQUIRES_ENCRYPTION = Permissions.READ_REQUIRES_ENCRYPTION
|
||||||
|
WRITE_REQUIRES_ENCRYPTION = Permissions.WRITE_REQUIRES_ENCRYPTION
|
||||||
|
READ_REQUIRES_AUTHENTICATION = Permissions.READ_REQUIRES_AUTHENTICATION
|
||||||
|
WRITE_REQUIRES_AUTHENTICATION = Permissions.WRITE_REQUIRES_AUTHENTICATION
|
||||||
|
READ_REQUIRES_AUTHORIZATION = Permissions.READ_REQUIRES_AUTHORIZATION
|
||||||
|
WRITE_REQUIRES_AUTHORIZATION = Permissions.WRITE_REQUIRES_AUTHORIZATION
|
||||||
|
|
||||||
|
value: Union[str, bytes, ConnectionValue]
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
attribute_type: Union[str, bytes, UUID],
|
||||||
|
permissions: Union[str, Attribute.Permissions],
|
||||||
|
value: Union[str, bytes, ConnectionValue] = b'',
|
||||||
|
) -> None:
|
||||||
EventEmitter.__init__(self)
|
EventEmitter.__init__(self)
|
||||||
self.handle = 0
|
self.handle = 0
|
||||||
self.end_group_handle = 0
|
self.end_group_handle = 0
|
||||||
if isinstance(permissions, str):
|
if isinstance(permissions, str):
|
||||||
self.permissions = self.string_to_permissions(permissions)
|
self.permissions = Attribute.Permissions.from_string(permissions)
|
||||||
else:
|
else:
|
||||||
self.permissions = permissions
|
self.permissions = permissions
|
||||||
|
|
||||||
@@ -778,22 +800,26 @@ class Attribute(EventEmitter):
|
|||||||
else:
|
else:
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def encode_value(self, value):
|
def encode_value(self, value: Any) -> bytes:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def decode_value(self, value_bytes):
|
def decode_value(self, value_bytes: bytes) -> Any:
|
||||||
return value_bytes
|
return value_bytes
|
||||||
|
|
||||||
def read_value(self, connection: Connection):
|
def read_value(self, connection: Optional[Connection]) -> bytes:
|
||||||
if (
|
if (
|
||||||
self.permissions & self.READ_REQUIRES_ENCRYPTION
|
(self.permissions & self.READ_REQUIRES_ENCRYPTION)
|
||||||
) and not connection.encryption:
|
and connection is not None
|
||||||
|
and not connection.encryption
|
||||||
|
):
|
||||||
raise ATT_Error(
|
raise ATT_Error(
|
||||||
error_code=ATT_INSUFFICIENT_ENCRYPTION_ERROR, att_handle=self.handle
|
error_code=ATT_INSUFFICIENT_ENCRYPTION_ERROR, att_handle=self.handle
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
self.permissions & self.READ_REQUIRES_AUTHENTICATION
|
(self.permissions & self.READ_REQUIRES_AUTHENTICATION)
|
||||||
) and not connection.authenticated:
|
and connection is not None
|
||||||
|
and not connection.authenticated
|
||||||
|
):
|
||||||
raise ATT_Error(
|
raise ATT_Error(
|
||||||
error_code=ATT_INSUFFICIENT_AUTHENTICATION_ERROR, att_handle=self.handle
|
error_code=ATT_INSUFFICIENT_AUTHENTICATION_ERROR, att_handle=self.handle
|
||||||
)
|
)
|
||||||
@@ -803,9 +829,9 @@ class Attribute(EventEmitter):
|
|||||||
error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle
|
error_code=ATT_INSUFFICIENT_AUTHORIZATION_ERROR, att_handle=self.handle
|
||||||
)
|
)
|
||||||
|
|
||||||
if read := getattr(self.value, 'read', None):
|
if hasattr(self.value, 'read'):
|
||||||
try:
|
try:
|
||||||
value = read(connection) # pylint: disable=not-callable
|
value = self.value.read(connection)
|
||||||
except ATT_Error as error:
|
except ATT_Error as error:
|
||||||
raise ATT_Error(
|
raise ATT_Error(
|
||||||
error_code=error.error_code, att_handle=self.handle
|
error_code=error.error_code, att_handle=self.handle
|
||||||
@@ -815,7 +841,7 @@ class Attribute(EventEmitter):
|
|||||||
|
|
||||||
return self.encode_value(value)
|
return self.encode_value(value)
|
||||||
|
|
||||||
def write_value(self, connection: Connection, value_bytes):
|
def write_value(self, connection: Connection, value_bytes: bytes) -> None:
|
||||||
if (
|
if (
|
||||||
self.permissions & self.WRITE_REQUIRES_ENCRYPTION
|
self.permissions & self.WRITE_REQUIRES_ENCRYPTION
|
||||||
) and not connection.encryption:
|
) and not connection.encryption:
|
||||||
@@ -836,9 +862,9 @@ class Attribute(EventEmitter):
|
|||||||
|
|
||||||
value = self.decode_value(value_bytes)
|
value = self.decode_value(value_bytes)
|
||||||
|
|
||||||
if write := getattr(self.value, 'write', None):
|
if hasattr(self.value, 'write'):
|
||||||
try:
|
try:
|
||||||
write(connection, value) # pylint: disable=not-callable
|
self.value.write(connection, value) # pylint: disable=not-callable
|
||||||
except ATT_Error as error:
|
except ATT_Error as error:
|
||||||
raise ATT_Error(
|
raise ATT_Error(
|
||||||
error_code=error.error_code, att_handle=self.handle
|
error_code=error.error_code, att_handle=self.handle
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class BaseError(Exception):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
error_code: int | None,
|
error_code: Optional[int],
|
||||||
error_namespace: str = '',
|
error_namespace: str = '',
|
||||||
error_name: str = '',
|
error_name: str = '',
|
||||||
details: str = '',
|
details: str = '',
|
||||||
|
|||||||
@@ -1186,8 +1186,8 @@ class Device(CompositeEventEmitter):
|
|||||||
def create_l2cap_registrar(self, psm):
|
def create_l2cap_registrar(self, psm):
|
||||||
return lambda handler: self.register_l2cap_server(psm, handler)
|
return lambda handler: self.register_l2cap_server(psm, handler)
|
||||||
|
|
||||||
def register_l2cap_server(self, psm, server):
|
def register_l2cap_server(self, psm, server) -> int:
|
||||||
self.l2cap_channel_manager.register_server(psm, server)
|
return self.l2cap_channel_manager.register_server(psm, server)
|
||||||
|
|
||||||
def register_l2cap_channel_server(
|
def register_l2cap_channel_server(
|
||||||
self,
|
self,
|
||||||
@@ -1425,10 +1425,10 @@ class Device(CompositeEventEmitter):
|
|||||||
check_result=True,
|
check_result=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.advertising_own_address_type = own_address_type
|
|
||||||
self.auto_restart_advertising = auto_restart
|
|
||||||
self.advertising_type = advertising_type
|
self.advertising_type = advertising_type
|
||||||
|
self.advertising_own_address_type = own_address_type
|
||||||
self.advertising = True
|
self.advertising = True
|
||||||
|
self.auto_restart_advertising = auto_restart
|
||||||
|
|
||||||
async def stop_advertising(self) -> None:
|
async def stop_advertising(self) -> None:
|
||||||
# Disable advertising
|
# Disable advertising
|
||||||
@@ -1438,9 +1438,9 @@ class Device(CompositeEventEmitter):
|
|||||||
check_result=True,
|
check_result=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.advertising_type = None
|
||||||
self.advertising_own_address_type = None
|
self.advertising_own_address_type = None
|
||||||
self.advertising = False
|
self.advertising = False
|
||||||
self.advertising_type = None
|
|
||||||
self.auto_restart_advertising = False
|
self.auto_restart_advertising = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -2630,7 +2630,6 @@ class Device(CompositeEventEmitter):
|
|||||||
own_address_type = self.advertising_own_address_type
|
own_address_type = self.advertising_own_address_type
|
||||||
|
|
||||||
# We are no longer advertising
|
# We are no longer advertising
|
||||||
self.advertising_own_address_type = None
|
|
||||||
self.advertising = False
|
self.advertising = False
|
||||||
|
|
||||||
if own_address_type in (
|
if own_address_type in (
|
||||||
@@ -2687,7 +2686,6 @@ class Device(CompositeEventEmitter):
|
|||||||
and self.advertising
|
and self.advertising
|
||||||
and self.advertising_type.is_directed
|
and self.advertising_type.is_directed
|
||||||
):
|
):
|
||||||
self.advertising_own_address_type = None
|
|
||||||
self.advertising = False
|
self.advertising = False
|
||||||
|
|
||||||
# Notify listeners
|
# Notify listeners
|
||||||
@@ -2758,7 +2756,9 @@ class Device(CompositeEventEmitter):
|
|||||||
self.abort_on(
|
self.abort_on(
|
||||||
'flush',
|
'flush',
|
||||||
self.start_advertising(
|
self.start_advertising(
|
||||||
advertising_type=self.advertising_type, auto_restart=True
|
advertising_type=self.advertising_type,
|
||||||
|
own_address_type=self.advertising_own_address_type,
|
||||||
|
auto_restart=True,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import enum
|
|||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
from typing import Optional, Sequence, List
|
from typing import Optional, Sequence, Iterable, List, Union
|
||||||
|
|
||||||
from .colors import color
|
from .colors import color
|
||||||
from .core import UUID, get_dict_key_by_value
|
from .core import UUID, get_dict_key_by_value
|
||||||
@@ -187,7 +187,7 @@ GATT_CENTRAL_ADDRESS_RESOLUTION__CHARACTERISTIC = UUID.from_16_bi
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def show_services(services):
|
def show_services(services: Iterable[Service]) -> None:
|
||||||
for service in services:
|
for service in services:
|
||||||
print(color(str(service), 'cyan'))
|
print(color(str(service), 'cyan'))
|
||||||
|
|
||||||
@@ -210,11 +210,11 @@ class Service(Attribute):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
uuid,
|
uuid: Union[str, UUID],
|
||||||
characteristics: List[Characteristic],
|
characteristics: List[Characteristic],
|
||||||
primary=True,
|
primary=True,
|
||||||
included_services: List[Service] = [],
|
included_services: List[Service] = [],
|
||||||
):
|
) -> None:
|
||||||
# Convert the uuid to a UUID object if it isn't already
|
# Convert the uuid to a UUID object if it isn't already
|
||||||
if isinstance(uuid, str):
|
if isinstance(uuid, str):
|
||||||
uuid = UUID(uuid)
|
uuid = UUID(uuid)
|
||||||
@@ -239,7 +239,7 @@ class Service(Attribute):
|
|||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'Service(handle=0x{self.handle:04X}, '
|
f'Service(handle=0x{self.handle:04X}, '
|
||||||
f'end=0x{self.end_group_handle:04X}, '
|
f'end=0x{self.end_group_handle:04X}, '
|
||||||
@@ -255,9 +255,11 @@ class TemplateService(Service):
|
|||||||
to expose their UUID as a class property
|
to expose their UUID as a class property
|
||||||
'''
|
'''
|
||||||
|
|
||||||
UUID: Optional[UUID] = None
|
UUID: UUID
|
||||||
|
|
||||||
def __init__(self, characteristics, primary=True):
|
def __init__(
|
||||||
|
self, characteristics: List[Characteristic], primary: bool = True
|
||||||
|
) -> None:
|
||||||
super().__init__(self.UUID, characteristics, primary)
|
super().__init__(self.UUID, characteristics, primary)
|
||||||
|
|
||||||
|
|
||||||
@@ -269,7 +271,7 @@ class IncludedServiceDeclaration(Attribute):
|
|||||||
|
|
||||||
service: Service
|
service: Service
|
||||||
|
|
||||||
def __init__(self, service):
|
def __init__(self, service: Service) -> None:
|
||||||
declaration_bytes = struct.pack(
|
declaration_bytes = struct.pack(
|
||||||
'<HH2s', service.handle, service.end_group_handle, service.uuid.to_bytes()
|
'<HH2s', service.handle, service.end_group_handle, service.uuid.to_bytes()
|
||||||
)
|
)
|
||||||
@@ -278,7 +280,7 @@ class IncludedServiceDeclaration(Attribute):
|
|||||||
)
|
)
|
||||||
self.service = service
|
self.service = service
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'IncludedServiceDefinition(handle=0x{self.handle:04X}, '
|
f'IncludedServiceDefinition(handle=0x{self.handle:04X}, '
|
||||||
f'group_starting_handle=0x{self.service.handle:04X}, '
|
f'group_starting_handle=0x{self.service.handle:04X}, '
|
||||||
@@ -326,7 +328,7 @@ class Characteristic(Attribute):
|
|||||||
f"Characteristic.Properties::from_string() error:\nExpected a string containing any of the keys, separated by , or |: {enum_list_str}\nGot: {properties_str}"
|
f"Characteristic.Properties::from_string() error:\nExpected a string containing any of the keys, separated by , or |: {enum_list_str}\nGot: {properties_str}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
# NOTE: we override this method to offer a consistent result between python
|
# NOTE: we override this method to offer a consistent result between python
|
||||||
# versions: the value returned by IntFlag.__str__() changed in version 11.
|
# versions: the value returned by IntFlag.__str__() changed in version 11.
|
||||||
return '|'.join(
|
return '|'.join(
|
||||||
@@ -348,10 +350,10 @@ class Characteristic(Attribute):
|
|||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
uuid,
|
uuid: Union[str, bytes, UUID],
|
||||||
properties: Characteristic.Properties,
|
properties: Characteristic.Properties,
|
||||||
permissions,
|
permissions: Union[str, Attribute.Permissions],
|
||||||
value=b'',
|
value: Union[str, bytes, CharacteristicValue] = b'',
|
||||||
descriptors: Sequence[Descriptor] = (),
|
descriptors: Sequence[Descriptor] = (),
|
||||||
):
|
):
|
||||||
super().__init__(uuid, permissions, value)
|
super().__init__(uuid, permissions, value)
|
||||||
@@ -369,7 +371,7 @@ class Characteristic(Attribute):
|
|||||||
def has_properties(self, properties: Characteristic.Properties) -> bool:
|
def has_properties(self, properties: Characteristic.Properties) -> bool:
|
||||||
return self.properties & properties == properties
|
return self.properties & properties == properties
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'Characteristic(handle=0x{self.handle:04X}, '
|
f'Characteristic(handle=0x{self.handle:04X}, '
|
||||||
f'end=0x{self.end_group_handle:04X}, '
|
f'end=0x{self.end_group_handle:04X}, '
|
||||||
@@ -386,7 +388,7 @@ class CharacteristicDeclaration(Attribute):
|
|||||||
|
|
||||||
characteristic: Characteristic
|
characteristic: Characteristic
|
||||||
|
|
||||||
def __init__(self, characteristic, value_handle):
|
def __init__(self, characteristic: Characteristic, value_handle: int) -> None:
|
||||||
declaration_bytes = (
|
declaration_bytes = (
|
||||||
struct.pack('<BH', characteristic.properties, value_handle)
|
struct.pack('<BH', characteristic.properties, value_handle)
|
||||||
+ characteristic.uuid.to_pdu_bytes()
|
+ characteristic.uuid.to_pdu_bytes()
|
||||||
@@ -397,7 +399,7 @@ class CharacteristicDeclaration(Attribute):
|
|||||||
self.value_handle = value_handle
|
self.value_handle = value_handle
|
||||||
self.characteristic = characteristic
|
self.characteristic = characteristic
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'CharacteristicDeclaration(handle=0x{self.handle:04X}, '
|
f'CharacteristicDeclaration(handle=0x{self.handle:04X}, '
|
||||||
f'value_handle=0x{self.value_handle:04X}, '
|
f'value_handle=0x{self.value_handle:04X}, '
|
||||||
@@ -520,7 +522,7 @@ class CharacteristicAdapter:
|
|||||||
|
|
||||||
return self.wrapped_characteristic.unsubscribe(subscriber)
|
return self.wrapped_characteristic.unsubscribe(subscriber)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
wrapped = str(self.wrapped_characteristic)
|
wrapped = str(self.wrapped_characteristic)
|
||||||
return f'{self.__class__.__name__}({wrapped})'
|
return f'{self.__class__.__name__}({wrapped})'
|
||||||
|
|
||||||
@@ -600,10 +602,10 @@ class UTF8CharacteristicAdapter(CharacteristicAdapter):
|
|||||||
Adapter that converts strings to/from bytes using UTF-8 encoding
|
Adapter that converts strings to/from bytes using UTF-8 encoding
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def encode_value(self, value):
|
def encode_value(self, value: str) -> bytes:
|
||||||
return value.encode('utf-8')
|
return value.encode('utf-8')
|
||||||
|
|
||||||
def decode_value(self, value):
|
def decode_value(self, value: bytes) -> str:
|
||||||
return value.decode('utf-8')
|
return value.decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
@@ -613,7 +615,7 @@ class Descriptor(Attribute):
|
|||||||
See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations
|
See Vol 3, Part G - 3.3.3 Characteristic Descriptor Declarations
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'Descriptor(handle=0x{self.handle:04X}, '
|
f'Descriptor(handle=0x{self.handle:04X}, '
|
||||||
f'type={self.type}, '
|
f'type={self.type}, '
|
||||||
|
|||||||
@@ -28,7 +28,18 @@ import asyncio
|
|||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import List, Optional, Dict, Tuple, Callable, Union, Any
|
from typing import (
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Dict,
|
||||||
|
Tuple,
|
||||||
|
Callable,
|
||||||
|
Union,
|
||||||
|
Any,
|
||||||
|
Iterable,
|
||||||
|
Type,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
)
|
||||||
|
|
||||||
from pyee import EventEmitter
|
from pyee import EventEmitter
|
||||||
|
|
||||||
@@ -66,8 +77,12 @@ from .gatt import (
|
|||||||
GATT_INCLUDE_ATTRIBUTE_TYPE,
|
GATT_INCLUDE_ATTRIBUTE_TYPE,
|
||||||
Characteristic,
|
Characteristic,
|
||||||
ClientCharacteristicConfigurationBits,
|
ClientCharacteristicConfigurationBits,
|
||||||
|
TemplateService,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bumble.device import Connection
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Logging
|
# Logging
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -78,16 +93,16 @@ logger = logging.getLogger(__name__)
|
|||||||
# Proxies
|
# Proxies
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class AttributeProxy(EventEmitter):
|
class AttributeProxy(EventEmitter):
|
||||||
client: Client
|
def __init__(
|
||||||
|
self, client: Client, handle: int, end_group_handle: int, attribute_type: UUID
|
||||||
def __init__(self, client, handle, end_group_handle, attribute_type):
|
) -> None:
|
||||||
EventEmitter.__init__(self)
|
EventEmitter.__init__(self)
|
||||||
self.client = client
|
self.client = client
|
||||||
self.handle = handle
|
self.handle = handle
|
||||||
self.end_group_handle = end_group_handle
|
self.end_group_handle = end_group_handle
|
||||||
self.type = attribute_type
|
self.type = attribute_type
|
||||||
|
|
||||||
async def read_value(self, no_long_read=False):
|
async def read_value(self, no_long_read: bool = False) -> bytes:
|
||||||
return self.decode_value(
|
return self.decode_value(
|
||||||
await self.client.read_value(self.handle, no_long_read)
|
await self.client.read_value(self.handle, no_long_read)
|
||||||
)
|
)
|
||||||
@@ -97,13 +112,13 @@ class AttributeProxy(EventEmitter):
|
|||||||
self.handle, self.encode_value(value), with_response
|
self.handle, self.encode_value(value), with_response
|
||||||
)
|
)
|
||||||
|
|
||||||
def encode_value(self, value):
|
def encode_value(self, value: Any) -> bytes:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def decode_value(self, value_bytes):
|
def decode_value(self, value_bytes: bytes) -> Any:
|
||||||
return value_bytes
|
return value_bytes
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return f'Attribute(handle=0x{self.handle:04X}, type={self.type})'
|
return f'Attribute(handle=0x{self.handle:04X}, type={self.type})'
|
||||||
|
|
||||||
|
|
||||||
@@ -136,14 +151,14 @@ class ServiceProxy(AttributeProxy):
|
|||||||
def get_characteristics_by_uuid(self, uuid):
|
def get_characteristics_by_uuid(self, uuid):
|
||||||
return self.client.get_characteristics_by_uuid(uuid, self)
|
return self.client.get_characteristics_by_uuid(uuid, self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return f'Service(handle=0x{self.handle:04X}, uuid={self.uuid})'
|
return f'Service(handle=0x{self.handle:04X}, uuid={self.uuid})'
|
||||||
|
|
||||||
|
|
||||||
class CharacteristicProxy(AttributeProxy):
|
class CharacteristicProxy(AttributeProxy):
|
||||||
properties: Characteristic.Properties
|
properties: Characteristic.Properties
|
||||||
descriptors: List[DescriptorProxy]
|
descriptors: List[DescriptorProxy]
|
||||||
subscribers: Dict[Any, Callable]
|
subscribers: Dict[Any, Callable[[bytes], Any]]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -171,7 +186,9 @@ class CharacteristicProxy(AttributeProxy):
|
|||||||
return await self.client.discover_descriptors(self)
|
return await self.client.discover_descriptors(self)
|
||||||
|
|
||||||
async def subscribe(
|
async def subscribe(
|
||||||
self, subscriber: Optional[Callable] = None, prefer_notify=True
|
self,
|
||||||
|
subscriber: Optional[Callable[[bytes], Any]] = None,
|
||||||
|
prefer_notify: bool = True,
|
||||||
):
|
):
|
||||||
if subscriber is not None:
|
if subscriber is not None:
|
||||||
if subscriber in self.subscribers:
|
if subscriber in self.subscribers:
|
||||||
@@ -195,7 +212,7 @@ class CharacteristicProxy(AttributeProxy):
|
|||||||
|
|
||||||
return await self.client.unsubscribe(self, subscriber)
|
return await self.client.unsubscribe(self, subscriber)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'Characteristic(handle=0x{self.handle:04X}, '
|
f'Characteristic(handle=0x{self.handle:04X}, '
|
||||||
f'uuid={self.uuid}, '
|
f'uuid={self.uuid}, '
|
||||||
@@ -207,7 +224,7 @@ class DescriptorProxy(AttributeProxy):
|
|||||||
def __init__(self, client, handle, descriptor_type):
|
def __init__(self, client, handle, descriptor_type):
|
||||||
super().__init__(client, handle, 0, descriptor_type)
|
super().__init__(client, handle, 0, descriptor_type)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return f'Descriptor(handle=0x{self.handle:04X}, type={self.type})'
|
return f'Descriptor(handle=0x{self.handle:04X}, type={self.type})'
|
||||||
|
|
||||||
|
|
||||||
@@ -216,8 +233,10 @@ class ProfileServiceProxy:
|
|||||||
Base class for profile-specific service proxies
|
Base class for profile-specific service proxies
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
SERVICE_CLASS: Type[TemplateService]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_client(cls, client):
|
def from_client(cls, client: Client) -> ProfileServiceProxy:
|
||||||
return ServiceProxy.from_client(cls, client, cls.SERVICE_CLASS.UUID)
|
return ServiceProxy.from_client(cls, client, cls.SERVICE_CLASS.UUID)
|
||||||
|
|
||||||
|
|
||||||
@@ -227,8 +246,12 @@ class ProfileServiceProxy:
|
|||||||
class Client:
|
class Client:
|
||||||
services: List[ServiceProxy]
|
services: List[ServiceProxy]
|
||||||
cached_values: Dict[int, Tuple[datetime, bytes]]
|
cached_values: Dict[int, Tuple[datetime, bytes]]
|
||||||
|
notification_subscribers: Dict[int, Callable[[bytes], Any]]
|
||||||
|
indication_subscribers: Dict[int, Callable[[bytes], Any]]
|
||||||
|
pending_response: Optional[asyncio.futures.Future[ATT_PDU]]
|
||||||
|
pending_request: Optional[ATT_PDU]
|
||||||
|
|
||||||
def __init__(self, connection):
|
def __init__(self, connection: Connection) -> None:
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.mtu_exchange_done = False
|
self.mtu_exchange_done = False
|
||||||
self.request_semaphore = asyncio.Semaphore(1)
|
self.request_semaphore = asyncio.Semaphore(1)
|
||||||
@@ -241,16 +264,16 @@ class Client:
|
|||||||
self.services = []
|
self.services = []
|
||||||
self.cached_values = {}
|
self.cached_values = {}
|
||||||
|
|
||||||
def send_gatt_pdu(self, pdu):
|
def send_gatt_pdu(self, pdu: bytes) -> None:
|
||||||
self.connection.send_l2cap_pdu(ATT_CID, pdu)
|
self.connection.send_l2cap_pdu(ATT_CID, pdu)
|
||||||
|
|
||||||
async def send_command(self, command):
|
async def send_command(self, command: ATT_PDU) -> None:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'GATT Command from client: [0x{self.connection.handle:04X}] {command}'
|
f'GATT Command from client: [0x{self.connection.handle:04X}] {command}'
|
||||||
)
|
)
|
||||||
self.send_gatt_pdu(command.to_bytes())
|
self.send_gatt_pdu(command.to_bytes())
|
||||||
|
|
||||||
async def send_request(self, request):
|
async def send_request(self, request: ATT_PDU):
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'GATT Request from client: [0x{self.connection.handle:04X}] {request}'
|
f'GATT Request from client: [0x{self.connection.handle:04X}] {request}'
|
||||||
)
|
)
|
||||||
@@ -279,14 +302,14 @@ class Client:
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def send_confirmation(self, confirmation):
|
def send_confirmation(self, confirmation: ATT_Handle_Value_Confirmation) -> None:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'GATT Confirmation from client: [0x{self.connection.handle:04X}] '
|
f'GATT Confirmation from client: [0x{self.connection.handle:04X}] '
|
||||||
f'{confirmation}'
|
f'{confirmation}'
|
||||||
)
|
)
|
||||||
self.send_gatt_pdu(confirmation.to_bytes())
|
self.send_gatt_pdu(confirmation.to_bytes())
|
||||||
|
|
||||||
async def request_mtu(self, mtu):
|
async def request_mtu(self, mtu: int) -> int:
|
||||||
# Check the range
|
# Check the range
|
||||||
if mtu < ATT_DEFAULT_MTU:
|
if mtu < ATT_DEFAULT_MTU:
|
||||||
raise ValueError(f'MTU must be >= {ATT_DEFAULT_MTU}')
|
raise ValueError(f'MTU must be >= {ATT_DEFAULT_MTU}')
|
||||||
@@ -313,10 +336,12 @@ class Client:
|
|||||||
|
|
||||||
return self.connection.att_mtu
|
return self.connection.att_mtu
|
||||||
|
|
||||||
def get_services_by_uuid(self, uuid):
|
def get_services_by_uuid(self, uuid: UUID) -> List[ServiceProxy]:
|
||||||
return [service for service in self.services if service.uuid == uuid]
|
return [service for service in self.services if service.uuid == uuid]
|
||||||
|
|
||||||
def get_characteristics_by_uuid(self, uuid, service=None):
|
def get_characteristics_by_uuid(
|
||||||
|
self, uuid: UUID, service: Optional[ServiceProxy] = None
|
||||||
|
) -> List[CharacteristicProxy]:
|
||||||
services = [service] if service else self.services
|
services = [service] if service else self.services
|
||||||
return [
|
return [
|
||||||
c
|
c
|
||||||
@@ -363,7 +388,7 @@ class Client:
|
|||||||
if not already_known:
|
if not already_known:
|
||||||
self.services.append(service)
|
self.services.append(service)
|
||||||
|
|
||||||
async def discover_services(self, uuids=None) -> List[ServiceProxy]:
|
async def discover_services(self, uuids: Iterable[UUID] = []) -> List[ServiceProxy]:
|
||||||
'''
|
'''
|
||||||
See Vol 3, Part G - 4.4.1 Discover All Primary Services
|
See Vol 3, Part G - 4.4.1 Discover All Primary Services
|
||||||
'''
|
'''
|
||||||
@@ -435,7 +460,7 @@ class Client:
|
|||||||
|
|
||||||
return services
|
return services
|
||||||
|
|
||||||
async def discover_service(self, uuid):
|
async def discover_service(self, uuid: Union[str, UUID]) -> List[ServiceProxy]:
|
||||||
'''
|
'''
|
||||||
See Vol 3, Part G - 4.4.2 Discover Primary Service by Service UUID
|
See Vol 3, Part G - 4.4.2 Discover Primary Service by Service UUID
|
||||||
'''
|
'''
|
||||||
@@ -468,7 +493,7 @@ class Client:
|
|||||||
f'{HCI_Constant.error_name(response.error_code)}'
|
f'{HCI_Constant.error_name(response.error_code)}'
|
||||||
)
|
)
|
||||||
# TODO raise appropriate exception
|
# TODO raise appropriate exception
|
||||||
return
|
return []
|
||||||
break
|
break
|
||||||
|
|
||||||
for attribute_handle, end_group_handle in response.handles_information:
|
for attribute_handle, end_group_handle in response.handles_information:
|
||||||
@@ -480,7 +505,7 @@ class Client:
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
f'bogus handle values: {attribute_handle} {end_group_handle}'
|
f'bogus handle values: {attribute_handle} {end_group_handle}'
|
||||||
)
|
)
|
||||||
return
|
return []
|
||||||
|
|
||||||
# Create a service proxy for this service
|
# Create a service proxy for this service
|
||||||
service = ServiceProxy(
|
service = ServiceProxy(
|
||||||
@@ -721,7 +746,7 @@ class Client:
|
|||||||
|
|
||||||
return descriptors
|
return descriptors
|
||||||
|
|
||||||
async def discover_attributes(self):
|
async def discover_attributes(self) -> List[AttributeProxy]:
|
||||||
'''
|
'''
|
||||||
Discover all attributes, regardless of type
|
Discover all attributes, regardless of type
|
||||||
'''
|
'''
|
||||||
@@ -844,7 +869,9 @@ class Client:
|
|||||||
# No more subscribers left
|
# No more subscribers left
|
||||||
await self.write_value(cccd, b'\x00\x00', with_response=True)
|
await self.write_value(cccd, b'\x00\x00', with_response=True)
|
||||||
|
|
||||||
async def read_value(self, attribute, no_long_read=False):
|
async def read_value(
|
||||||
|
self, attribute: Union[int, AttributeProxy], no_long_read: bool = False
|
||||||
|
) -> Any:
|
||||||
'''
|
'''
|
||||||
See Vol 3, Part G - 4.8.1 Read Characteristic Value
|
See Vol 3, Part G - 4.8.1 Read Characteristic Value
|
||||||
|
|
||||||
@@ -905,7 +932,9 @@ class Client:
|
|||||||
# Return the value as bytes
|
# Return the value as bytes
|
||||||
return attribute_value
|
return attribute_value
|
||||||
|
|
||||||
async def read_characteristics_by_uuid(self, uuid, service):
|
async def read_characteristics_by_uuid(
|
||||||
|
self, uuid: UUID, service: Optional[ServiceProxy]
|
||||||
|
) -> List[bytes]:
|
||||||
'''
|
'''
|
||||||
See Vol 3, Part G - 4.8.2 Read Using Characteristic UUID
|
See Vol 3, Part G - 4.8.2 Read Using Characteristic UUID
|
||||||
'''
|
'''
|
||||||
@@ -960,7 +989,12 @@ class Client:
|
|||||||
|
|
||||||
return characteristics_values
|
return characteristics_values
|
||||||
|
|
||||||
async def write_value(self, attribute, value, with_response=False):
|
async def write_value(
|
||||||
|
self,
|
||||||
|
attribute: Union[int, AttributeProxy],
|
||||||
|
value: bytes,
|
||||||
|
with_response: bool = False,
|
||||||
|
) -> None:
|
||||||
'''
|
'''
|
||||||
See Vol 3, Part G - 4.9.1 Write Without Response & 4.9.3 Write Characteristic
|
See Vol 3, Part G - 4.9.1 Write Without Response & 4.9.3 Write Characteristic
|
||||||
Value
|
Value
|
||||||
@@ -990,7 +1024,7 @@ class Client:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_gatt_pdu(self, att_pdu):
|
def on_gatt_pdu(self, att_pdu: ATT_PDU) -> None:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'GATT Response to client: [0x{self.connection.handle:04X}] {att_pdu}'
|
f'GATT Response to client: [0x{self.connection.handle:04X}] {att_pdu}'
|
||||||
)
|
)
|
||||||
@@ -1013,6 +1047,7 @@ class Client:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Return the response to the coroutine that is waiting for it
|
# Return the response to the coroutine that is waiting for it
|
||||||
|
assert self.pending_response is not None
|
||||||
self.pending_response.set_result(att_pdu)
|
self.pending_response.set_result(att_pdu)
|
||||||
else:
|
else:
|
||||||
handler_name = f'on_{att_pdu.name.lower()}'
|
handler_name = f'on_{att_pdu.name.lower()}'
|
||||||
@@ -1060,7 +1095,7 @@ class Client:
|
|||||||
# Confirm that we received the indication
|
# Confirm that we received the indication
|
||||||
self.send_confirmation(ATT_Handle_Value_Confirmation())
|
self.send_confirmation(ATT_Handle_Value_Confirmation())
|
||||||
|
|
||||||
def cache_value(self, attribute_handle: int, value: bytes):
|
def cache_value(self, attribute_handle: int, value: bytes) -> None:
|
||||||
self.cached_values[attribute_handle] = (
|
self.cached_values[attribute_handle] = (
|
||||||
datetime.now(),
|
datetime.now(),
|
||||||
value,
|
value,
|
||||||
|
|||||||
@@ -23,11 +23,12 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Imports
|
# Imports
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import struct
|
import struct
|
||||||
from typing import List, Tuple, Optional, TypeVar, Type
|
from typing import List, Tuple, Optional, TypeVar, Type, Dict, Iterable, TYPE_CHECKING
|
||||||
from pyee import EventEmitter
|
from pyee import EventEmitter
|
||||||
|
|
||||||
from .colors import color
|
from .colors import color
|
||||||
@@ -42,6 +43,7 @@ from .att import (
|
|||||||
ATT_INVALID_OFFSET_ERROR,
|
ATT_INVALID_OFFSET_ERROR,
|
||||||
ATT_REQUEST_NOT_SUPPORTED_ERROR,
|
ATT_REQUEST_NOT_SUPPORTED_ERROR,
|
||||||
ATT_REQUESTS,
|
ATT_REQUESTS,
|
||||||
|
ATT_PDU,
|
||||||
ATT_UNLIKELY_ERROR_ERROR,
|
ATT_UNLIKELY_ERROR_ERROR,
|
||||||
ATT_UNSUPPORTED_GROUP_TYPE_ERROR,
|
ATT_UNSUPPORTED_GROUP_TYPE_ERROR,
|
||||||
ATT_Error,
|
ATT_Error,
|
||||||
@@ -73,6 +75,8 @@ from .gatt import (
|
|||||||
Service,
|
Service,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from bumble.device import Device, Connection
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Logging
|
# Logging
|
||||||
@@ -91,8 +95,13 @@ GATT_SERVER_DEFAULT_MAX_MTU = 517
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Server(EventEmitter):
|
class Server(EventEmitter):
|
||||||
attributes: List[Attribute]
|
attributes: List[Attribute]
|
||||||
|
services: List[Service]
|
||||||
|
attributes_by_handle: Dict[int, Attribute]
|
||||||
|
subscribers: Dict[int, Dict[int, bytes]]
|
||||||
|
indication_semaphores: defaultdict[int, asyncio.Semaphore]
|
||||||
|
pending_confirmations: defaultdict[int, Optional[asyncio.futures.Future]]
|
||||||
|
|
||||||
def __init__(self, device):
|
def __init__(self, device: Device) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.device = device
|
self.device = device
|
||||||
self.services = []
|
self.services = []
|
||||||
@@ -107,16 +116,16 @@ class Server(EventEmitter):
|
|||||||
self.indication_semaphores = defaultdict(lambda: asyncio.Semaphore(1))
|
self.indication_semaphores = defaultdict(lambda: asyncio.Semaphore(1))
|
||||||
self.pending_confirmations = defaultdict(lambda: None)
|
self.pending_confirmations = defaultdict(lambda: None)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "\n".join(map(str, self.attributes))
|
return "\n".join(map(str, self.attributes))
|
||||||
|
|
||||||
def send_gatt_pdu(self, connection_handle, pdu):
|
def send_gatt_pdu(self, connection_handle: int, pdu: bytes) -> None:
|
||||||
self.device.send_l2cap_pdu(connection_handle, ATT_CID, pdu)
|
self.device.send_l2cap_pdu(connection_handle, ATT_CID, pdu)
|
||||||
|
|
||||||
def next_handle(self):
|
def next_handle(self) -> int:
|
||||||
return 1 + len(self.attributes)
|
return 1 + len(self.attributes)
|
||||||
|
|
||||||
def get_advertising_service_data(self):
|
def get_advertising_service_data(self) -> Dict[Attribute, bytes]:
|
||||||
return {
|
return {
|
||||||
attribute: data
|
attribute: data
|
||||||
for attribute in self.attributes
|
for attribute in self.attributes
|
||||||
@@ -124,7 +133,7 @@ class Server(EventEmitter):
|
|||||||
and (data := attribute.get_advertising_data())
|
and (data := attribute.get_advertising_data())
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_attribute(self, handle):
|
def get_attribute(self, handle: int) -> Optional[Attribute]:
|
||||||
attribute = self.attributes_by_handle.get(handle)
|
attribute = self.attributes_by_handle.get(handle)
|
||||||
if attribute:
|
if attribute:
|
||||||
return attribute
|
return attribute
|
||||||
@@ -173,12 +182,17 @@ class Server(EventEmitter):
|
|||||||
|
|
||||||
return next(
|
return next(
|
||||||
(
|
(
|
||||||
(attribute, self.get_attribute(attribute.characteristic.handle))
|
(
|
||||||
|
attribute,
|
||||||
|
self.get_attribute(attribute.characteristic.handle),
|
||||||
|
) # type: ignore
|
||||||
for attribute in map(
|
for attribute in map(
|
||||||
self.get_attribute,
|
self.get_attribute,
|
||||||
range(service_handle.handle, service_handle.end_group_handle + 1),
|
range(service_handle.handle, service_handle.end_group_handle + 1),
|
||||||
)
|
)
|
||||||
if attribute.type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
if attribute is not None
|
||||||
|
and attribute.type == GATT_CHARACTERISTIC_ATTRIBUTE_TYPE
|
||||||
|
and isinstance(attribute, CharacteristicDeclaration)
|
||||||
and attribute.characteristic.uuid == characteristic_uuid
|
and attribute.characteristic.uuid == characteristic_uuid
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
@@ -197,7 +211,7 @@ class Server(EventEmitter):
|
|||||||
|
|
||||||
return next(
|
return next(
|
||||||
(
|
(
|
||||||
attribute
|
attribute # type: ignore
|
||||||
for attribute in map(
|
for attribute in map(
|
||||||
self.get_attribute,
|
self.get_attribute,
|
||||||
range(
|
range(
|
||||||
@@ -205,12 +219,12 @@ class Server(EventEmitter):
|
|||||||
characteristic_value.end_group_handle + 1,
|
characteristic_value.end_group_handle + 1,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if attribute.type == descriptor_uuid
|
if attribute is not None and attribute.type == descriptor_uuid
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_attribute(self, attribute):
|
def add_attribute(self, attribute: Attribute) -> None:
|
||||||
# Assign a handle to this attribute
|
# Assign a handle to this attribute
|
||||||
attribute.handle = self.next_handle()
|
attribute.handle = self.next_handle()
|
||||||
attribute.end_group_handle = (
|
attribute.end_group_handle = (
|
||||||
@@ -220,7 +234,7 @@ class Server(EventEmitter):
|
|||||||
# Add this attribute to the list
|
# Add this attribute to the list
|
||||||
self.attributes.append(attribute)
|
self.attributes.append(attribute)
|
||||||
|
|
||||||
def add_service(self, service: Service):
|
def add_service(self, service: Service) -> None:
|
||||||
# Add the service attribute to the DB
|
# Add the service attribute to the DB
|
||||||
self.add_attribute(service)
|
self.add_attribute(service)
|
||||||
|
|
||||||
@@ -285,11 +299,13 @@ class Server(EventEmitter):
|
|||||||
service.end_group_handle = self.attributes[-1].handle
|
service.end_group_handle = self.attributes[-1].handle
|
||||||
self.services.append(service)
|
self.services.append(service)
|
||||||
|
|
||||||
def add_services(self, services):
|
def add_services(self, services: Iterable[Service]) -> None:
|
||||||
for service in services:
|
for service in services:
|
||||||
self.add_service(service)
|
self.add_service(service)
|
||||||
|
|
||||||
def read_cccd(self, connection, characteristic):
|
def read_cccd(
|
||||||
|
self, connection: Optional[Connection], characteristic: Characteristic
|
||||||
|
) -> bytes:
|
||||||
if connection is None:
|
if connection is None:
|
||||||
return bytes([0, 0])
|
return bytes([0, 0])
|
||||||
|
|
||||||
@@ -300,7 +316,12 @@ class Server(EventEmitter):
|
|||||||
|
|
||||||
return cccd or bytes([0, 0])
|
return cccd or bytes([0, 0])
|
||||||
|
|
||||||
def write_cccd(self, connection, characteristic, value):
|
def write_cccd(
|
||||||
|
self,
|
||||||
|
connection: Connection,
|
||||||
|
characteristic: Characteristic,
|
||||||
|
value: bytes,
|
||||||
|
) -> None:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'Subscription update for connection=0x{connection.handle:04X}, '
|
f'Subscription update for connection=0x{connection.handle:04X}, '
|
||||||
f'handle=0x{characteristic.handle:04X}: {value.hex()}'
|
f'handle=0x{characteristic.handle:04X}: {value.hex()}'
|
||||||
@@ -327,13 +348,19 @@ class Server(EventEmitter):
|
|||||||
indicate_enabled,
|
indicate_enabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
def send_response(self, connection, response):
|
def send_response(self, connection: Connection, response: ATT_PDU) -> None:
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'GATT Response from server: [0x{connection.handle:04X}] {response}'
|
f'GATT Response from server: [0x{connection.handle:04X}] {response}'
|
||||||
)
|
)
|
||||||
self.send_gatt_pdu(connection.handle, response.to_bytes())
|
self.send_gatt_pdu(connection.handle, response.to_bytes())
|
||||||
|
|
||||||
async def notify_subscriber(self, connection, attribute, value=None, force=False):
|
async def notify_subscriber(
|
||||||
|
self,
|
||||||
|
connection: Connection,
|
||||||
|
attribute: Attribute,
|
||||||
|
value: Optional[bytes] = None,
|
||||||
|
force: bool = False,
|
||||||
|
) -> None:
|
||||||
# Check if there's a subscriber
|
# Check if there's a subscriber
|
||||||
if not force:
|
if not force:
|
||||||
subscribers = self.subscribers.get(connection.handle)
|
subscribers = self.subscribers.get(connection.handle)
|
||||||
@@ -370,7 +397,13 @@ class Server(EventEmitter):
|
|||||||
)
|
)
|
||||||
self.send_gatt_pdu(connection.handle, bytes(notification))
|
self.send_gatt_pdu(connection.handle, bytes(notification))
|
||||||
|
|
||||||
async def indicate_subscriber(self, connection, attribute, value=None, force=False):
|
async def indicate_subscriber(
|
||||||
|
self,
|
||||||
|
connection: Connection,
|
||||||
|
attribute: Attribute,
|
||||||
|
value: Optional[bytes] = None,
|
||||||
|
force: bool = False,
|
||||||
|
) -> None:
|
||||||
# Check if there's a subscriber
|
# Check if there's a subscriber
|
||||||
if not force:
|
if not force:
|
||||||
subscribers = self.subscribers.get(connection.handle)
|
subscribers = self.subscribers.get(connection.handle)
|
||||||
@@ -411,15 +444,13 @@ class Server(EventEmitter):
|
|||||||
assert self.pending_confirmations[connection.handle] is None
|
assert self.pending_confirmations[connection.handle] is None
|
||||||
|
|
||||||
# Create a future value to hold the eventual response
|
# Create a future value to hold the eventual response
|
||||||
self.pending_confirmations[
|
pending_confirmation = self.pending_confirmations[
|
||||||
connection.handle
|
connection.handle
|
||||||
] = asyncio.get_running_loop().create_future()
|
] = asyncio.get_running_loop().create_future()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.send_gatt_pdu(connection.handle, indication.to_bytes())
|
self.send_gatt_pdu(connection.handle, indication.to_bytes())
|
||||||
await asyncio.wait_for(
|
await asyncio.wait_for(pending_confirmation, GATT_REQUEST_TIMEOUT)
|
||||||
self.pending_confirmations[connection.handle], GATT_REQUEST_TIMEOUT
|
|
||||||
)
|
|
||||||
except asyncio.TimeoutError as error:
|
except asyncio.TimeoutError as error:
|
||||||
logger.warning(color('!!! GATT Indicate timeout', 'red'))
|
logger.warning(color('!!! GATT Indicate timeout', 'red'))
|
||||||
raise TimeoutError(f'GATT timeout for {indication.name}') from error
|
raise TimeoutError(f'GATT timeout for {indication.name}') from error
|
||||||
@@ -427,8 +458,12 @@ class Server(EventEmitter):
|
|||||||
self.pending_confirmations[connection.handle] = None
|
self.pending_confirmations[connection.handle] = None
|
||||||
|
|
||||||
async def notify_or_indicate_subscribers(
|
async def notify_or_indicate_subscribers(
|
||||||
self, indicate, attribute, value=None, force=False
|
self,
|
||||||
):
|
indicate: bool,
|
||||||
|
attribute: Attribute,
|
||||||
|
value: Optional[bytes] = None,
|
||||||
|
force: bool = False,
|
||||||
|
) -> None:
|
||||||
# Get all the connections for which there's at least one subscription
|
# Get all the connections for which there's at least one subscription
|
||||||
connections = [
|
connections = [
|
||||||
connection
|
connection
|
||||||
@@ -450,13 +485,23 @@ class Server(EventEmitter):
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
async def notify_subscribers(self, attribute, value=None, force=False):
|
async def notify_subscribers(
|
||||||
|
self,
|
||||||
|
attribute: Attribute,
|
||||||
|
value: Optional[bytes] = None,
|
||||||
|
force: bool = False,
|
||||||
|
):
|
||||||
return await self.notify_or_indicate_subscribers(False, attribute, value, force)
|
return await self.notify_or_indicate_subscribers(False, attribute, value, force)
|
||||||
|
|
||||||
async def indicate_subscribers(self, attribute, value=None, force=False):
|
async def indicate_subscribers(
|
||||||
|
self,
|
||||||
|
attribute: Attribute,
|
||||||
|
value: Optional[bytes] = None,
|
||||||
|
force: bool = False,
|
||||||
|
):
|
||||||
return await self.notify_or_indicate_subscribers(True, attribute, value, force)
|
return await self.notify_or_indicate_subscribers(True, attribute, value, force)
|
||||||
|
|
||||||
def on_disconnection(self, connection):
|
def on_disconnection(self, connection: Connection) -> None:
|
||||||
if connection.handle in self.subscribers:
|
if connection.handle in self.subscribers:
|
||||||
del self.subscribers[connection.handle]
|
del self.subscribers[connection.handle]
|
||||||
if connection.handle in self.indication_semaphores:
|
if connection.handle in self.indication_semaphores:
|
||||||
@@ -464,7 +509,7 @@ class Server(EventEmitter):
|
|||||||
if connection.handle in self.pending_confirmations:
|
if connection.handle in self.pending_confirmations:
|
||||||
del self.pending_confirmations[connection.handle]
|
del self.pending_confirmations[connection.handle]
|
||||||
|
|
||||||
def on_gatt_pdu(self, connection, att_pdu):
|
def on_gatt_pdu(self, connection: Connection, att_pdu: ATT_PDU) -> None:
|
||||||
logger.debug(f'GATT Request to server: [0x{connection.handle:04X}] {att_pdu}')
|
logger.debug(f'GATT Request to server: [0x{connection.handle:04X}] {att_pdu}')
|
||||||
handler_name = f'on_{att_pdu.name.lower()}'
|
handler_name = f'on_{att_pdu.name.lower()}'
|
||||||
handler = getattr(self, handler_name, None)
|
handler = getattr(self, handler_name, None)
|
||||||
@@ -506,7 +551,7 @@ class Server(EventEmitter):
|
|||||||
#######################################################
|
#######################################################
|
||||||
# ATT handlers
|
# ATT handlers
|
||||||
#######################################################
|
#######################################################
|
||||||
def on_att_request(self, connection, pdu):
|
def on_att_request(self, connection: Connection, pdu: ATT_PDU) -> None:
|
||||||
'''
|
'''
|
||||||
Handler for requests without a more specific handler
|
Handler for requests without a more specific handler
|
||||||
'''
|
'''
|
||||||
@@ -679,7 +724,6 @@ class Server(EventEmitter):
|
|||||||
and attribute.handle <= request.ending_handle
|
and attribute.handle <= request.ending_handle
|
||||||
and pdu_space_available
|
and pdu_space_available
|
||||||
):
|
):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
attribute_value = attribute.read_value(connection)
|
attribute_value = attribute.read_value(connection)
|
||||||
except ATT_Error as error:
|
except ATT_Error as error:
|
||||||
|
|||||||
@@ -4397,7 +4397,7 @@ class HCI_Event(HCI_Packet):
|
|||||||
if len(parameters) != length:
|
if len(parameters) != length:
|
||||||
raise ValueError('invalid packet length')
|
raise ValueError('invalid packet length')
|
||||||
|
|
||||||
cls: Type[HCI_Event | HCI_LE_Meta_Event] | None
|
cls: Any
|
||||||
if event_code == HCI_LE_META_EVENT:
|
if event_code == HCI_LE_META_EVENT:
|
||||||
# We do this dispatch here and not in the subclass in order to avoid call
|
# We do this dispatch here and not in the subclass in order to avoid call
|
||||||
# loops
|
# loops
|
||||||
|
|||||||
167
bumble/l2cap.py
@@ -17,6 +17,7 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import enum
|
||||||
import logging
|
import logging
|
||||||
import struct
|
import struct
|
||||||
|
|
||||||
@@ -676,6 +677,7 @@ class L2CAP_LE_Flow_Control_Credit(L2CAP_Control_Frame):
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class Channel(EventEmitter):
|
class Channel(EventEmitter):
|
||||||
|
class State(enum.IntEnum):
|
||||||
# States
|
# States
|
||||||
CLOSED = 0x00
|
CLOSED = 0x00
|
||||||
WAIT_CONNECT = 0x01
|
WAIT_CONNECT = 0x01
|
||||||
@@ -699,33 +701,11 @@ class Channel(EventEmitter):
|
|||||||
WAIT_FINAL_RSP = 0x16
|
WAIT_FINAL_RSP = 0x16
|
||||||
WAIT_CONTROL_IND = 0x17
|
WAIT_CONTROL_IND = 0x17
|
||||||
|
|
||||||
STATE_NAMES = {
|
|
||||||
CLOSED: 'CLOSED',
|
|
||||||
WAIT_CONNECT: 'WAIT_CONNECT',
|
|
||||||
WAIT_CONNECT_RSP: 'WAIT_CONNECT_RSP',
|
|
||||||
OPEN: 'OPEN',
|
|
||||||
WAIT_DISCONNECT: 'WAIT_DISCONNECT',
|
|
||||||
WAIT_CREATE: 'WAIT_CREATE',
|
|
||||||
WAIT_CREATE_RSP: 'WAIT_CREATE_RSP',
|
|
||||||
WAIT_MOVE: 'WAIT_MOVE',
|
|
||||||
WAIT_MOVE_RSP: 'WAIT_MOVE_RSP',
|
|
||||||
WAIT_MOVE_CONFIRM: 'WAIT_MOVE_CONFIRM',
|
|
||||||
WAIT_CONFIRM_RSP: 'WAIT_CONFIRM_RSP',
|
|
||||||
WAIT_CONFIG: 'WAIT_CONFIG',
|
|
||||||
WAIT_SEND_CONFIG: 'WAIT_SEND_CONFIG',
|
|
||||||
WAIT_CONFIG_REQ_RSP: 'WAIT_CONFIG_REQ_RSP',
|
|
||||||
WAIT_CONFIG_RSP: 'WAIT_CONFIG_RSP',
|
|
||||||
WAIT_CONFIG_REQ: 'WAIT_CONFIG_REQ',
|
|
||||||
WAIT_IND_FINAL_RSP: 'WAIT_IND_FINAL_RSP',
|
|
||||||
WAIT_FINAL_RSP: 'WAIT_FINAL_RSP',
|
|
||||||
WAIT_CONTROL_IND: 'WAIT_CONTROL_IND',
|
|
||||||
}
|
|
||||||
|
|
||||||
connection_result: Optional[asyncio.Future[None]]
|
connection_result: Optional[asyncio.Future[None]]
|
||||||
disconnection_result: Optional[asyncio.Future[None]]
|
disconnection_result: Optional[asyncio.Future[None]]
|
||||||
response: Optional[asyncio.Future[bytes]]
|
response: Optional[asyncio.Future[bytes]]
|
||||||
sink: Optional[Callable[[bytes], Any]]
|
sink: Optional[Callable[[bytes], Any]]
|
||||||
state: int
|
state: State
|
||||||
connection: Connection
|
connection: Connection
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@@ -741,7 +721,7 @@ class Channel(EventEmitter):
|
|||||||
self.manager = manager
|
self.manager = manager
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.signaling_cid = signaling_cid
|
self.signaling_cid = signaling_cid
|
||||||
self.state = Channel.CLOSED
|
self.state = self.State.CLOSED
|
||||||
self.mtu = mtu
|
self.mtu = mtu
|
||||||
self.psm = psm
|
self.psm = psm
|
||||||
self.source_cid = source_cid
|
self.source_cid = source_cid
|
||||||
@@ -751,13 +731,11 @@ class Channel(EventEmitter):
|
|||||||
self.disconnection_result = None
|
self.disconnection_result = None
|
||||||
self.sink = None
|
self.sink = None
|
||||||
|
|
||||||
def change_state(self, new_state: int) -> None:
|
def _change_state(self, new_state: State) -> None:
|
||||||
logger.debug(
|
logger.debug(f'{self} state change -> {color(new_state.name, "cyan")}')
|
||||||
f'{self} state change -> {color(Channel.STATE_NAMES[new_state], "cyan")}'
|
|
||||||
)
|
|
||||||
self.state = new_state
|
self.state = new_state
|
||||||
|
|
||||||
def send_pdu(self, pdu: SupportsBytes | bytes) -> None:
|
def send_pdu(self, pdu: Union[SupportsBytes, bytes]) -> None:
|
||||||
self.manager.send_pdu(self.connection, self.destination_cid, pdu)
|
self.manager.send_pdu(self.connection, self.destination_cid, pdu)
|
||||||
|
|
||||||
def send_control_frame(self, frame: L2CAP_Control_Frame) -> None:
|
def send_control_frame(self, frame: L2CAP_Control_Frame) -> None:
|
||||||
@@ -767,7 +745,7 @@ class Channel(EventEmitter):
|
|||||||
# Check that there isn't already a request pending
|
# Check that there isn't already a request pending
|
||||||
if self.response:
|
if self.response:
|
||||||
raise InvalidStateError('request already pending')
|
raise InvalidStateError('request already pending')
|
||||||
if self.state != Channel.OPEN:
|
if self.state != self.State.OPEN:
|
||||||
raise InvalidStateError('channel not open')
|
raise InvalidStateError('channel not open')
|
||||||
|
|
||||||
self.response = asyncio.get_running_loop().create_future()
|
self.response = asyncio.get_running_loop().create_future()
|
||||||
@@ -787,14 +765,14 @@ class Channel(EventEmitter):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def connect(self) -> None:
|
async def connect(self) -> None:
|
||||||
if self.state != Channel.CLOSED:
|
if self.state != self.State.CLOSED:
|
||||||
raise InvalidStateError('invalid state')
|
raise InvalidStateError('invalid state')
|
||||||
|
|
||||||
# Check that we can start a new connection
|
# Check that we can start a new connection
|
||||||
if self.connection_result:
|
if self.connection_result:
|
||||||
raise RuntimeError('connection already pending')
|
raise RuntimeError('connection already pending')
|
||||||
|
|
||||||
self.change_state(Channel.WAIT_CONNECT_RSP)
|
self._change_state(self.State.WAIT_CONNECT_RSP)
|
||||||
self.send_control_frame(
|
self.send_control_frame(
|
||||||
L2CAP_Connection_Request(
|
L2CAP_Connection_Request(
|
||||||
identifier=self.manager.next_identifier(self.connection),
|
identifier=self.manager.next_identifier(self.connection),
|
||||||
@@ -814,10 +792,10 @@ class Channel(EventEmitter):
|
|||||||
self.connection_result = None
|
self.connection_result = None
|
||||||
|
|
||||||
async def disconnect(self) -> None:
|
async def disconnect(self) -> None:
|
||||||
if self.state != Channel.OPEN:
|
if self.state != self.State.OPEN:
|
||||||
raise InvalidStateError('invalid state')
|
raise InvalidStateError('invalid state')
|
||||||
|
|
||||||
self.change_state(Channel.WAIT_DISCONNECT)
|
self._change_state(self.State.WAIT_DISCONNECT)
|
||||||
self.send_control_frame(
|
self.send_control_frame(
|
||||||
L2CAP_Disconnection_Request(
|
L2CAP_Disconnection_Request(
|
||||||
identifier=self.manager.next_identifier(self.connection),
|
identifier=self.manager.next_identifier(self.connection),
|
||||||
@@ -832,8 +810,8 @@ class Channel(EventEmitter):
|
|||||||
return await self.disconnection_result
|
return await self.disconnection_result
|
||||||
|
|
||||||
def abort(self) -> None:
|
def abort(self) -> None:
|
||||||
if self.state == self.OPEN:
|
if self.state == self.State.OPEN:
|
||||||
self.change_state(self.CLOSED)
|
self._change_state(self.State.CLOSED)
|
||||||
self.emit('close')
|
self.emit('close')
|
||||||
|
|
||||||
def send_configure_request(self) -> None:
|
def send_configure_request(self) -> None:
|
||||||
@@ -856,7 +834,7 @@ class Channel(EventEmitter):
|
|||||||
|
|
||||||
def on_connection_request(self, request) -> None:
|
def on_connection_request(self, request) -> None:
|
||||||
self.destination_cid = request.source_cid
|
self.destination_cid = request.source_cid
|
||||||
self.change_state(Channel.WAIT_CONNECT)
|
self._change_state(self.State.WAIT_CONNECT)
|
||||||
self.send_control_frame(
|
self.send_control_frame(
|
||||||
L2CAP_Connection_Response(
|
L2CAP_Connection_Response(
|
||||||
identifier=request.identifier,
|
identifier=request.identifier,
|
||||||
@@ -866,24 +844,24 @@ class Channel(EventEmitter):
|
|||||||
status=0x0000,
|
status=0x0000,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.change_state(Channel.WAIT_CONFIG)
|
self._change_state(self.State.WAIT_CONFIG)
|
||||||
self.send_configure_request()
|
self.send_configure_request()
|
||||||
self.change_state(Channel.WAIT_CONFIG_REQ_RSP)
|
self._change_state(self.State.WAIT_CONFIG_REQ_RSP)
|
||||||
|
|
||||||
def on_connection_response(self, response):
|
def on_connection_response(self, response):
|
||||||
if self.state != Channel.WAIT_CONNECT_RSP:
|
if self.state != self.State.WAIT_CONNECT_RSP:
|
||||||
logger.warning(color('invalid state', 'red'))
|
logger.warning(color('invalid state', 'red'))
|
||||||
return
|
return
|
||||||
|
|
||||||
if response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL:
|
if response.result == L2CAP_Connection_Response.CONNECTION_SUCCESSFUL:
|
||||||
self.destination_cid = response.destination_cid
|
self.destination_cid = response.destination_cid
|
||||||
self.change_state(Channel.WAIT_CONFIG)
|
self._change_state(self.State.WAIT_CONFIG)
|
||||||
self.send_configure_request()
|
self.send_configure_request()
|
||||||
self.change_state(Channel.WAIT_CONFIG_REQ_RSP)
|
self._change_state(self.State.WAIT_CONFIG_REQ_RSP)
|
||||||
elif response.result == L2CAP_Connection_Response.CONNECTION_PENDING:
|
elif response.result == L2CAP_Connection_Response.CONNECTION_PENDING:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
self.change_state(Channel.CLOSED)
|
self._change_state(self.State.CLOSED)
|
||||||
self.connection_result.set_exception(
|
self.connection_result.set_exception(
|
||||||
ProtocolError(
|
ProtocolError(
|
||||||
response.result,
|
response.result,
|
||||||
@@ -895,9 +873,9 @@ class Channel(EventEmitter):
|
|||||||
|
|
||||||
def on_configure_request(self, request) -> None:
|
def on_configure_request(self, request) -> None:
|
||||||
if self.state not in (
|
if self.state not in (
|
||||||
Channel.WAIT_CONFIG,
|
self.State.WAIT_CONFIG,
|
||||||
Channel.WAIT_CONFIG_REQ,
|
self.State.WAIT_CONFIG_REQ,
|
||||||
Channel.WAIT_CONFIG_REQ_RSP,
|
self.State.WAIT_CONFIG_REQ_RSP,
|
||||||
):
|
):
|
||||||
logger.warning(color('invalid state', 'red'))
|
logger.warning(color('invalid state', 'red'))
|
||||||
return
|
return
|
||||||
@@ -918,25 +896,28 @@ class Channel(EventEmitter):
|
|||||||
options=request.options, # TODO: don't accept everything blindly
|
options=request.options, # TODO: don't accept everything blindly
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if self.state == Channel.WAIT_CONFIG:
|
if self.state == self.State.WAIT_CONFIG:
|
||||||
self.change_state(Channel.WAIT_SEND_CONFIG)
|
self._change_state(self.State.WAIT_SEND_CONFIG)
|
||||||
self.send_configure_request()
|
self.send_configure_request()
|
||||||
self.change_state(Channel.WAIT_CONFIG_RSP)
|
self._change_state(self.State.WAIT_CONFIG_RSP)
|
||||||
elif self.state == Channel.WAIT_CONFIG_REQ:
|
elif self.state == self.State.WAIT_CONFIG_REQ:
|
||||||
self.change_state(Channel.OPEN)
|
self._change_state(self.State.OPEN)
|
||||||
if self.connection_result:
|
if self.connection_result:
|
||||||
self.connection_result.set_result(None)
|
self.connection_result.set_result(None)
|
||||||
self.connection_result = None
|
self.connection_result = None
|
||||||
self.emit('open')
|
self.emit('open')
|
||||||
elif self.state == Channel.WAIT_CONFIG_REQ_RSP:
|
elif self.state == self.State.WAIT_CONFIG_REQ_RSP:
|
||||||
self.change_state(Channel.WAIT_CONFIG_RSP)
|
self._change_state(self.State.WAIT_CONFIG_RSP)
|
||||||
|
|
||||||
def on_configure_response(self, response) -> None:
|
def on_configure_response(self, response) -> None:
|
||||||
if response.result == L2CAP_Configure_Response.SUCCESS:
|
if response.result == L2CAP_Configure_Response.SUCCESS:
|
||||||
if self.state == Channel.WAIT_CONFIG_REQ_RSP:
|
if self.state == self.State.WAIT_CONFIG_REQ_RSP:
|
||||||
self.change_state(Channel.WAIT_CONFIG_REQ)
|
self._change_state(self.State.WAIT_CONFIG_REQ)
|
||||||
elif self.state in (Channel.WAIT_CONFIG_RSP, Channel.WAIT_CONTROL_IND):
|
elif self.state in (
|
||||||
self.change_state(Channel.OPEN)
|
self.State.WAIT_CONFIG_RSP,
|
||||||
|
self.State.WAIT_CONTROL_IND,
|
||||||
|
):
|
||||||
|
self._change_state(self.State.OPEN)
|
||||||
if self.connection_result:
|
if self.connection_result:
|
||||||
self.connection_result.set_result(None)
|
self.connection_result.set_result(None)
|
||||||
self.connection_result = None
|
self.connection_result = None
|
||||||
@@ -966,7 +947,7 @@ class Channel(EventEmitter):
|
|||||||
# TODO: decide how to fail gracefully
|
# TODO: decide how to fail gracefully
|
||||||
|
|
||||||
def on_disconnection_request(self, request) -> None:
|
def on_disconnection_request(self, request) -> None:
|
||||||
if self.state in (Channel.OPEN, Channel.WAIT_DISCONNECT):
|
if self.state in (self.State.OPEN, self.State.WAIT_DISCONNECT):
|
||||||
self.send_control_frame(
|
self.send_control_frame(
|
||||||
L2CAP_Disconnection_Response(
|
L2CAP_Disconnection_Response(
|
||||||
identifier=request.identifier,
|
identifier=request.identifier,
|
||||||
@@ -974,14 +955,14 @@ class Channel(EventEmitter):
|
|||||||
source_cid=request.source_cid,
|
source_cid=request.source_cid,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.change_state(Channel.CLOSED)
|
self._change_state(self.State.CLOSED)
|
||||||
self.emit('close')
|
self.emit('close')
|
||||||
self.manager.on_channel_closed(self)
|
self.manager.on_channel_closed(self)
|
||||||
else:
|
else:
|
||||||
logger.warning(color('invalid state', 'red'))
|
logger.warning(color('invalid state', 'red'))
|
||||||
|
|
||||||
def on_disconnection_response(self, response) -> None:
|
def on_disconnection_response(self, response) -> None:
|
||||||
if self.state != Channel.WAIT_DISCONNECT:
|
if self.state != self.State.WAIT_DISCONNECT:
|
||||||
logger.warning(color('invalid state', 'red'))
|
logger.warning(color('invalid state', 'red'))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -992,7 +973,7 @@ class Channel(EventEmitter):
|
|||||||
logger.warning('unexpected source or destination CID')
|
logger.warning('unexpected source or destination CID')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.change_state(Channel.CLOSED)
|
self._change_state(self.State.CLOSED)
|
||||||
if self.disconnection_result:
|
if self.disconnection_result:
|
||||||
self.disconnection_result.set_result(None)
|
self.disconnection_result.set_result(None)
|
||||||
self.disconnection_result = None
|
self.disconnection_result = None
|
||||||
@@ -1004,7 +985,7 @@ class Channel(EventEmitter):
|
|||||||
f'Channel({self.source_cid}->{self.destination_cid}, '
|
f'Channel({self.source_cid}->{self.destination_cid}, '
|
||||||
f'PSM={self.psm}, '
|
f'PSM={self.psm}, '
|
||||||
f'MTU={self.mtu}, '
|
f'MTU={self.mtu}, '
|
||||||
f'state={Channel.STATE_NAMES[self.state]})'
|
f'state={self.state.name})'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1014,6 +995,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
LE Credit-based Connection Oriented Channel
|
LE Credit-based Connection Oriented Channel
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class State(enum.IntEnum):
|
||||||
INIT = 0
|
INIT = 0
|
||||||
CONNECTED = 1
|
CONNECTED = 1
|
||||||
CONNECTING = 2
|
CONNECTING = 2
|
||||||
@@ -1021,26 +1003,13 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
DISCONNECTED = 4
|
DISCONNECTED = 4
|
||||||
CONNECTION_ERROR = 5
|
CONNECTION_ERROR = 5
|
||||||
|
|
||||||
STATE_NAMES = {
|
|
||||||
INIT: 'INIT',
|
|
||||||
CONNECTED: 'CONNECTED',
|
|
||||||
CONNECTING: 'CONNECTING',
|
|
||||||
DISCONNECTING: 'DISCONNECTING',
|
|
||||||
DISCONNECTED: 'DISCONNECTED',
|
|
||||||
CONNECTION_ERROR: 'CONNECTION_ERROR',
|
|
||||||
}
|
|
||||||
|
|
||||||
out_queue: Deque[bytes]
|
out_queue: Deque[bytes]
|
||||||
connection_result: Optional[asyncio.Future[LeConnectionOrientedChannel]]
|
connection_result: Optional[asyncio.Future[LeConnectionOrientedChannel]]
|
||||||
disconnection_result: Optional[asyncio.Future[None]]
|
disconnection_result: Optional[asyncio.Future[None]]
|
||||||
out_sdu: Optional[bytes]
|
out_sdu: Optional[bytes]
|
||||||
state: int
|
state: State
|
||||||
connection: Connection
|
connection: Connection
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def state_name(state: int) -> str:
|
|
||||||
return name_or_number(LeConnectionOrientedChannel.STATE_NAMES, state)
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
manager: ChannelManager,
|
manager: ChannelManager,
|
||||||
@@ -1083,22 +1052,20 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
self.drained.set()
|
self.drained.set()
|
||||||
|
|
||||||
if connected:
|
if connected:
|
||||||
self.state = LeConnectionOrientedChannel.CONNECTED
|
self.state = self.State.CONNECTED
|
||||||
else:
|
else:
|
||||||
self.state = LeConnectionOrientedChannel.INIT
|
self.state = self.State.INIT
|
||||||
|
|
||||||
def change_state(self, new_state: int) -> None:
|
def _change_state(self, new_state: State) -> None:
|
||||||
logger.debug(
|
logger.debug(f'{self} state change -> {color(new_state.name, "cyan")}')
|
||||||
f'{self} state change -> {color(self.state_name(new_state), "cyan")}'
|
|
||||||
)
|
|
||||||
self.state = new_state
|
self.state = new_state
|
||||||
|
|
||||||
if new_state == self.CONNECTED:
|
if new_state == self.State.CONNECTED:
|
||||||
self.emit('open')
|
self.emit('open')
|
||||||
elif new_state == self.DISCONNECTED:
|
elif new_state == self.State.DISCONNECTED:
|
||||||
self.emit('close')
|
self.emit('close')
|
||||||
|
|
||||||
def send_pdu(self, pdu: SupportsBytes | bytes) -> None:
|
def send_pdu(self, pdu: Union[SupportsBytes, bytes]) -> None:
|
||||||
self.manager.send_pdu(self.connection, self.destination_cid, pdu)
|
self.manager.send_pdu(self.connection, self.destination_cid, pdu)
|
||||||
|
|
||||||
def send_control_frame(self, frame: L2CAP_Control_Frame) -> None:
|
def send_control_frame(self, frame: L2CAP_Control_Frame) -> None:
|
||||||
@@ -1106,7 +1073,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
|
|
||||||
async def connect(self) -> LeConnectionOrientedChannel:
|
async def connect(self) -> LeConnectionOrientedChannel:
|
||||||
# Check that we're in the right state
|
# Check that we're in the right state
|
||||||
if self.state != self.INIT:
|
if self.state != self.State.INIT:
|
||||||
raise InvalidStateError('not in a connectable state')
|
raise InvalidStateError('not in a connectable state')
|
||||||
|
|
||||||
# Check that we can start a new connection
|
# Check that we can start a new connection
|
||||||
@@ -1114,7 +1081,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
if identifier in self.manager.le_coc_requests:
|
if identifier in self.manager.le_coc_requests:
|
||||||
raise RuntimeError('too many concurrent connection requests')
|
raise RuntimeError('too many concurrent connection requests')
|
||||||
|
|
||||||
self.change_state(self.CONNECTING)
|
self._change_state(self.State.CONNECTING)
|
||||||
request = L2CAP_LE_Credit_Based_Connection_Request(
|
request = L2CAP_LE_Credit_Based_Connection_Request(
|
||||||
identifier=identifier,
|
identifier=identifier,
|
||||||
le_psm=self.le_psm,
|
le_psm=self.le_psm,
|
||||||
@@ -1134,10 +1101,10 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
|
|
||||||
async def disconnect(self) -> None:
|
async def disconnect(self) -> None:
|
||||||
# Check that we're connected
|
# Check that we're connected
|
||||||
if self.state != self.CONNECTED:
|
if self.state != self.State.CONNECTED:
|
||||||
raise InvalidStateError('not connected')
|
raise InvalidStateError('not connected')
|
||||||
|
|
||||||
self.change_state(self.DISCONNECTING)
|
self._change_state(self.State.DISCONNECTING)
|
||||||
self.flush_output()
|
self.flush_output()
|
||||||
self.send_control_frame(
|
self.send_control_frame(
|
||||||
L2CAP_Disconnection_Request(
|
L2CAP_Disconnection_Request(
|
||||||
@@ -1153,15 +1120,15 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
return await self.disconnection_result
|
return await self.disconnection_result
|
||||||
|
|
||||||
def abort(self) -> None:
|
def abort(self) -> None:
|
||||||
if self.state == self.CONNECTED:
|
if self.state == self.State.CONNECTED:
|
||||||
self.change_state(self.DISCONNECTED)
|
self._change_state(self.State.DISCONNECTED)
|
||||||
|
|
||||||
def on_pdu(self, pdu: bytes) -> None:
|
def on_pdu(self, pdu: bytes) -> None:
|
||||||
if self.sink is None:
|
if self.sink is None:
|
||||||
logger.warning('received pdu without a sink')
|
logger.warning('received pdu without a sink')
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.state != self.CONNECTED:
|
if self.state != self.State.CONNECTED:
|
||||||
logger.warning('received PDU while not connected, dropping')
|
logger.warning('received PDU while not connected, dropping')
|
||||||
|
|
||||||
# Manage the peer credits
|
# Manage the peer credits
|
||||||
@@ -1240,7 +1207,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
self.credits = response.initial_credits
|
self.credits = response.initial_credits
|
||||||
self.connected = True
|
self.connected = True
|
||||||
self.connection_result.set_result(self)
|
self.connection_result.set_result(self)
|
||||||
self.change_state(self.CONNECTED)
|
self._change_state(self.State.CONNECTED)
|
||||||
else:
|
else:
|
||||||
self.connection_result.set_exception(
|
self.connection_result.set_exception(
|
||||||
ProtocolError(
|
ProtocolError(
|
||||||
@@ -1251,7 +1218,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.change_state(self.CONNECTION_ERROR)
|
self._change_state(self.State.CONNECTION_ERROR)
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
self.connection_result = None
|
self.connection_result = None
|
||||||
@@ -1271,11 +1238,11 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
source_cid=request.source_cid,
|
source_cid=request.source_cid,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.change_state(self.DISCONNECTED)
|
self._change_state(self.State.DISCONNECTED)
|
||||||
self.flush_output()
|
self.flush_output()
|
||||||
|
|
||||||
def on_disconnection_response(self, response) -> None:
|
def on_disconnection_response(self, response) -> None:
|
||||||
if self.state != self.DISCONNECTING:
|
if self.state != self.State.DISCONNECTING:
|
||||||
logger.warning(color('invalid state', 'red'))
|
logger.warning(color('invalid state', 'red'))
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1286,7 +1253,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
logger.warning('unexpected source or destination CID')
|
logger.warning('unexpected source or destination CID')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.change_state(self.DISCONNECTED)
|
self._change_state(self.State.DISCONNECTED)
|
||||||
if self.disconnection_result:
|
if self.disconnection_result:
|
||||||
self.disconnection_result.set_result(None)
|
self.disconnection_result.set_result(None)
|
||||||
self.disconnection_result = None
|
self.disconnection_result = None
|
||||||
@@ -1339,7 +1306,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def write(self, data: bytes) -> None:
|
def write(self, data: bytes) -> None:
|
||||||
if self.state != self.CONNECTED:
|
if self.state != self.State.CONNECTED:
|
||||||
logger.warning('not connected, dropping data')
|
logger.warning('not connected, dropping data')
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1367,7 +1334,7 @@ class LeConnectionOrientedChannel(EventEmitter):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return (
|
return (
|
||||||
f'CoC({self.source_cid}->{self.destination_cid}, '
|
f'CoC({self.source_cid}->{self.destination_cid}, '
|
||||||
f'State={self.state_name(self.state)}, '
|
f'State={self.state.name}, '
|
||||||
f'PSM={self.le_psm}, '
|
f'PSM={self.le_psm}, '
|
||||||
f'MTU={self.mtu}/{self.peer_mtu}, '
|
f'MTU={self.mtu}/{self.peer_mtu}, '
|
||||||
f'MPS={self.mps}/{self.peer_mps}, '
|
f'MPS={self.mps}/{self.peer_mps}, '
|
||||||
@@ -1571,7 +1538,7 @@ class ChannelManager:
|
|||||||
if connection_handle in self.identifiers:
|
if connection_handle in self.identifiers:
|
||||||
del self.identifiers[connection_handle]
|
del self.identifiers[connection_handle]
|
||||||
|
|
||||||
def send_pdu(self, connection, cid: int, pdu: SupportsBytes | bytes) -> None:
|
def send_pdu(self, connection, cid: int, pdu: Union[SupportsBytes, bytes]) -> None:
|
||||||
pdu_str = pdu.hex() if isinstance(pdu, bytes) else str(pdu)
|
pdu_str = pdu.hex() if isinstance(pdu, bytes) else str(pdu)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f'{color(">>> Sending L2CAP PDU", "blue")} '
|
f'{color(">>> Sending L2CAP PDU", "blue")} '
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import contextlib
|
||||||
import grpc
|
import grpc
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -27,8 +28,8 @@ from bumble.core import (
|
|||||||
)
|
)
|
||||||
from bumble.device import Connection as BumbleConnection, Device
|
from bumble.device import Connection as BumbleConnection, Device
|
||||||
from bumble.hci import HCI_Error
|
from bumble.hci import HCI_Error
|
||||||
|
from bumble.utils import EventWatcher
|
||||||
from bumble.pairing import PairingConfig, PairingDelegate as BasePairingDelegate
|
from bumble.pairing import PairingConfig, PairingDelegate as BasePairingDelegate
|
||||||
from contextlib import suppress
|
|
||||||
from google.protobuf import any_pb2 # pytype: disable=pyi-error
|
from google.protobuf import any_pb2 # pytype: disable=pyi-error
|
||||||
from google.protobuf import empty_pb2 # pytype: disable=pyi-error
|
from google.protobuf import empty_pb2 # pytype: disable=pyi-error
|
||||||
from google.protobuf import wrappers_pb2 # pytype: disable=pyi-error
|
from google.protobuf import wrappers_pb2 # pytype: disable=pyi-error
|
||||||
@@ -232,7 +233,11 @@ class SecurityService(SecurityServicer):
|
|||||||
sc=config.pairing_sc_enable,
|
sc=config.pairing_sc_enable,
|
||||||
mitm=config.pairing_mitm_enable,
|
mitm=config.pairing_mitm_enable,
|
||||||
bonding=config.pairing_bonding_enable,
|
bonding=config.pairing_bonding_enable,
|
||||||
identity_address_type=config.identity_address_type,
|
identity_address_type=(
|
||||||
|
PairingConfig.AddressType.PUBLIC
|
||||||
|
if connection.self_address.is_public
|
||||||
|
else config.identity_address_type
|
||||||
|
),
|
||||||
delegate=PairingDelegate(
|
delegate=PairingDelegate(
|
||||||
connection,
|
connection,
|
||||||
self,
|
self,
|
||||||
@@ -294,23 +299,35 @@ class SecurityService(SecurityServicer):
|
|||||||
try:
|
try:
|
||||||
self.log.debug('Pair...')
|
self.log.debug('Pair...')
|
||||||
|
|
||||||
|
security_result = asyncio.get_running_loop().create_future()
|
||||||
|
|
||||||
|
with contextlib.closing(EventWatcher()) as watcher:
|
||||||
|
|
||||||
|
@watcher.on(connection, 'pairing')
|
||||||
|
def on_pairing(*_: Any) -> None:
|
||||||
|
security_result.set_result('success')
|
||||||
|
|
||||||
|
@watcher.on(connection, 'pairing_failure')
|
||||||
|
def on_pairing_failure(*_: Any) -> None:
|
||||||
|
security_result.set_result('pairing_failure')
|
||||||
|
|
||||||
|
@watcher.on(connection, 'disconnection')
|
||||||
|
def on_disconnection(*_: Any) -> None:
|
||||||
|
security_result.set_result('connection_died')
|
||||||
|
|
||||||
if (
|
if (
|
||||||
connection.transport == BT_LE_TRANSPORT
|
connection.transport == BT_LE_TRANSPORT
|
||||||
and connection.role == BT_PERIPHERAL_ROLE
|
and connection.role == BT_PERIPHERAL_ROLE
|
||||||
):
|
):
|
||||||
wait_for_security: asyncio.Future[
|
|
||||||
bool
|
|
||||||
] = asyncio.get_running_loop().create_future()
|
|
||||||
connection.on("pairing", lambda *_: wait_for_security.set_result(True)) # type: ignore
|
|
||||||
connection.on("pairing_failure", wait_for_security.set_exception)
|
|
||||||
|
|
||||||
connection.request_pairing()
|
connection.request_pairing()
|
||||||
|
|
||||||
await wait_for_security
|
|
||||||
else:
|
else:
|
||||||
await connection.pair()
|
await connection.pair()
|
||||||
|
|
||||||
self.log.debug('Paired')
|
result = await security_result
|
||||||
|
|
||||||
|
self.log.debug(f'Pairing session complete, status={result}')
|
||||||
|
if result != 'success':
|
||||||
|
return SecureResponse(**{result: empty_pb2.Empty()})
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
self.log.warning("Connection died during encryption")
|
self.log.warning("Connection died during encryption")
|
||||||
return SecureResponse(connection_died=empty_pb2.Empty())
|
return SecureResponse(connection_died=empty_pb2.Empty())
|
||||||
@@ -369,6 +386,7 @@ class SecurityService(SecurityServicer):
|
|||||||
str
|
str
|
||||||
] = asyncio.get_running_loop().create_future()
|
] = asyncio.get_running_loop().create_future()
|
||||||
authenticate_task: Optional[asyncio.Future[None]] = None
|
authenticate_task: Optional[asyncio.Future[None]] = None
|
||||||
|
pair_task: Optional[asyncio.Future[None]] = None
|
||||||
|
|
||||||
async def authenticate() -> None:
|
async def authenticate() -> None:
|
||||||
assert connection
|
assert connection
|
||||||
@@ -415,6 +433,10 @@ class SecurityService(SecurityServicer):
|
|||||||
if authenticate_task is None:
|
if authenticate_task is None:
|
||||||
authenticate_task = asyncio.create_task(authenticate())
|
authenticate_task = asyncio.create_task(authenticate())
|
||||||
|
|
||||||
|
def pair(*_: Any) -> None:
|
||||||
|
if self.need_pairing(connection, level):
|
||||||
|
pair_task = asyncio.create_task(connection.pair())
|
||||||
|
|
||||||
listeners: Dict[str, Callable[..., None]] = {
|
listeners: Dict[str, Callable[..., None]] = {
|
||||||
'disconnection': set_failure('connection_died'),
|
'disconnection': set_failure('connection_died'),
|
||||||
'pairing_failure': set_failure('pairing_failure'),
|
'pairing_failure': set_failure('pairing_failure'),
|
||||||
@@ -425,6 +447,7 @@ class SecurityService(SecurityServicer):
|
|||||||
'connection_encryption_change': on_encryption_change,
|
'connection_encryption_change': on_encryption_change,
|
||||||
'classic_pairing': try_set_success,
|
'classic_pairing': try_set_success,
|
||||||
'classic_pairing_failure': set_failure('pairing_failure'),
|
'classic_pairing_failure': set_failure('pairing_failure'),
|
||||||
|
'security_request': pair,
|
||||||
}
|
}
|
||||||
|
|
||||||
# register event handlers
|
# register event handlers
|
||||||
@@ -452,6 +475,15 @@ class SecurityService(SecurityServicer):
|
|||||||
pass
|
pass
|
||||||
self.log.debug('Authenticated')
|
self.log.debug('Authenticated')
|
||||||
|
|
||||||
|
# wait for `pair` to finish if any
|
||||||
|
if pair_task is not None:
|
||||||
|
self.log.debug('Wait for authentication...')
|
||||||
|
try:
|
||||||
|
await pair_task # type: ignore
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
self.log.debug('paired')
|
||||||
|
|
||||||
return WaitSecurityResponse(**kwargs)
|
return WaitSecurityResponse(**kwargs)
|
||||||
|
|
||||||
def reached_security_level(
|
def reached_security_level(
|
||||||
@@ -523,7 +555,7 @@ class SecurityStorageService(SecurityStorageServicer):
|
|||||||
self.log.debug(f"DeleteBond: {address}")
|
self.log.debug(f"DeleteBond: {address}")
|
||||||
|
|
||||||
if self.device.keystore is not None:
|
if self.device.keystore is not None:
|
||||||
with suppress(KeyError):
|
with contextlib.suppress(KeyError):
|
||||||
await self.device.keystore.delete(str(address))
|
await self.device.keystore.delete(str(address))
|
||||||
|
|
||||||
return empty_pb2.Empty()
|
return empty_pb2.Empty()
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from typing import (
|
|||||||
Optional,
|
Optional,
|
||||||
Tuple,
|
Tuple,
|
||||||
Type,
|
Type,
|
||||||
|
cast,
|
||||||
)
|
)
|
||||||
|
|
||||||
from pyee import EventEmitter
|
from pyee import EventEmitter
|
||||||
@@ -1771,7 +1772,26 @@ class Manager(EventEmitter):
|
|||||||
cid = SMP_BR_CID if connection.transport == BT_BR_EDR_TRANSPORT else SMP_CID
|
cid = SMP_BR_CID if connection.transport == BT_BR_EDR_TRANSPORT else SMP_CID
|
||||||
connection.send_l2cap_pdu(cid, command.to_bytes())
|
connection.send_l2cap_pdu(cid, command.to_bytes())
|
||||||
|
|
||||||
|
def on_smp_security_request_command(
|
||||||
|
self, connection: Connection, request: SMP_Security_Request_Command
|
||||||
|
) -> None:
|
||||||
|
connection.emit('security_request', request.auth_req)
|
||||||
|
|
||||||
def on_smp_pdu(self, connection: Connection, pdu: bytes) -> None:
|
def on_smp_pdu(self, connection: Connection, pdu: bytes) -> None:
|
||||||
|
# Parse the L2CAP payload into an SMP Command object
|
||||||
|
command = SMP_Command.from_bytes(pdu)
|
||||||
|
logger.debug(
|
||||||
|
f'<<< Received SMP Command on connection [0x{connection.handle:04X}] '
|
||||||
|
f'{connection.peer_address}: {command}'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Security request is more than just pairing, so let applications handle them
|
||||||
|
if command.code == SMP_SECURITY_REQUEST_COMMAND:
|
||||||
|
self.on_smp_security_request_command(
|
||||||
|
connection, cast(SMP_Security_Request_Command, command)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
# Look for a session with this connection, and create one if none exists
|
# Look for a session with this connection, and create one if none exists
|
||||||
if not (session := self.sessions.get(connection.handle)):
|
if not (session := self.sessions.get(connection.handle)):
|
||||||
if connection.role == BT_CENTRAL_ROLE:
|
if connection.role == BT_CENTRAL_ROLE:
|
||||||
@@ -1782,13 +1802,6 @@ class Manager(EventEmitter):
|
|||||||
)
|
)
|
||||||
self.sessions[connection.handle] = session
|
self.sessions[connection.handle] = session
|
||||||
|
|
||||||
# Parse the L2CAP payload into an SMP Command object
|
|
||||||
command = SMP_Command.from_bytes(pdu)
|
|
||||||
logger.debug(
|
|
||||||
f'<<< Received SMP Command on connection [0x{connection.handle:04X}] '
|
|
||||||
f'{connection.peer_address}: {command}'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Delegate the handling of the command to the session
|
# Delegate the handling of the command to the session
|
||||||
session.on_smp_command(command)
|
session.on_smp_command(command)
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
import logging
|
import logging
|
||||||
import grpc.aio
|
import grpc.aio
|
||||||
|
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
from .common import PumpedTransport, PumpedPacketSource, PumpedPacketSink, Transport
|
from .common import PumpedTransport, PumpedPacketSource, PumpedPacketSink, Transport
|
||||||
|
|
||||||
# pylint: disable=no-name-in-module
|
# pylint: disable=no-name-in-module
|
||||||
@@ -33,7 +35,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def open_android_emulator_transport(spec: str | None) -> Transport:
|
async def open_android_emulator_transport(spec: Optional[str]) -> Transport:
|
||||||
'''
|
'''
|
||||||
Open a transport connection to an Android emulator via its gRPC interface.
|
Open a transport connection to an Android emulator via its gRPC interface.
|
||||||
The parameter string has this syntax:
|
The parameter string has this syntax:
|
||||||
@@ -82,7 +84,7 @@ async def open_android_emulator_transport(spec: str | None) -> Transport:
|
|||||||
logger.debug(f'connecting to gRPC server at {server_address}')
|
logger.debug(f'connecting to gRPC server at {server_address}')
|
||||||
channel = grpc.aio.insecure_channel(server_address)
|
channel = grpc.aio.insecure_channel(server_address)
|
||||||
|
|
||||||
service: EmulatedBluetoothServiceStub | VhciForwardingServiceStub
|
service: Union[EmulatedBluetoothServiceStub, VhciForwardingServiceStub]
|
||||||
if mode == 'host':
|
if mode == 'host':
|
||||||
# Connect as a host
|
# Connect as a host
|
||||||
service = EmulatedBluetoothServiceStub(channel)
|
service = EmulatedBluetoothServiceStub(channel)
|
||||||
@@ -95,10 +97,13 @@ async def open_android_emulator_transport(spec: str | None) -> Transport:
|
|||||||
raise ValueError('invalid mode')
|
raise ValueError('invalid mode')
|
||||||
|
|
||||||
# Create the transport object
|
# Create the transport object
|
||||||
transport = PumpedTransport(
|
class EmulatorTransport(PumpedTransport):
|
||||||
PumpedPacketSource(hci_device.read),
|
async def close(self):
|
||||||
PumpedPacketSink(hci_device.write),
|
await super().close()
|
||||||
channel.close,
|
await channel.close()
|
||||||
|
|
||||||
|
transport = EmulatorTransport(
|
||||||
|
PumpedPacketSource(hci_device.read), PumpedPacketSink(hci_device.write)
|
||||||
)
|
)
|
||||||
transport.start()
|
transport.start()
|
||||||
|
|
||||||
|
|||||||
@@ -18,11 +18,12 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import atexit
|
import atexit
|
||||||
import logging
|
import logging
|
||||||
import grpc.aio
|
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import sys
|
import sys
|
||||||
from typing import Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
|
import grpc.aio
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
ParserSource,
|
ParserSource,
|
||||||
@@ -33,8 +34,8 @@ from .common import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
# pylint: disable=no-name-in-module
|
# pylint: disable=no-name-in-module
|
||||||
from .grpc_protobuf.packet_streamer_pb2_grpc import PacketStreamerStub
|
|
||||||
from .grpc_protobuf.packet_streamer_pb2_grpc import (
|
from .grpc_protobuf.packet_streamer_pb2_grpc import (
|
||||||
|
PacketStreamerStub,
|
||||||
PacketStreamerServicer,
|
PacketStreamerServicer,
|
||||||
add_PacketStreamerServicer_to_server,
|
add_PacketStreamerServicer_to_server,
|
||||||
)
|
)
|
||||||
@@ -43,6 +44,7 @@ from .grpc_protobuf.hci_packet_pb2 import HCIPacket
|
|||||||
from .grpc_protobuf.startup_pb2 import Chip, ChipInfo
|
from .grpc_protobuf.startup_pb2 import Chip, ChipInfo
|
||||||
from .grpc_protobuf.common_pb2 import ChipKind
|
from .grpc_protobuf.common_pb2 import ChipKind
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Logging
|
# Logging
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -74,14 +76,20 @@ def get_ini_dir() -> Optional[pathlib.Path]:
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
def find_grpc_port() -> int:
|
def ini_file_name(instance_number: int) -> str:
|
||||||
|
suffix = f'_{instance_number}' if instance_number > 0 else ''
|
||||||
|
return f'netsim{suffix}.ini'
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def find_grpc_port(instance_number: int) -> int:
|
||||||
if not (ini_dir := get_ini_dir()):
|
if not (ini_dir := get_ini_dir()):
|
||||||
logger.debug('no known directory for .ini file')
|
logger.debug('no known directory for .ini file')
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
ini_file = ini_dir / 'netsim.ini'
|
ini_file = ini_dir / ini_file_name(instance_number)
|
||||||
|
logger.debug(f'Looking for .ini file at {ini_file}')
|
||||||
if ini_file.is_file():
|
if ini_file.is_file():
|
||||||
logger.debug(f'Found .ini file at {ini_file}')
|
|
||||||
with open(ini_file, 'r') as ini_file_data:
|
with open(ini_file, 'r') as ini_file_data:
|
||||||
for line in ini_file_data.readlines():
|
for line in ini_file_data.readlines():
|
||||||
if '=' in line:
|
if '=' in line:
|
||||||
@@ -90,12 +98,14 @@ def find_grpc_port() -> int:
|
|||||||
logger.debug(f'gRPC port = {value}')
|
logger.debug(f'gRPC port = {value}')
|
||||||
return int(value)
|
return int(value)
|
||||||
|
|
||||||
|
logger.debug('no grpc.port property found in .ini file')
|
||||||
|
|
||||||
# Not found
|
# Not found
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
def publish_grpc_port(grpc_port) -> bool:
|
def publish_grpc_port(grpc_port: int, instance_number: int) -> bool:
|
||||||
if not (ini_dir := get_ini_dir()):
|
if not (ini_dir := get_ini_dir()):
|
||||||
logger.debug('no known directory for .ini file')
|
logger.debug('no known directory for .ini file')
|
||||||
return False
|
return False
|
||||||
@@ -104,7 +114,7 @@ def publish_grpc_port(grpc_port) -> bool:
|
|||||||
logger.debug('ini directory does not exist')
|
logger.debug('ini directory does not exist')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
ini_file = ini_dir / 'netsim.ini'
|
ini_file = ini_dir / ini_file_name(instance_number)
|
||||||
try:
|
try:
|
||||||
ini_file.write_text(f'grpc.port={grpc_port}\n')
|
ini_file.write_text(f'grpc.port={grpc_port}\n')
|
||||||
logger.debug(f"published gRPC port at {ini_file}")
|
logger.debug(f"published gRPC port at {ini_file}")
|
||||||
@@ -122,14 +132,15 @@ def publish_grpc_port(grpc_port) -> bool:
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def open_android_netsim_controller_transport(
|
async def open_android_netsim_controller_transport(
|
||||||
server_host: str | None, server_port: int
|
server_host: Optional[str], server_port: int, options: Dict[str, str]
|
||||||
) -> Transport:
|
) -> Transport:
|
||||||
if not server_port:
|
if not server_port:
|
||||||
raise ValueError('invalid port')
|
raise ValueError('invalid port')
|
||||||
if server_host == '_' or not server_host:
|
if server_host == '_' or not server_host:
|
||||||
server_host = 'localhost'
|
server_host = 'localhost'
|
||||||
|
|
||||||
if not publish_grpc_port(server_port):
|
instance_number = int(options.get('instance', "0"))
|
||||||
|
if not publish_grpc_port(server_port, instance_number):
|
||||||
logger.warning("unable to publish gRPC port")
|
logger.warning("unable to publish gRPC port")
|
||||||
|
|
||||||
class HciDevice:
|
class HciDevice:
|
||||||
@@ -186,16 +197,13 @@ async def open_android_netsim_controller_transport(
|
|||||||
logger.debug(f'<<< PACKET: {data.hex()}')
|
logger.debug(f'<<< PACKET: {data.hex()}')
|
||||||
self.on_data_received(data)
|
self.on_data_received(data)
|
||||||
|
|
||||||
def send_packet(self, data):
|
async def send_packet(self, data):
|
||||||
async def send():
|
return await self.context.write(
|
||||||
await self.context.write(
|
|
||||||
PacketResponse(
|
PacketResponse(
|
||||||
hci_packet=HCIPacket(packet_type=data[0], packet=data[1:])
|
hci_packet=HCIPacket(packet_type=data[0], packet=data[1:])
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.loop.create_task(send())
|
|
||||||
|
|
||||||
def terminate(self):
|
def terminate(self):
|
||||||
self.task.cancel()
|
self.task.cancel()
|
||||||
|
|
||||||
@@ -228,17 +236,17 @@ async def open_android_netsim_controller_transport(
|
|||||||
logger.debug('gRPC server cancelled')
|
logger.debug('gRPC server cancelled')
|
||||||
await self.grpc_server.stop(None)
|
await self.grpc_server.stop(None)
|
||||||
|
|
||||||
def on_packet(self, packet):
|
async def send_packet(self, packet):
|
||||||
if not self.device:
|
if not self.device:
|
||||||
logger.debug('no device, dropping packet')
|
logger.debug('no device, dropping packet')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.device.send_packet(packet)
|
return await self.device.send_packet(packet)
|
||||||
|
|
||||||
async def StreamPackets(self, _request_iterator, context):
|
async def StreamPackets(self, _request_iterator, context):
|
||||||
logger.debug('StreamPackets request')
|
logger.debug('StreamPackets request')
|
||||||
|
|
||||||
# Check that we won't already have a device
|
# Check that we don't already have a device
|
||||||
if self.device:
|
if self.device:
|
||||||
logger.debug('busy, already serving a device')
|
logger.debug('busy, already serving a device')
|
||||||
return PacketResponse(error='Busy')
|
return PacketResponse(error='Busy')
|
||||||
@@ -261,15 +269,42 @@ async def open_android_netsim_controller_transport(
|
|||||||
await server.start()
|
await server.start()
|
||||||
asyncio.get_running_loop().create_task(server.serve())
|
asyncio.get_running_loop().create_task(server.serve())
|
||||||
|
|
||||||
class GrpcServerTransport(Transport):
|
sink = PumpedPacketSink(server.send_packet)
|
||||||
async def close(self):
|
sink.start()
|
||||||
await super().close()
|
return Transport(server, sink)
|
||||||
|
|
||||||
return GrpcServerTransport(server, server)
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def open_android_netsim_host_transport(server_host, server_port, options):
|
async def open_android_netsim_host_transport_with_address(
|
||||||
|
server_host: Optional[str],
|
||||||
|
server_port: int,
|
||||||
|
options: Optional[Dict[str, str]] = None,
|
||||||
|
):
|
||||||
|
if server_host == '_' or not server_host:
|
||||||
|
server_host = 'localhost'
|
||||||
|
|
||||||
|
if not server_port:
|
||||||
|
# Look for the gRPC config in a .ini file
|
||||||
|
instance_number = 0 if options is None else int(options.get('instance', '0'))
|
||||||
|
server_port = find_grpc_port(instance_number)
|
||||||
|
if not server_port:
|
||||||
|
raise RuntimeError('gRPC server port not found')
|
||||||
|
|
||||||
|
# Connect to the gRPC server
|
||||||
|
server_address = f'{server_host}:{server_port}'
|
||||||
|
logger.debug(f'Connecting to gRPC server at {server_address}')
|
||||||
|
channel = grpc.aio.insecure_channel(server_address)
|
||||||
|
|
||||||
|
return await open_android_netsim_host_transport_with_channel(
|
||||||
|
channel,
|
||||||
|
options,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
async def open_android_netsim_host_transport_with_channel(
|
||||||
|
channel, options: Optional[Dict[str, str]] = None
|
||||||
|
):
|
||||||
# Wrapper for I/O operations
|
# Wrapper for I/O operations
|
||||||
class HciDevice:
|
class HciDevice:
|
||||||
def __init__(self, name, manufacturer, hci_device):
|
def __init__(self, name, manufacturer, hci_device):
|
||||||
@@ -288,10 +323,12 @@ async def open_android_netsim_host_transport(server_host, server_port, options):
|
|||||||
async def read(self):
|
async def read(self):
|
||||||
response = await self.hci_device.read()
|
response = await self.hci_device.read()
|
||||||
response_type = response.WhichOneof('response_type')
|
response_type = response.WhichOneof('response_type')
|
||||||
|
|
||||||
if response_type == 'error':
|
if response_type == 'error':
|
||||||
logger.warning(f'received error: {response.error}')
|
logger.warning(f'received error: {response.error}')
|
||||||
raise RuntimeError(response.error)
|
raise RuntimeError(response.error)
|
||||||
elif response_type == 'hci_packet':
|
|
||||||
|
if response_type == 'hci_packet':
|
||||||
return (
|
return (
|
||||||
bytes([response.hci_packet.packet_type])
|
bytes([response.hci_packet.packet_type])
|
||||||
+ response.hci_packet.packet
|
+ response.hci_packet.packet
|
||||||
@@ -306,24 +343,9 @@ async def open_android_netsim_host_transport(server_host, server_port, options):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
name = options.get('name', DEFAULT_NAME)
|
name = DEFAULT_NAME if options is None else options.get('name', DEFAULT_NAME)
|
||||||
manufacturer = DEFAULT_MANUFACTURER
|
manufacturer = DEFAULT_MANUFACTURER
|
||||||
|
|
||||||
if server_host == '_' or not server_host:
|
|
||||||
server_host = 'localhost'
|
|
||||||
|
|
||||||
if not server_port:
|
|
||||||
# Look for the gRPC config in a .ini file
|
|
||||||
server_host = 'localhost'
|
|
||||||
server_port = find_grpc_port()
|
|
||||||
if not server_port:
|
|
||||||
raise RuntimeError('gRPC server port not found')
|
|
||||||
|
|
||||||
# Connect to the gRPC server
|
|
||||||
server_address = f'{server_host}:{server_port}'
|
|
||||||
logger.debug(f'Connecting to gRPC server at {server_address}')
|
|
||||||
channel = grpc.aio.insecure_channel(server_address)
|
|
||||||
|
|
||||||
# Connect as a host
|
# Connect as a host
|
||||||
service = PacketStreamerStub(channel)
|
service = PacketStreamerStub(channel)
|
||||||
hci_device = HciDevice(
|
hci_device = HciDevice(
|
||||||
@@ -334,10 +356,14 @@ async def open_android_netsim_host_transport(server_host, server_port, options):
|
|||||||
await hci_device.start()
|
await hci_device.start()
|
||||||
|
|
||||||
# Create the transport object
|
# Create the transport object
|
||||||
transport = PumpedTransport(
|
class GrpcTransport(PumpedTransport):
|
||||||
|
async def close(self):
|
||||||
|
await super().close()
|
||||||
|
await channel.close()
|
||||||
|
|
||||||
|
transport = GrpcTransport(
|
||||||
PumpedPacketSource(hci_device.read),
|
PumpedPacketSource(hci_device.read),
|
||||||
PumpedPacketSink(hci_device.write),
|
PumpedPacketSink(hci_device.write),
|
||||||
channel.close,
|
|
||||||
)
|
)
|
||||||
transport.start()
|
transport.start()
|
||||||
|
|
||||||
@@ -345,7 +371,7 @@ async def open_android_netsim_host_transport(server_host, server_port, options):
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def open_android_netsim_transport(spec):
|
async def open_android_netsim_transport(spec: Optional[str]) -> Transport:
|
||||||
'''
|
'''
|
||||||
Open a transport connection as a client or server, implementing Android's `netsim`
|
Open a transport connection as a client or server, implementing Android's `netsim`
|
||||||
simulator protocol over gRPC.
|
simulator protocol over gRPC.
|
||||||
@@ -359,6 +385,11 @@ async def open_android_netsim_transport(spec):
|
|||||||
to connect *to* a netsim server (netsim is the controller), or accept
|
to connect *to* a netsim server (netsim is the controller), or accept
|
||||||
connections *as* a netsim-compatible server.
|
connections *as* a netsim-compatible server.
|
||||||
|
|
||||||
|
instance=<n>
|
||||||
|
Specifies an instance number, with <n> > 0. This is used to determine which
|
||||||
|
.init file to use. In `host` mode, it is ignored when the <host>:<port>
|
||||||
|
specifier is present, since in that case no .ini file is used.
|
||||||
|
|
||||||
In `host` mode:
|
In `host` mode:
|
||||||
The <host>:<port> part is optional. When not specified, the transport
|
The <host>:<port> part is optional. When not specified, the transport
|
||||||
looks for a netsim .ini file, from which it will read the `grpc.backend.port`
|
looks for a netsim .ini file, from which it will read the `grpc.backend.port`
|
||||||
@@ -387,14 +418,15 @@ async def open_android_netsim_transport(spec):
|
|||||||
params = spec.split(',') if spec else []
|
params = spec.split(',') if spec else []
|
||||||
if params and ':' in params[0]:
|
if params and ':' in params[0]:
|
||||||
# Explicit <host>:<port>
|
# Explicit <host>:<port>
|
||||||
host, port = params[0].split(':')
|
host, port_str = params[0].split(':')
|
||||||
|
port = int(port_str)
|
||||||
params_offset = 1
|
params_offset = 1
|
||||||
else:
|
else:
|
||||||
host = None
|
host = None
|
||||||
port = 0
|
port = 0
|
||||||
params_offset = 0
|
params_offset = 0
|
||||||
|
|
||||||
options = {}
|
options: Dict[str, str] = {}
|
||||||
for param in params[params_offset:]:
|
for param in params[params_offset:]:
|
||||||
if '=' not in param:
|
if '=' not in param:
|
||||||
raise ValueError('invalid parameter, expected <name>=<value>')
|
raise ValueError('invalid parameter, expected <name>=<value>')
|
||||||
@@ -403,10 +435,12 @@ async def open_android_netsim_transport(spec):
|
|||||||
|
|
||||||
mode = options.get('mode', 'host')
|
mode = options.get('mode', 'host')
|
||||||
if mode == 'host':
|
if mode == 'host':
|
||||||
return await open_android_netsim_host_transport(host, port, options)
|
return await open_android_netsim_host_transport_with_address(
|
||||||
|
host, port, options
|
||||||
|
)
|
||||||
if mode == 'controller':
|
if mode == 'controller':
|
||||||
if host is None:
|
if host is None:
|
||||||
raise ValueError('<host>:<port> missing')
|
raise ValueError('<host>:<port> missing')
|
||||||
return await open_android_netsim_controller_transport(host, port)
|
return await open_android_netsim_controller_transport(host, port, options)
|
||||||
|
|
||||||
raise ValueError('invalid mode option')
|
raise ValueError('invalid mode option')
|
||||||
|
|||||||
@@ -339,8 +339,9 @@ class PumpedPacketSource(ParserSource):
|
|||||||
try:
|
try:
|
||||||
packet = await self.receive_function()
|
packet = await self.receive_function()
|
||||||
self.parser.feed_data(packet)
|
self.parser.feed_data(packet)
|
||||||
except asyncio.exceptions.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.debug('source pump task done')
|
logger.debug('source pump task done')
|
||||||
|
self.terminated.set_result(None)
|
||||||
break
|
break
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
logger.warning(f'exception while waiting for packet: {error}')
|
logger.warning(f'exception while waiting for packet: {error}')
|
||||||
@@ -370,7 +371,7 @@ class PumpedPacketSink:
|
|||||||
try:
|
try:
|
||||||
packet = await self.packet_queue.get()
|
packet = await self.packet_queue.get()
|
||||||
await self.send_function(packet)
|
await self.send_function(packet)
|
||||||
except asyncio.exceptions.CancelledError:
|
except asyncio.CancelledError:
|
||||||
logger.debug('sink pump task done')
|
logger.debug('sink pump task done')
|
||||||
break
|
break
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
@@ -393,19 +394,13 @@ class PumpedTransport(Transport):
|
|||||||
self,
|
self,
|
||||||
source: PumpedPacketSource,
|
source: PumpedPacketSource,
|
||||||
sink: PumpedPacketSink,
|
sink: PumpedPacketSink,
|
||||||
close_function,
|
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(source, sink)
|
super().__init__(source, sink)
|
||||||
self.close_function = close_function
|
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
self.source.start()
|
self.source.start()
|
||||||
self.sink.start()
|
self.sink.start()
|
||||||
|
|
||||||
async def close(self) -> None:
|
|
||||||
await super().close()
|
|
||||||
await self.close_function()
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
class SnoopingTransport(Transport):
|
class SnoopingTransport(Transport):
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import socket
|
|||||||
import ctypes
|
import ctypes
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from .common import Transport, ParserSource
|
from .common import Transport, ParserSource
|
||||||
|
|
||||||
|
|
||||||
@@ -33,7 +35,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def open_hci_socket_transport(spec: str | None) -> Transport:
|
async def open_hci_socket_transport(spec: Optional[str]) -> Transport:
|
||||||
'''
|
'''
|
||||||
Open an HCI Socket (only available on some platforms).
|
Open an HCI Socket (only available on some platforms).
|
||||||
The parameter string is either empty (to use the first/default Bluetooth adapter)
|
The parameter string is either empty (to use the first/default Bluetooth adapter)
|
||||||
@@ -45,9 +47,9 @@ async def open_hci_socket_transport(spec: str | None) -> Transport:
|
|||||||
# Create a raw HCI socket
|
# Create a raw HCI socket
|
||||||
try:
|
try:
|
||||||
hci_socket = socket.socket(
|
hci_socket = socket.socket(
|
||||||
socket.AF_BLUETOOTH,
|
socket.AF_BLUETOOTH, # type: ignore[attr-defined]
|
||||||
socket.SOCK_RAW | socket.SOCK_NONBLOCK,
|
socket.SOCK_RAW | socket.SOCK_NONBLOCK, # type: ignore[attr-defined]
|
||||||
socket.BTPROTO_HCI, # type: ignore
|
socket.BTPROTO_HCI, # type: ignore[attr-defined]
|
||||||
)
|
)
|
||||||
except AttributeError as error:
|
except AttributeError as error:
|
||||||
# Not supported on this platform
|
# Not supported on this platform
|
||||||
@@ -78,7 +80,7 @@ async def open_hci_socket_transport(spec: str | None) -> Transport:
|
|||||||
bind_address = struct.pack(
|
bind_address = struct.pack(
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
'<HHH',
|
'<HHH',
|
||||||
socket.AF_BLUETOOTH,
|
socket.AF_BLUETOOTH, # type: ignore[attr-defined]
|
||||||
adapter_index,
|
adapter_index,
|
||||||
HCI_CHANNEL_USER,
|
HCI_CHANNEL_USER,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import atexit
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from .common import Transport, StreamPacketSource, StreamPacketSink
|
from .common import Transport, StreamPacketSource, StreamPacketSink
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -32,7 +34,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def open_pty_transport(spec: str | None) -> Transport:
|
async def open_pty_transport(spec: Optional[str]) -> Transport:
|
||||||
'''
|
'''
|
||||||
Open a PTY transport.
|
Open a PTY transport.
|
||||||
The parameter string may be empty, or a path name where a symbolic link
|
The parameter string may be empty, or a path name where a symbolic link
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from .common import Transport
|
from .common import Transport
|
||||||
from .file import open_file_transport
|
from .file import open_file_transport
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
async def open_vhci_transport(spec: str | None) -> Transport:
|
async def open_vhci_transport(spec: Optional[str]) -> Transport:
|
||||||
'''
|
'''
|
||||||
Open a VHCI transport (only available on some platforms).
|
Open a VHCI transport (only available on some platforms).
|
||||||
The parameter string is either empty (to use the default VHCI device
|
The parameter string is either empty (to use the default VHCI device
|
||||||
|
|||||||
@@ -31,19 +31,21 @@ async def open_ws_client_transport(spec: str) -> Transport:
|
|||||||
'''
|
'''
|
||||||
Open a WebSocket client transport.
|
Open a WebSocket client transport.
|
||||||
The parameter string has this syntax:
|
The parameter string has this syntax:
|
||||||
<remote-host>:<remote-port>
|
<websocket-url>
|
||||||
|
|
||||||
Example: 127.0.0.1:9001
|
Example: ws://localhost:7681/v1/websocket/bt
|
||||||
'''
|
'''
|
||||||
|
|
||||||
remote_host, remote_port = spec.split(':')
|
websocket = await websockets.client.connect(spec)
|
||||||
uri = f'ws://{remote_host}:{remote_port}'
|
|
||||||
websocket = await websockets.client.connect(uri)
|
|
||||||
|
|
||||||
transport = PumpedTransport(
|
class WsTransport(PumpedTransport):
|
||||||
|
async def close(self):
|
||||||
|
await super().close()
|
||||||
|
await websocket.close()
|
||||||
|
|
||||||
|
transport = WsTransport(
|
||||||
PumpedPacketSource(websocket.recv),
|
PumpedPacketSource(websocket.recv),
|
||||||
PumpedPacketSink(websocket.send),
|
PumpedPacketSink(websocket.send),
|
||||||
websocket.close,
|
|
||||||
)
|
)
|
||||||
transport.start()
|
transport.start()
|
||||||
return transport
|
return transport
|
||||||
|
|||||||
129
bumble/utils.py
@@ -15,13 +15,25 @@
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# Imports
|
# Imports
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
from __future__ import annotations
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
import collections
|
import collections
|
||||||
import sys
|
import sys
|
||||||
from typing import Awaitable, Set, TypeVar
|
from typing import (
|
||||||
from functools import wraps
|
Awaitable,
|
||||||
|
Set,
|
||||||
|
TypeVar,
|
||||||
|
List,
|
||||||
|
Tuple,
|
||||||
|
Callable,
|
||||||
|
Any,
|
||||||
|
Optional,
|
||||||
|
Union,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
|
from functools import wraps, partial
|
||||||
from pyee import EventEmitter
|
from pyee import EventEmitter
|
||||||
|
|
||||||
from .colors import color
|
from .colors import color
|
||||||
@@ -64,6 +76,102 @@ def composite_listener(cls):
|
|||||||
return cls
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
_Handler = TypeVar('_Handler', bound=Callable)
|
||||||
|
|
||||||
|
|
||||||
|
class EventWatcher:
|
||||||
|
'''A wrapper class to control the lifecycle of event handlers better.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
```
|
||||||
|
watcher = EventWatcher()
|
||||||
|
|
||||||
|
def on_foo():
|
||||||
|
...
|
||||||
|
watcher.on(emitter, 'foo', on_foo)
|
||||||
|
|
||||||
|
@watcher.on(emitter, 'bar')
|
||||||
|
def on_bar():
|
||||||
|
...
|
||||||
|
|
||||||
|
# Close all event handlers watching through this watcher
|
||||||
|
watcher.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
As context:
|
||||||
|
```
|
||||||
|
with contextlib.closing(EventWatcher()) as context:
|
||||||
|
@context.on(emitter, 'foo')
|
||||||
|
def on_foo():
|
||||||
|
...
|
||||||
|
# on_foo() has been removed here!
|
||||||
|
```
|
||||||
|
'''
|
||||||
|
|
||||||
|
handlers: List[Tuple[EventEmitter, str, Callable[..., Any]]]
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.handlers = []
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def on(self, emitter: EventEmitter, event: str) -> Callable[[_Handler], _Handler]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def on(self, emitter: EventEmitter, event: str, handler: _Handler) -> _Handler:
|
||||||
|
...
|
||||||
|
|
||||||
|
def on(
|
||||||
|
self, emitter: EventEmitter, event: str, handler: Optional[_Handler] = None
|
||||||
|
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
|
||||||
|
'''Watch an event until the context is closed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
emitter: EventEmitter to watch
|
||||||
|
event: Event name
|
||||||
|
handler: (Optional) Event handler. When nothing is passed, this method works as a decorator.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def wrapper(f: _Handler) -> _Handler:
|
||||||
|
self.handlers.append((emitter, event, f))
|
||||||
|
emitter.on(event, f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
return wrapper if handler is None else wrapper(handler)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def once(self, emitter: EventEmitter, event: str) -> Callable[[_Handler], _Handler]:
|
||||||
|
...
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def once(self, emitter: EventEmitter, event: str, handler: _Handler) -> _Handler:
|
||||||
|
...
|
||||||
|
|
||||||
|
def once(
|
||||||
|
self, emitter: EventEmitter, event: str, handler: Optional[_Handler] = None
|
||||||
|
) -> Union[_Handler, Callable[[_Handler], _Handler]]:
|
||||||
|
'''Watch an event for once.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
emitter: EventEmitter to watch
|
||||||
|
event: Event name
|
||||||
|
handler: (Optional) Event handler. When nothing passed, this method works as a decorator.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def wrapper(f: _Handler) -> _Handler:
|
||||||
|
self.handlers.append((emitter, event, f))
|
||||||
|
emitter.once(event, f)
|
||||||
|
return f
|
||||||
|
|
||||||
|
return wrapper if handler is None else wrapper(handler)
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
for emitter, event, handler in self.handlers:
|
||||||
|
if handler in emitter.listeners(event):
|
||||||
|
emitter.remove_listener(event, handler)
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
_T = TypeVar('_T')
|
_T = TypeVar('_T')
|
||||||
|
|
||||||
@@ -302,3 +410,20 @@ class FlowControlAsyncPipe:
|
|||||||
self.resume_source()
|
self.resume_source()
|
||||||
|
|
||||||
self.check_pump()
|
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)
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ nav:
|
|||||||
- Zephyr: platforms/zephyr.md
|
- Zephyr: platforms/zephyr.md
|
||||||
- Examples:
|
- Examples:
|
||||||
- Overview: examples/index.md
|
- Overview: examples/index.md
|
||||||
|
- Extras:
|
||||||
|
- Overview: extras/index.md
|
||||||
|
- Android Remote HCI: extras/android_remote_hci.md
|
||||||
|
|
||||||
copyright: Copyright 2021-2023 Google LLC
|
copyright: Copyright 2021-2023 Google LLC
|
||||||
|
|
||||||
|
|||||||
141
docs/mkdocs/src/extras/android_remote_hci.md
Normal file
@@ -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
|
||||||
|
```
|
||||||
11
docs/mkdocs/src/extras/index.md
Normal file
@@ -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.
|
||||||
@@ -3,7 +3,7 @@ HARDWARE
|
|||||||
|
|
||||||
The Bumble Host connects to a controller over an [HCI Transport](../transports/index.md).
|
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).
|
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.
|
In theory, any controller that is compliant with the HCI over UART or HCI over USB protocols can be used.
|
||||||
|
|
||||||
|
|||||||
10
extras/android/RemoteHCI/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
1
extras/android/RemoteHCI/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
73
extras/android/RemoteHCI/app/build.gradle.kts
Normal file
@@ -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"))
|
||||||
|
}
|
||||||
21
extras/android/RemoteHCI/app/proguard-rules.pro
vendored
Normal file
@@ -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
|
||||||
29
extras/android/RemoteHCI/app/src/main/AndroidManifest.xml
Normal file
@@ -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>
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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 android.hardware.bluetooth@1.1;
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
@@ -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 android.hardware.bluetooth@1.1;
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
BIN
extras/android/RemoteHCI/app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 = "android.hardware.bluetooth@1.0::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("android.hardware.bluetooth@1.0::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("android.hardware.bluetooth@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceChain on an IChild object must yield the following:
|
||||||
|
* ["android.hardware.foo@1.0::IChild",
|
||||||
|
* "android.hardware.foo@1.0::IParent"
|
||||||
|
* "android.internal.hidl.base@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceDescriptor on an IChild object must yield
|
||||||
|
* "android.hardware.foo@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = "android.hardware.bluetooth@1.0::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("android.hardware.bluetooth@1.0::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("android.hardware.bluetooth@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceChain on an IChild object must yield the following:
|
||||||
|
* ["android.hardware.foo@1.0::IChild",
|
||||||
|
* "android.hardware.foo@1.0::IParent"
|
||||||
|
* "android.internal.hidl.base@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceDescriptor on an IChild object must yield
|
||||||
|
* "android.hardware.foo@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
@@ -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 = "android.hardware.bluetooth@1.1::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("android.hardware.bluetooth@1.1::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("android.hardware.bluetooth@1.1::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceChain on an IChild object must yield the following:
|
||||||
|
* ["android.hardware.foo@1.0::IChild",
|
||||||
|
* "android.hardware.foo@1.0::IParent"
|
||||||
|
* "android.internal.hidl.base@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceDescriptor on an IChild object must yield
|
||||||
|
* "android.hardware.foo@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = "android.hardware.bluetooth@1.1::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("android.hardware.bluetooth@1.1::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("android.hardware.bluetooth@1.1::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceChain on an IChild object must yield the following:
|
||||||
|
* ["android.hardware.foo@1.0::IChild",
|
||||||
|
* "android.hardware.foo@1.0::IParent"
|
||||||
|
* "android.internal.hidl.base@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* interface IParent {};
|
||||||
|
* interface IChild extends IParent {};
|
||||||
|
* Calling interfaceDescriptor on an IChild object must yield
|
||||||
|
* "android.hardware.foo@1.0::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 android.hardware.foo@1.0;
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.github.google.bumble.remotehci;
|
||||||
|
|
||||||
|
public interface HciHalCallback {
|
||||||
|
public void onPacket(HciPacket.Type type, byte[] packet);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
)
|
||||||
|
*/
|
||||||
|
)
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 13 KiB |
10
extras/android/RemoteHCI/app/src/main/res/values/colors.xml
Normal file
@@ -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>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">Remote HCI</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="Theme.RemoteHCI" parent="android:Theme.Material.Light.NoActionBar" />
|
||||||
|
</resources>
|
||||||
@@ -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>
|
||||||
@@ -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>
|
||||||
8
extras/android/RemoteHCI/build.gradle.kts
Normal file
@@ -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
|
||||||
23
extras/android/RemoteHCI/gradle.properties
Normal file
@@ -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
|
||||||
36
extras/android/RemoteHCI/gradle/libs.versions.toml
Normal file
@@ -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" }
|
||||||
|
|
||||||
BIN
extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
extras/android/RemoteHCI/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -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
|
||||||
185
extras/android/RemoteHCI/gradlew
vendored
Executable file
@@ -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" "$@"
|
||||||
89
extras/android/RemoteHCI/gradlew.bat
vendored
Normal file
@@ -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
|
||||||
1
extras/android/RemoteHCI/lib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
39
extras/android/RemoteHCI/lib/build.gradle.kts
Normal file
@@ -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)
|
||||||
|
}
|
||||||
0
extras/android/RemoteHCI/lib/consumer-rules.pro
Normal file
21
extras/android/RemoteHCI/lib/proguard-rules.pro
vendored
Normal file
@@ -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
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 = "android.hidl.base@1.0::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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();}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package android.os;
|
||||||
|
|
||||||
|
public interface IHwInterface {
|
||||||
|
public IHwBinder asBinder();
|
||||||
|
}
|
||||||