1# Gabeldorsche Architecture 2 3[TOC] 4 5This document outlines some architectural considerations we've made when 6developing the Gabeldorsche (GD) Bluetooth stack. 7 8## Threading model 9 10First of all, the GD stack does not build on concepts of threads. Instead, it 11works with [`Handlers`](#handler). However, since GD ultimately runs on an OS, 12it still needs to interact with processes and threads before achieving the 13[`Handler`](#handler) abstraction. 14 15### Processes 16 17In general. three types of processes exist in the GD runtime environment: 18 19**Application processes** 20: include third-party apps, other system components such as audio and telecom 21 services that interact with the _Bluetooth stack process_ APIs defined 22 through various RPC/IPC methods such as Binder, Socket IPC, gRPC, DBUS. and 23 so on, using languages such as AIDL or Protobuf. For Android applications, 24 although APIs are defined in AIDL, some boiler plate code is wrapped in Java 25 libraries exposed through code in 26 [`frameworks/base/core/java/android/bluetooth`](https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/bluetooth/) 27 that is released to developers as 28 [Android SDK](https://developer.android.com/guide/topics/connectivity/bluetooth). 29 30**Hardware abstraction layer (HAL) processes** 31: one or many processes from the vendor partition, and hence is hardware 32 depenedent. They interact with the _Bluetooth stack process_ via a set of 33 hardware abstraction APIs defined through RPC/IPC methods such as Binder, 34 Socket IPC, DBUS, and so on, using languages such as HIDL. On Android, this 35 would be HAL processes that implement HIDL APIs such as 36 [IBluetoothHci](https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/master/bluetooth/1.1/IBluetoothHci.hal) 37 and 38 [IBluetoothAudioProvider](https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/master/bluetooth/audio/2.0/IBluetoothAudioProvider.hal). 39 40**Bluetooth stack process** 41: typically one single process that implements various Bluetooth protocols and 42 profiles above the Host Controller Interface (HCI) and below the Bluetooth 43 SDK APIs. On one hand, it servces the requests from _Application processes_; 44 on the other hand, it forwards these requests via interactions with _HAL 45 processes_. On Android, this process typically runs under AID_BLUETOOTH 46 (usually 1002) with process name "com.android.bluetooth". The process is 47 started in Java and loads native libraries through JNI. Other systems that 48 do not use Java virtual machine may have a pure native process. Multiple 49 threads may exist in this process for various reasons. The GD stack runs 50 entirely in this process. 51 52### Threads in Bluetooth stack process 53 54Currently, the goals of thread optimization in the Bluetooth stack are: 55 56* Reduce the number of threads as much as possible to simplify synchronization 57* Do blocking I/O operations in separate threads 58* Try moving I/O operations into polling mode so that we can use event driven 59 methods to interact with it on main thread 60* Move alarm and timer mechanisms to their calling threads to avoid a separate 61 alarm thread 62* Isolate individual components so that each component can be started and 63 stopped individually without terminating the main thread 64* Prefer data passing over data sharing among threads to reduce locking and 65 race conditions 66 67After above optimization, we are left with five main types of threads within the 68native code: 69 70**Main thread** 71: The main workhorse in the Bluetooth stack. The thread's execution context is 72 further divided into [`Handlers`](#handler) that reside in individual 73 [`Modules`](#module). This thread can be divided further into smaller ones 74 if performance is constrained on the running platform. Deployer just needs 75 to bind handlers to different threads and that should not affect the overall 76 operation. 77 78**JNI thread** 79: In the native thread, we treat the Java layer as a separate application as 80 its threading module is completely different. Therefore, we put a thread 81 between these two layers to buffer any blocking operation. 82 83**HCI thread (or other HW I/O thread)** 84: This thread is responsible for deadling with hardware I/O and can be 85 potentially blocking. Hence it has its separate thread to avoid blocking the 86 main thread. 87 88**Audio worker thread** 89: Responsible for audio encoding and decoding operations that require higher 90 precision execution timing. Such worker has its separate thread to avoid 91 being affected by the main thread. 92 93**Socket I/O thread** 94: Communicate with various applications that uses the 95 [`BluetootSocket`](https://developer.android.com/reference/android/bluetooth/BluetoothSocket) 96 interface. It has its sepearate thread due to potential I/O delay. 97 98### Data flow diagram 99 100Function invocations between different components are abstracted as control 101packets (function closure) passed through queues. Data flow between components 102are data packets sent through queues, signaled using [`Reactor`](#reactor). They 103will merge to the input queue for each component. We define three types of 104queues: 105 106**Non-blocking queue** 107: When users try to dequeue when it’s empty, or enqueue when it’s full, it 108 will return immediately. All queueing within a thread must be non-blocking, 109 because otherwise it will deadlock. 110 111**Blocking queue** 112: When users try to dequeue when it’s empty, or enqueue when it’s full, it 113 will block, until other thread makes the queue to be writable/readable. It 114 can be used as a flow control mechanism to avoid too many packets from user 115 thread. 116 117**Leaky queue** 118: Same as non-blocking queue, but it will flush when it’s full and user tries 119 to enqueue. This is useful for audio encoding. 120 121![Threading Model](./data_flow_diagram.png) 122 123## Building blocks 124 125### Module {#module} 126 127Code in GD is packed into C++ objects called 128[`Module`](https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/module.h). 129A module standardized the following aspects of GD code: 130 131* **Dependencies**: A module provides its own dependencies on other modules by 132 implementing `ListDependencies()` 133* **Life Cycle**: A module must implement `Start()` and `Stop()` life cycle 134 methods 135* **Threading Module**: The `Module` base class provides a `Handler` for code 136 execution context via `GetHandler()` 137* **Metrics**: A `Module` can dump its state information for dumpsys through 138 `DumpState()` 139 140See its definition at: https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/module.h 141 142### Handler {#handler} 143 144Similar to 145[`android.os.Handler`](https://developer.android.com/reference/android/os/Handler), 146[`bluetooth::os::Handler`](https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/os/handler.h) 147provides a sequential execution context while hiding the concept of thread from 148the executing code. 149 150By scoping execution context into smaller areas, `Handler` benefits development 151in the following ways: 152 153* Less need for locking due to sequential execution context 154* Smaller context leads to easier management of code flow 155* Separation from thread gives system deployer more freedom to tweak the 156 underlying thread allocation. For example, for real time OS without full 157 thread implementation, a `Handler` can be used to provide a near-thread 158 execution context 159 160Of course, there are downsides of using `Handler`, which developers should be 161cautious about: 162 163WARNING: Although multiple `Handler` could bind to the same thread, `Handler` 164does not gurantee sequential execution of code across different `Handler` even 165when the are on the same thread. 166 167WARNING: Locking among `Handlers` that were bound to the same thread may result 168in deadlock 169 170WARNING: Data must be copied between `Handler` to avoid both deadlock and race 171condition 172 173See its definition at: https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/os/handler.h 174 175### Reactor {#reactor} 176 177[`bluetooth::os:Reactor`](https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/os/reactor.h) 178implements the 179[Reactor Design Pattern](https://en.wikipedia.org/wiki/Reactor_pattern), in 180which concurrent _Events_ are demultiplexed by a _Synchronous Event 181Demultiplexer_ to a list of _Request Handlers_ registered through a 182_Dispatcher_. 183 184In a generic Linux operating system, such as Android, we implemented it using 185file descriptors such as 186[eventfd](http://man7.org/linux/man-pages/man2/eventfd.2.html) for `Handler`, 187[timerfd](http://man7.org/linux/man-pages/man2/timerfd_create.2.html) for 188`Alarm`, and [socketfd](http://man7.org/linux/man-pages/man2/socket.2.html) for 189data processing pipelines. In the context of file descriptors, events are 190catigorized into two types: 191 192* **OnReadReady**: means that the demultiplexer has some events for the 193 handler and the handler can read at least one event from the underlying 194 event queue. This is often associated with `EPOLLIN`, `EPOLLHUP`, 195 `EPOLLRDHUP`, and `EPOLLERR`. 196* **OnWriteReady**: means that the demultiplexer is ready to consume more 197 events from this handler, and the handler can write at least one event to 198 the underlying queue. this is often associated with `EPOLLOUT`. 199 200This pattern naturally creates a back pressure from one queue to another without 201any extra signaling mechanism. When used in networking stack like ours, it 202simplifies the signaling code flow. 203 204See its definition at: 205https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/os/reactor.h 206 207A pure data use case of `Reactor` is a `Reactive Queue`, see its definition at: 208https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/os/queue.h 209 210## Packet Definition Language (PDL) 211 212Packet parsing and serialization has been a big part of any networking stack. It 213is usually the first snippet of code that interface with a remote device. In the 214past, this has been achieved manually using macros like `STREAM_TO_UNIT8` or 215`UINT8_TO_STREAM`. This manual method is tedious and errorprone. To fix this, we 216created a Packet Definition Language that defines networking packet structure to 217the bits level. C++ headers and Python bindings will be automatically generated 218from its code generator and any fixes to the code generator will apply 219systematically to all packet code generated. 220 221Example PDL: 222 223``` 224// Comments 225little_endian_packets // Whether this packet is big or small endian 226 227// Include header from other C++ header files 228custom_field SixBytes : 48 "packet/parser/test/" // expect six_bytes.h 229custom_field Variable "packet/parser/test/" // expect variable.h 230 231// A packet 232packet Parent { 233 _fixed_ = 0x12 : 8, // fixed field 0x12 that takes 8 bits 234 _size_(_payload_) : 8, // Size field that takes 8 bits 235 _payload_, // special payload field of variable size 236 footer : 8, // fiexed size footer of 8 bits 237} 238 239packet Child : Parent { 240 field_name : 16, // addition field append after Parent 241} 242 243// an enum of 4 bits 244enum FourBits : 4 { 245 ONE = 1, 246 TWO = 2, 247 THREE = 3, 248 FIVE = 5, 249 TEN = 10, 250 LAZY_ME = 15, 251} 252``` 253 254See its documentation at: 255https://android.googlesource.com/platform/packages/modules/Bluetooth/system/+/master/gd/packet/parser/README 256 257## Calling convention between modules 258 259### Asynchronous server-client model 260 261For most communication among modules, developers should assume an asynchronous 262server-client model in a generic model like: 263 264```c++ 265// Define callback function type 266using CallbackFunction = std::function<void(ParamType)>; 267 268// Asynchronous method definition 269bool Foo(Parameter param, CallbackFunction callback); 270 271// A new callback is passed for each asynchronous call 272// Always prefer lambda over std::bind 273CallbackFunction callback = [this] { 274 // do something 275}; 276Parameter param = { 277 // something 278}; 279if (Foo(param, callback)) { 280 // The callback will be invoked 281 // Callback must be invoked in the future 282} else { 283 // Failed, no need to wait 284} 285``` 286 287Many protocols and profiles fit into such model such as `AclManager` and 288`L2cap`. 289 290### Synchronous database model 291 292In some cases, an asynchronous server-client model is not feasible. In this 293case, developers can consider a synchronous database model. In such a model, 294operations can happen synchronously with help of mutex. When the method returns, 295the changes must be reflected to all dependencies. Any changes in the internal 296states must be applied atomically. 297 298```c++ 299// Synchronous method definition 300void Foo(Parameter param, Output* output); 301int Bar(Parameter param); 302Parameter param = { 303 // something 304}; 305Output output = {}; 306Foo(param, &output); 307// output can be used immediately 308int bar_output = Bar(param); 309// bar_output can be used immediately 310``` 311 312Many storage and informational modules fit into this model such as `Metrics` and 313`Storage`. 314