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

Commit 1a116708 authored by JohnLai's avatar JohnLai
Browse files

floss: pandora: Handle gRPC method error

When an unexpected error occurs in a gRPC request,
pass the status code and details back to the client.

Bug: 312653014
Test: m Bluetooth && pts-bot
Flag: EXEMPT floss only changes
Change-Id: Ie4eb76337a32445c1aad1a75649c5e552f0bff73
parent a2a4b28e
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -54,8 +54,9 @@ class HIDService(hid_grpc_aio.HIDServicer):
                future = self.task['set_hid_report']
                future.get_loop().call_soon_threadsafe(future.set_result, None)

        if request.address is None:
            raise ValueError('Request address field must be set.')
        if not request.address:
            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Request address field must be set.')

        address = utils.address_from(request.address)

        try:
@@ -64,9 +65,10 @@ class HIDService(hid_grpc_aio.HIDServicer):
            name = utils.create_observer_name(observer)
            self.bluetooth.qa_client.register_callback_observer(name, observer)
            if request.report_type not in iter(floss_enums.BthhReportType):
                raise ValueError('Invalid report type.')
                await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Invalid report type.')

            if not self.bluetooth.set_hid_report(address, request.report_type, request.report):
                raise RuntimeError('Failed to call set_hid_report.')
                await context.abort(grpc.StatusCode.UNKNOWN, 'Failed to call set_hid_report.')

            await asyncio.wait_for(set_hid_report, timeout=5)

+19 −11
Original line number Diff line number Diff line
@@ -21,8 +21,8 @@ import uuid as uuid_module
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 scanner_client
from floss.pandora.floss import utils
from floss.pandora.server import bluetooth as bluetooth_module
from google.protobuf import empty_pb2
@@ -165,7 +165,8 @@ class HostService(host_grpc_aio.HostServicer):
                    success, reason = await connect_device

                    if not success:
                        raise RuntimeError(f'Failed to connect to the {address}. Reason: {reason}')
                        await context.abort(grpc.StatusCode.UNKNOWN,
                                            f'Failed to connect to the {address}. Reason: {reason}.')
                else:
                    if not self.security.manually_confirm:
                        create_bond = asyncio.get_running_loop().create_future()
@@ -181,13 +182,14 @@ class HostService(host_grpc_aio.HostServicer):
                        self.bluetooth.adapter_client.register_callback_observer(name, observer)

                    if not self.bluetooth.create_bond(address, floss_enums.BtTransport.BREDR):
                        raise RuntimeError('Failed to call create_bond.')
                        await context.abort(grpc.StatusCode.UNKNOWN, 'Failed to call create_bond.')

                    if not self.security.manually_confirm:
                        success, reason = await create_bond

                        if not success:
                            raise RuntimeError(f'Failed to connect to the {address}. Reason: {reason}')
                            await context.abort(grpc.StatusCode.UNKNOWN,
                                                f'Failed to connect to the {address}. Reason: {reason}.')

                        if self.bluetooth.is_bonded(address) and self.bluetooth.is_connected(address):
                            self.bluetooth.connect_device(address)
@@ -215,8 +217,9 @@ class HostService(host_grpc_aio.HostServicer):
                future = self.task['wait_connection']
                future.get_loop().call_soon_threadsafe(future.set_result, address)

        if request.address is None:
            raise ValueError('Request address field must be set.')
        if not request.address:
            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Request address field must be set.')

        address = utils.address_from(request.address)

        if not self.bluetooth.is_connected(address) or address not in self.waited_connections:
@@ -257,11 +260,11 @@ class HostService(host_grpc_aio.HostServicer):
                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')
            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, '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}.')
            await context.abort(grpc.StatusCode.UNIMPLEMENTED, f'Unsupported OwnAddressType: {own_address_type}.')

        address = utils.address_from(request.address)
        self.initiated_le_connection.add(address)
@@ -273,7 +276,8 @@ class HostService(host_grpc_aio.HostServicer):
            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}')
                await context.abort(grpc.StatusCode.UNKNOWN,
                                    f'Failed to connect to the address: {address}. Reason: {reason}.')
        finally:
            self.bluetooth.gatt_client.unregister_callback_observer(name, observer)

@@ -305,8 +309,9 @@ class HostService(host_grpc_aio.HostServicer):
                future = self.task['wait_disconnection']
                future.get_loop().call_soon_threadsafe(future.set_result, address)

        if request.address is None:
            raise ValueError('Request address field must be set')
        if not request.address:
            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Request address field must be set.')

        address = utils.address_from(request.address)

        if self.bluetooth.is_connected(address):
@@ -410,6 +415,9 @@ class HostService(host_grpc_aio.HostServicer):
            observers.append((name, observer))

            advertiser_id = await asyncio.wait_for(advertising_request['start_advertising'], timeout=5)
            if advertiser_id is None:
                await context.abort(grpc.StatusCode.UNKNOWN, 'Failed to start advertising.')

            started_ids.append(advertiser_id)

            while True:
+16 −15
Original line number Diff line number Diff line
@@ -101,7 +101,8 @@ class SecurityService(security_grpc_aio.SecurityServicer):
        if level == security_pb2.LE_LEVEL1:
            return True
        if level == security_pb2.LE_LEVEL4:
            raise RuntimeError(f'wait_le_security_level: Low-energy level 4 not supported')
            logging.error('wait_le_security_level: Low-energy level 4 not supported.')
            return False

        if self.bluetooth.is_bonded(address):
            is_bonded = True
@@ -122,7 +123,9 @@ class SecurityService(security_grpc_aio.SecurityServicer):
            return is_encrypted
        if level == security_pb2.LE_LEVEL3:
            return is_encrypted and is_bonded
        raise ValueError(f'wait_le_security_level: Invalid security level {level}')

        logging.error('wait_le_security_level: Invalid security level %s.', level)
        return False

    async def wait_classic_security_level(self, level, address):

@@ -151,7 +154,8 @@ class SecurityService(security_grpc_aio.SecurityServicer):
        if level == security_pb2.LEVEL0:
            return True
        if level == security_pb2.LEVEL3:
            raise RuntimeError('wait_classic_security_level: Classic level 3 not supported')
            logging.error('wait_classic_security_level: Classic level 3 not supported')
            return False

        if self.bluetooth.is_bonded(address):
            is_bonded = True
@@ -311,28 +315,29 @@ class SecurityService(security_grpc_aio.SecurityServicer):

        if transport == floss_enums.BtTransport.LE:
            if not request.HasField('le'):
                raise RuntimeError('Secure: Request le field must be set.')
                await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Request le field must be set.')
            if request.le == security_pb2.LE_LEVEL1:
                security_level_reached = True
            elif request.le == security_pb2.LE_LEVEL4:
                raise RuntimeError('Secure: Low-energy security level 4 not supported')
                await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Low-energy security level 4 is not supported.')
            else:
                if not self.bluetooth.is_bonded(address):
                    self.bluetooth.create_bond(address, transport)
                security_level_reached = await self.wait_le_security_level(request.le, address)
        elif transport == floss_enums.BtTransport.BREDR:
            if not request.HasField('classic'):
                raise RuntimeError('Secure: Request classic field must be set.')
                await context.abort(grpc.StatusCode.INVALID_ARGUMENT, 'Request classic field must be set.')
            if request.classic == security_pb2.LEVEL0:
                security_level_reached = True
            elif request.classic >= security_pb2.LEVEL3:
                raise RuntimeError('Secure: Classic security level up to 3 not supported')
                await context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                                    'Classic security level up to 3 is not supported.')
            else:
                if not self.bluetooth.is_bonded(address):
                    self.bluetooth.create_bond(address, transport)
                security_level_reached = await self.wait_classic_security_level(request.classic, address)
        else:
            raise RuntimeError(f'Secure: Invalid bluetooth transport type: {transport}')
            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f'Invalid bluetooth transport type: {transport}.')

        secure_response = security_pb2.SecureResponse()
        if security_level_reached:
@@ -351,7 +356,7 @@ class SecurityService(security_grpc_aio.SecurityServicer):
        elif transport == floss_enums.BtTransport.BREDR:
            security_level_reached = await self.wait_classic_security_level(request.classic, address)
        else:
            raise RuntimeError(f'WaitSecurity: Invalid bluetooth transport type: {transport}')
            await context.abort(grpc.StatusCode.INVALID_ARGUMENT, f'Invalid bluetooth transport type: {transport}.')

        wait_security_response = security_pb2.WaitSecurityResponse()
        if security_level_reached:
@@ -376,8 +381,6 @@ class SecurityStorageService(security_grpc_aio.SecurityStorageServicer):

    async def IsBonded(self, request: security_pb2.IsBondedRequest,
                       context: grpc.ServicerContext) -> wrappers_pb2.BoolValue:
        if not (request.HasField('public') or request.HasField('random')):
            raise ValueError('Invalid request address field.')

        address = utils.address_from(request.address)
        is_bonded = self.bluetooth.is_bonded(address)
@@ -411,9 +414,6 @@ class SecurityStorageService(security_grpc_aio.SecurityStorageServicer):
                        future.set_result, (False, f'{address} failed on remove_bond, got bond state {state},'
                                            f' want {floss_enums.BondState.NOT_BONDED}'))

        if not (request.HasField('public') or request.HasField('random')):
            raise ValueError('Invalid request address field.')

        address = utils.address_from(request.address)
        if not self.bluetooth.is_bonded(address):
            return empty_pb2.Empty()
@@ -425,7 +425,8 @@ class SecurityStorageService(security_grpc_aio.SecurityStorageServicer):
            self.bluetooth.remove_bond(address)
            success, reason = await remove_bond
            if not success:
                raise RuntimeError(f'Failed to remove bond of address: {address}. Reason: {reason}')
                await context.abort(grpc.StatusCode.INVALID_ARGUMENT,
                                    f'Failed to remove bond of address: {address}. Reason: {reason}.')
        finally:
            self.bluetooth.adapter_client.unregister_callback_observer(name, observer)
        return empty_pb2.Empty()