google / bumble

Apache License 2.0
259 stars 74 forks source link

Cannot bridge tcp-server to android-netsim #217

Closed vChavezB closed 1 year ago

vChavezB commented 1 year ago

As a follow up to this issue

https://github.com/google/bumble/issues/215

I am trying to connect a Zephyr App through a bumble virtual controller to an android emulator. I first executed the run_gatt_server.py example to verify that the grpc server from the android emulator is working. When I scan the device I can see it in the Android emulator.

However at this point I am not sure if I understand how to link the tcp-server virtual controller to the android-netsim host as I have not been able to connect a tcp-server virtual controller to the android-netsim transport.

Here have been my attempts

HCI Bridge

  1. Run the virtual controller

python controllers.py tcp-server:172.22.0.1:9000 tcp-server:172.22.0.1:9001

  1. Run Android emulator

emulator-avd Pixel_7_API_34 -packet-streamer-endpoint default -grpc 8557

  1. Connect my zephyr app to tcp-server:172.22.0.1:9000

  2. Create an hci-bridge

python hci_bridge.py tcp-client:172.22.0.1:9001 android-netsim

Relay Link

  1. Run the link relay

python link_relay

  1. Run the virtual controller with link relay

python controllers.py tcp-server:172.22.0.1:9000 link-relay:ws://localhost:10723/test

  1. Run Android emulator

emulator-avd Pixel_7_API_34 -packet-streamer-endpoint default -grpc 8557

  1. Connect my zephyr app to tcp-server:172.22.0.1:9000

  2. Bridge android-netsim host to link relay

python hci_bridge.py android-netsim link-relay:ws://localhost:10723/test

barbibulle commented 1 year ago

The reason this doesn't work is that you're bridging two controllers together, which isn't going to work (despite the generic "bridge" name, the HCI bridge app can only be used to bridge the transports of a Controller and a Host). The topolgy is: [app/device]<->[host]<-{hci}->[controller]<--- (radio/link) --->[controller]<-{hci}->[host]<->[app/device] An app/device communicates with another app/device, via the Host part of a stack, with talks to the Controller part of a stack, which then exchanges link layer data with other Controllers via a shared link (which is going to be a radio for a physical link, or a virtual link). Hosts and Controllers communicate via HCI packets. What Bumble calls a transport is a bi-directional stream of HCI packets between a Host and a Controller. The controllers app creates several controllers that are all tied via a local Link. Then you can connect multiple apps, one to each controller, via their own Host. So, if you connect the HCI side of a Controller to the HCI side of another Controller, that's a mismatch. Also, what maybe led to confusion here is that there are two types of controllers here: The Bumble virtual controller (which is what gets instantiated by the controllers app), and the Netsim virtual controller (with an internal component called Root Canal) which gets instantiated by the Android emulator. Netsim can create many controllers on demand (via a gRPC interface), which all can exchange packets with each other. The Bumble controllers app does the same, but you pass in an explicit list of controllers (each with its own transport interface), rather than being created on demand. But the Bumble virtual controllers can only exchange packets with other bumble virtual controllers attached to the same local Link (that's why, for example, if you run two controllers apps, devices attached to different instances won't see each other, you should only have one controllers app, with multiple controllers). Same with the Netsim controllers: only controllers instantiated by the same Netsim instance can "see" each other.

But... that's Ok, you can do exactly what you want here (have your Zephyr device communicate with an emulated Android device), with the right topology: [Zephyr app]<->[Zephyr host]<-{hci}->[TCP to gRPC bridge]<->[netsim controller]<-- (netsim internal link) -->[netsim controller]<->[Android host]<->[Android app]. The bridge here is used to allow the Zephyr host (BT stack of Zephyr) which can send/receive HCI packets over TCP, to reach the Netsim controller, which can send/receive packets over gRPC. The Bumble HCI bridge from TCP to gRPC here isn't a requirement, you could have the Zephyr host communicate with Netsim directly, but that would require the Zephyr app to implement all the gRPC logic, which Bumble already has. So, say that your Zephyr stack is configured to connect to 172.22.0.1:9000 over TCP, you would use the Bumble bridge as: python hci_bridge.py tcp-client:172.22.0.1:9000 android-netsim. The controllers app isn't needed here.

NOTE: in theory you could do the same but have everything go through the Bumble virtual link instead of Netsim (there are options to tell the Android emulator to connect its Host to something other than Netsim), but since Netsim is a much more capable virtual controller/link than Bumble's own limited virtual controller, I wouldn't recommend it. (The focus of Bumble is really on the bulk of the Host + profiles stack, the virtual controller is a limited component that's useful for unit testing and some simple configurations, but it isn't designed to support all use cases. There's a plan to eventually only use the Netsim/RootCanal controller going forward, at some point in the near future)

vChavezB commented 1 year ago

That clears most of my questions. However, i tried your suggestion

python hci_bridge.py tcp-client:172.22.0.1:9000 android-netsim

but now the zephyr application does not start, i.e. it does not connect to 172.22.0.1:9000.

Should it be started before the Zephyr application tries to connect to 172.22.0.1:9000 ?

The IP address I am using is the localhost that is proxied between windows and the Windows Subystem for Linux (WSL) as the Zephyr application is a native linux binary.

This is what the linux zephyr binary does to connect

https://github.com/zephyrproject-rtos/zephyr/blob/6f11257b886d85ee80fd84a2cc7543c51382801a/drivers/bluetooth/hci/userchan.c#L226-L235

barbibulle commented 1 year ago

Sorry, typo. The bridge should be a TCP server (for Zephyr to connect to): python hci_bridge.py tcp-server:_:9000 android-netsim. (_ stands for listening for local connections. If you need to listen on a specific network interface, use the IP addr of that interface instead).

vChavezB commented 1 year ago

Ah I see ok thanks for the info. Now I get the error

INFO:bumble.bridge:[HOST->CONTROLLER] HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND: 01
DEBUG:bumble.transport.tcp_server:connection end
DEBUG:bumble.transport.tcp_server:connection lost: None
WARNING:bumble.transport.common:exception while waiting for packet: <AioRpcError of RPC that terminated with:
        status = StatusCode.UNKNOWN
        details = "Stream removed"
        debug_error_string = "UNKNOWN:Error received from peer ipv6:%5B::1%5D:58215 {grpc_message:"Stream removed", grpc_status:2, created_time:"2023-07-24T07:46:54.409214084+00:00"}"

I think this is a similar error to the one from the bumble virtual controller. Do you know how I can override or fake a good reply for the command HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND from the android-netsim to the tcp-server ?

Update: I read the source from the hci-bridge.py file and found out you can add a filter to "shortcircuit" commands. To filter the command HCI_SET_CONTROLLER_TO_HOST_FLOW_CONTROL_COMMAND I am now running the bridge as follows

python hci_bridge.py tcp-server:_:9000 android-netsim 0x03:0x0031

where 0x03:0x0031 represents the op code for the Set Controller To Host Flow Control command.

vChavezB commented 1 year ago

Advertisment works! Still have to figure out to make the connection work.