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

Commit 01bed159 authored by Nagham Masalmah's avatar Nagham Masalmah Committed by JohnLai
Browse files

Floss: Implements missing methods for Pandora Host profile

Implement ConnectLE and SetConnectability methods in Host profile.

Bug: 309640882
Bug: 309682564
Test: mma packages/modules/Bluetooth && pts-bot GATT
Tag: #floss
Flag: EXEMPT floss only changes
Change-Id: Id5107d96364cb6e86cfccf3f3c6370c9b43f8456
parent 3c95c0c0
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -1015,3 +1015,23 @@ class FlossGattClient(GattClientCallbacks):
        if not self.wait_for_search_complete(address):
            return None
        return self.gatt_services[address]

    def register_callback_observer(self, name, observer):
        """Adds an observer for all callbacks.

        Args:
            name: Name of the observer.
            observer: Observer that implements all callback classes.
        """
        if isinstance(observer, GattClientCallbacks):
            self.callbacks.add_observer(name, observer)

    def unregister_callback_observer(self, name, observer):
        """Removes an observer for all callbacks.

        Args:
            name: Name of the observer.
            observer: Observer that implements all callback classes.
        """
        if isinstance(observer, GattClientCallbacks):
            self.callbacks.remove_observer(name, observer)
+17 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ from floss.pandora.floss import advertising_client
from floss.pandora.floss import manager_client
from floss.pandora.floss import qa_client
from floss.pandora.floss import scanner_client
from floss.pandora.floss import gatt_client
from floss.pandora.floss import floss_enums
from floss.pandora.floss import utils
from gi.repository import GLib
import pydbus
@@ -48,6 +50,8 @@ class Bluetooth(object):
    SCANNER_WINDOW = 0
    SCANNER_SCAN_TYPE = 0

    FAKE_GATT_APP_ID = '12345678123456781234567812345678'

    def __init__(self):
        self.setup_mainloop()

@@ -60,6 +64,7 @@ class Bluetooth(object):
        self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, self.DEFAULT_ADAPTER)
        self.scanner_client = scanner_client.FlossScannerClient(self.bus, self.DEFAULT_ADAPTER)
        self.qa_client = qa_client.FlossQAClient(self.bus, self.DEFAULT_ADAPTER)
        self.gatt_client = gatt_client.FlossGattClient(self.bus, self.DEFAULT_ADAPTER)

    def __del__(self):
        if not self.is_clean:
@@ -124,6 +129,9 @@ class Bluetooth(object):
        if not self.qa_client.register_qa_callback():
            logging.error('qa_client: Failed to register callbacks')
            return False
        if not self.gatt_client.register_client(self.FAKE_GATT_APP_ID, False):
            logging.error('gatt_client: Failed to register callbacks')
            return False
        return True

    def is_bluetoothd_proxy_valid(self):
@@ -134,7 +142,8 @@ class Bluetooth(object):
            self.adapter_client.has_proxy(),
            self.advertising_client.has_proxy(),
            self.scanner_client.has_proxy(),
            self.qa_client.has_proxy()
            self.qa_client.has_proxy(),
            self.gatt_client.has_proxy(),
        ])

        if not proxy_ready:
@@ -168,6 +177,7 @@ class Bluetooth(object):
            self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, default_adapter)
            self.scanner_client = scanner_client.FlossScannerClient(self.bus, default_adapter)
            self.qa_client = qa_client.FlossQAClient(self.bus, default_adapter)
            self.gatt_client = gatt_client.FlossGattClient(self.bus, default_adapter)

            try:
                utils.poll_for_condition(
@@ -287,3 +297,9 @@ class Bluetooth(object):

    def set_hid_report(self, addr, report_type, report):
        return self.qa_client.set_hid_report(addr, report_type, report)

    def gatt_connect(self, address, is_direct, transport):
        return self.gatt_client.connect_client(address, is_direct, transport)

    def set_connectable(self, mode):
        return self.qa_client.set_connectable(mode)
+50 −6
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ from floss.pandora.floss import adapter_client
from floss.pandora.floss import advertising_client
from floss.pandora.floss import floss_enums
from floss.pandora.floss import scanner_client
from floss.pandora.floss import gatt_client
from floss.pandora.floss import utils
from floss.pandora.server import bluetooth as bluetooth_module
from google.protobuf import empty_pb2
@@ -47,9 +48,11 @@ class HostService(host_grpc_aio.HostServicer):
        self.bluetooth = bluetooth
        self.security = security
        self.waited_connections = set()
        self.initiated_le_connection = set()

    async def FactoryReset(self, request: empty_pb2.Empty, context: grpc.ServicerContext) -> empty_pb2.Empty:
        self.waited_connections.clear()
        self.initiated_le_connection.clear()

        devices = self.bluetooth.get_bonded_devices()
        if devices is None:
@@ -65,6 +68,7 @@ class HostService(host_grpc_aio.HostServicer):

    async def Reset(self, request: empty_pb2.Empty, context: grpc.ServicerContext) -> empty_pb2.Empty:
        self.waited_connections.clear()
        self.initiated_le_connection.clear()
        self.bluetooth.reset()
        return empty_pb2.Empty()

@@ -233,9 +237,49 @@ class HostService(host_grpc_aio.HostServicer):

    async def ConnectLE(self, request: host_pb2.ConnectLERequest,
                        context: grpc.ServicerContext) -> host_pb2.ConnectLEResponse:
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)  # type: ignore
        context.set_details('Method not implemented!')  # type: ignore
        raise NotImplementedError('Method not implemented!')

        class ConnectionObserver(gatt_client.GattClientCallbacks):
            """Observer to observe the connection state."""

            def __init__(self, task):
                self.task = task

            @utils.glib_callback()
            def on_client_connection_state(self, status, client_id, connected, addr):
                if addr != self.task['address']:
                    return

                future = self.task['connect_le_device']
                if status != floss_enums.GattStatus.SUCCESS:
                    future.get_loop().call_soon_threadsafe(future.set_result,
                                                           (False, f'{address} failed to connect. Status: {status}.'))
                    return

                future.get_loop().call_soon_threadsafe(future.set_result, (connected, None))

        if not request.address:
            raise ValueError('Connect LE: Request address field must be set')

        own_address_type = request.own_address_type
        if own_address_type not in (host_pb2.RANDOM, host_pb2.RESOLVABLE_OR_RANDOM):
            raise RuntimeError(f'ConnectLE: Unsupported OwnAddressType: {own_address_type}.')

        address = utils.address_from(request.address)
        self.initiated_le_connection.add(address)
        try:
            connect_le_device = asyncio.get_running_loop().create_future()
            observer = ConnectionObserver({'connect_le_device': connect_le_device, 'address': address})
            name = utils.create_observer_name(observer)
            self.bluetooth.gatt_client.register_callback_observer(name, observer)
            self.bluetooth.gatt_connect(address, True, floss_enums.BtTransport.LE)
            connected, reason = await connect_le_device
            if not connected:
                raise RuntimeError(f'Failed to connect to the address: {address}. Reason: {reason}')
        finally:
            self.bluetooth.gatt_client.unregister_callback_observer(name, observer)

        return host_pb2.ConnectLEResponse(
            connection=utils.connection_to(utils.Connection(address, floss_enums.BtTransport.LE)))

    async def Disconnect(self, request: host_pb2.DisconnectRequest, context: grpc.ServicerContext) -> empty_pb2.Empty:
        address = utils.connection_from(request.connection).address
@@ -556,6 +600,6 @@ class HostService(host_grpc_aio.HostServicer):

    async def SetConnectabilityMode(self, request: host_pb2.SetConnectabilityModeRequest,
                                    context: grpc.ServicerContext) -> empty_pb2.Empty:
        context.set_code(grpc.StatusCode.UNIMPLEMENTED)  # type: ignore
        context.set_details('Method not implemented!')  # type: ignore
        raise NotImplementedError('Method not implemented!')
        mode = request.mode
        self.bluetooth.set_connectable(mode)
        return empty_pb2.Empty()