1# Copyright 2022 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Bumble Pandora server.""" 16 17__version__ = "0.0.1" 18 19import asyncio 20import grpc 21import grpc.aio 22import logging 23 24from avatar.bumble_device import BumbleDevice 25from avatar.bumble_server.host import HostService 26from avatar.bumble_server.security import SecurityService, SecurityStorageService 27from bumble.smp import PairingDelegate 28from pandora.host_grpc_aio import add_HostServicer_to_server 29from pandora.security_grpc_aio import add_SecurityServicer_to_server, add_SecurityStorageServicer_to_server 30from typing import Callable, List, Optional 31 32# Add servicers hooks. 33_SERVICERS_HOOKS: List[Callable[[BumbleDevice, grpc.aio.Server], None]] = [] 34 35 36def register_servicer_hook(hook: Callable[[BumbleDevice, grpc.aio.Server], None]) -> None: 37 _SERVICERS_HOOKS.append(hook) 38 39 40async def serve_bumble(bumble: BumbleDevice, grpc_server: Optional[grpc.aio.Server] = None, port: int = 0) -> None: 41 # initialize a gRPC server if not provided. 42 server = grpc_server if grpc_server is not None else grpc.aio.server() 43 port = server.add_insecure_port(f'localhost:{port}') 44 45 # load IO capability from config. 46 io_capability_name: str = bumble.config.get('io_capability', 'no_output_no_input').upper() 47 io_capability: int = getattr(PairingDelegate, io_capability_name) 48 49 try: 50 while True: 51 # add Pandora services to the gRPC server. 52 add_HostServicer_to_server(HostService(server, bumble.device), server) 53 add_SecurityServicer_to_server(SecurityService(bumble.device, io_capability), server) 54 add_SecurityStorageServicer_to_server(SecurityStorageService(bumble.device), server) 55 56 # call hooks if any. 57 for hook in _SERVICERS_HOOKS: 58 hook(bumble, server) 59 60 # open device. 61 await bumble.open() 62 try: 63 # Pandora require classic devices to to be discoverable & connectable. 64 if bumble.device.classic_enabled: 65 await bumble.device.set_discoverable(False) 66 await bumble.device.set_connectable(True) 67 68 # start & serve gRPC server. 69 await server.start() 70 await server.wait_for_termination() 71 finally: 72 # close device. 73 await bumble.close() 74 75 # re-initialize the gRPC server. 76 server = grpc.aio.server() 77 server.add_insecure_port(f'localhost:{port}') 78 finally: 79 # stop server. 80 await server.stop(None) 81 82 83BUMBLE_SERVER_GRPC_PORT = 7999 84ROOTCANAL_PORT_CUTTLEFISH = 7300 85 86if __name__ == '__main__': 87 bumble = BumbleDevice({'transport': f'tcp-client:127.0.0.1:{ROOTCANAL_PORT_CUTTLEFISH}', 'classic_enabled': True}) 88 logging.basicConfig(level=logging.DEBUG) 89 asyncio.run(serve_bumble(bumble, port=BUMBLE_SERVER_GRPC_PORT)) 90