forked from auracaster/bumble_mirror
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 01492d510c | |||
| 302c495178 | |||
| fc7923f83b | |||
| ce0cf5fd27 | |||
| 86ded3fece |
+1
-1
@@ -20,7 +20,7 @@ import click
|
||||
from colors import color
|
||||
|
||||
from bumble import hci
|
||||
from bumble.transport import PacketReader
|
||||
from bumble.transport.common import PacketReader
|
||||
from bumble.helpers import PacketTracer
|
||||
|
||||
|
||||
|
||||
+33
-15
@@ -243,7 +243,7 @@ class L2CAP_Control_Frame:
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@L2CAP_Control_Frame.subclass([
|
||||
('reason', {'size': 2, 'mapper': lambda x: L2CAP_Command_Reject.map_reason(x)}),
|
||||
('reason', {'size': 2, 'mapper': lambda x: L2CAP_Command_Reject.reason_name(x)}),
|
||||
('data', '*')
|
||||
])
|
||||
class L2CAP_Command_Reject(L2CAP_Control_Frame):
|
||||
@@ -262,7 +262,7 @@ class L2CAP_Command_Reject(L2CAP_Control_Frame):
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def map_reason(reason):
|
||||
def reason_name(reason):
|
||||
return name_or_number(L2CAP_Command_Reject.REASON_NAMES, reason)
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ class L2CAP_Configure_Request(L2CAP_Control_Frame):
|
||||
@L2CAP_Control_Frame.subclass([
|
||||
('source_cid', 2),
|
||||
('flags', 2),
|
||||
('result', {'size': 2, 'mapper': lambda x: L2CAP_Configure_Response.map_result(x)}),
|
||||
('result', {'size': 2, 'mapper': lambda x: L2CAP_Configure_Response.result_name(x)}),
|
||||
('options', '*')
|
||||
])
|
||||
class L2CAP_Configure_Response(L2CAP_Control_Frame):
|
||||
@@ -355,7 +355,7 @@ class L2CAP_Configure_Response(L2CAP_Control_Frame):
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def map_result(result):
|
||||
def result_name(result):
|
||||
return name_or_number(L2CAP_Configure_Response.RESULT_NAMES, result)
|
||||
|
||||
|
||||
@@ -403,31 +403,49 @@ class L2CAP_Echo_Response(L2CAP_Control_Frame):
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@L2CAP_Control_Frame.subclass([
|
||||
('info_type', 2)
|
||||
('info_type', {'size': 2, 'mapper': lambda x: L2CAP_Information_Request.info_type_name(x)})
|
||||
])
|
||||
class L2CAP_Information_Request(L2CAP_Control_Frame):
|
||||
'''
|
||||
See Bluetooth spec @ Vol 3, Part A - 4.10 INFORMATION REQUEST
|
||||
'''
|
||||
|
||||
SUCCESS = 0x00
|
||||
NOT_SUPPORTED = 0x01
|
||||
|
||||
CONNECTIONLESS_MTU = 0x0001
|
||||
EXTENDED_FEATURES_SUPPORTED = 0x0002
|
||||
FIXED_CHANNELS_SUPPORTED = 0x0003
|
||||
|
||||
INFO_TYPE_NAMES = {
|
||||
CONNECTIONLESS_MTU: 'CONNECTIONLESS_MTU',
|
||||
EXTENDED_FEATURES_SUPPORTED: 'EXTENDED_FEATURES_SUPPORTED',
|
||||
FIXED_CHANNELS_SUPPORTED: 'FIXED_CHANNELS_SUPPORTED'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def info_type_name(info_type):
|
||||
return name_or_number(L2CAP_Information_Request.INFO_TYPE_NAMES, info_type)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@L2CAP_Control_Frame.subclass([
|
||||
('info_type', 2),
|
||||
('result', 2),
|
||||
('info_type', {'size': 2, 'mapper': L2CAP_Information_Request.info_type_name}),
|
||||
('result', {'size': 2, 'mapper': lambda x: L2CAP_Information_Response.result_name(x)}),
|
||||
('data', '*')
|
||||
])
|
||||
class L2CAP_Information_Response(L2CAP_Control_Frame):
|
||||
'''
|
||||
See Bluetooth spec @ Vol 3, Part A - 4.11 INFORMATION RESPONSE
|
||||
'''
|
||||
SUCCESS = 0x00
|
||||
NOT_SUPPORTED = 0x01
|
||||
|
||||
RESULT_NAMES = {
|
||||
SUCCESS: 'SUCCESS',
|
||||
NOT_SUPPORTED: 'NOT_SUPPORTED'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def result_name(result):
|
||||
return name_or_number(L2CAP_Information_Response.RESULT_NAMES, result)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -473,7 +491,7 @@ class L2CAP_LE_Credit_Based_Connection_Request(L2CAP_Control_Frame):
|
||||
('mtu', 2),
|
||||
('mps', 2),
|
||||
('initial_credits', 2),
|
||||
('result', {'size': 2, 'mapper': lambda x: L2CAP_LE_Credit_Based_Connection_Response.map_result(x)})
|
||||
('result', {'size': 2, 'mapper': lambda x: L2CAP_LE_Credit_Based_Connection_Response.result_name(x)})
|
||||
])
|
||||
class L2CAP_LE_Credit_Based_Connection_Response(L2CAP_Control_Frame):
|
||||
'''
|
||||
@@ -505,7 +523,7 @@ class L2CAP_LE_Credit_Based_Connection_Response(L2CAP_Control_Frame):
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def map_result(result):
|
||||
def result_name(result):
|
||||
return name_or_number(L2CAP_LE_Credit_Based_Connection_Response.CONNECTION_RESULT_NAMES, result)
|
||||
|
||||
|
||||
@@ -980,13 +998,13 @@ class ChannelManager:
|
||||
|
||||
def on_l2cap_information_request(self, connection, cid, request):
|
||||
if request.info_type == L2CAP_Information_Request.CONNECTIONLESS_MTU:
|
||||
result = L2CAP_Information_Request.SUCCESS
|
||||
result = L2CAP_Information_Response.SUCCESS
|
||||
data = struct.pack('<H', 1024) # TODO: don't use a fixed value
|
||||
elif request.info_type == L2CAP_Information_Request.EXTENDED_FEATURES_SUPPORTED:
|
||||
result = L2CAP_Information_Request.SUCCESS
|
||||
result = L2CAP_Information_Response.SUCCESS
|
||||
data = bytes.fromhex('00000000') # TODO: don't use a fixed value
|
||||
elif request.info_type == L2CAP_Information_Request.FIXED_CHANNELS_SUPPORTED:
|
||||
result = L2CAP_Information_Request.SUCCESS
|
||||
result = L2CAP_Information_Response.SUCCESS
|
||||
data = bytes.fromhex('FFFFFFFFFFFFFFFF') # TODO: don't use a fixed value
|
||||
else:
|
||||
result = L2CAP_Information_Request.NO_SUPPORTED
|
||||
|
||||
+29
-16
@@ -37,14 +37,17 @@ async def open_usb_transport(spec):
|
||||
'''
|
||||
Open a USB transport.
|
||||
The parameter string has this syntax:
|
||||
either <index> or <vendor>:<product>
|
||||
either <index> or <vendor>:<product>[/<serial-number>]
|
||||
With <index> as the 0-based index to select amongst all the devices that appear
|
||||
to be supporting Bluetooth HCI (0 being the first one), or
|
||||
Where <vendor> and <product> are the vendor ID and product ID in hexadecimal.
|
||||
Where <vendor> and <product> are the vendor ID and product ID in hexadecimal. The
|
||||
/<serial-number> suffix max be specified when more than one device with the same
|
||||
vendor and product identifiers are present.
|
||||
|
||||
Examples:
|
||||
0 --> the first BT USB dongle
|
||||
04b4:f901 --> the BT USB dongle with vendor=04b4 and product=f901
|
||||
04b4:f901/00E04C239987 --> the BT USB dongle with vendor=04b4 and product=f901 and serial number 00E04C239987
|
||||
'''
|
||||
|
||||
USB_RECIPIENT_DEVICE = 0x00
|
||||
@@ -268,22 +271,32 @@ async def open_usb_transport(spec):
|
||||
found = None
|
||||
if ':' in spec:
|
||||
vendor_id, product_id = spec.split(':')
|
||||
found = context.getByVendorIDAndProductID(int(vendor_id, 16), int(product_id, 16), skip_on_error=True)
|
||||
if '/' in product_id:
|
||||
product_id, serial_number = product_id.split('/')
|
||||
for device in context.getDeviceIterator(skip_on_error=True):
|
||||
if (
|
||||
device.getVendorID() == int(vendor_id, 16) and
|
||||
device.getProductID() == int(product_id, 16) and
|
||||
device.getSerialNumber() == serial_number
|
||||
):
|
||||
found = device
|
||||
break
|
||||
device.close()
|
||||
else:
|
||||
found = context.getByVendorIDAndProductID(int(vendor_id, 16), int(product_id, 16), skip_on_error=True)
|
||||
else:
|
||||
device_index = int(spec)
|
||||
device_iterator = context.getDeviceIterator(skip_on_error=True)
|
||||
try:
|
||||
for device in device_iterator:
|
||||
if device.getDeviceClass() == USB_DEVICE_CLASS_WIRELESS_CONTROLLER and \
|
||||
device.getDeviceSubClass() == USB_DEVICE_SUBCLASS_RF_CONTROLLER and \
|
||||
device.getDeviceProtocol() == USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER:
|
||||
if device_index == 0:
|
||||
found = device
|
||||
break
|
||||
device_index -= 1
|
||||
device.close()
|
||||
finally:
|
||||
device_iterator.close()
|
||||
for device in context.getDeviceIterator(skip_on_error=True):
|
||||
if (
|
||||
device.getDeviceClass() == USB_DEVICE_CLASS_WIRELESS_CONTROLLER and
|
||||
device.getDeviceSubClass() == USB_DEVICE_SUBCLASS_RF_CONTROLLER and
|
||||
device.getDeviceProtocol() == USB_DEVICE_PROTOCOL_BLUETOOTH_PRIMARY_CONTROLLER
|
||||
):
|
||||
if device_index == 0:
|
||||
found = device
|
||||
break
|
||||
device_index -= 1
|
||||
device.close()
|
||||
|
||||
if found is None:
|
||||
context.close()
|
||||
|
||||
@@ -5,6 +5,8 @@ use_directory_urls: false
|
||||
nav:
|
||||
- Introduction: index.md
|
||||
- Getting Started: getting_started.md
|
||||
- Development:
|
||||
- Python Environments: development/python_environments.md
|
||||
- Use Cases:
|
||||
- Overview: use_cases/index.md
|
||||
- Use Case 1: use_cases/use_case_1.md
|
||||
@@ -63,7 +65,10 @@ theme:
|
||||
custom_dir: 'theme'
|
||||
|
||||
plugins:
|
||||
- mkdocstrings
|
||||
- mkdocstrings:
|
||||
handlers:
|
||||
python:
|
||||
paths: [../..]
|
||||
|
||||
docs_dir: 'src'
|
||||
|
||||
|
||||
@@ -7,6 +7,6 @@ These include:
|
||||
* [Console](console.md) - an interactive text-based console
|
||||
* [HCI Bridge](hci_bridge.md) - a HCI transport bridge to connect two HCI transports and filter/snoop the HCI packets
|
||||
* [Golden Gate Bridge](gg_bridge.md) - a bridge between GATT and UDP to use with the Golden Gate "stack tool"
|
||||
* [`Show`](show.md) - Parse a file with HCI packets and print the details of each packet in a human readable form
|
||||
* [`Link Relay`](link_relay.md) - WebSocket relay for virtual RemoteLink instances to communicate with each other.
|
||||
* [Show](show.md) - Parse a file with HCI packets and print the details of each packet in a human readable form
|
||||
* [Link Relay](link_relay.md) - WebSocket relay for virtual RemoteLink instances to communicate with each other.
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
PYTHON ENVIRONMENTS
|
||||
===================
|
||||
|
||||
When you don't want to install Bumble in your main/default python environment,
|
||||
using a virtual environment, where the package and its dependencies can be
|
||||
installed, isolated from the rest, may be useful.
|
||||
|
||||
There are many flavors of python environments and dependency managers.
|
||||
This page describes a few of the most common ones.
|
||||
|
||||
|
||||
## venv
|
||||
|
||||
`venv` is a standard module that is included with python.
|
||||
Visit the [`venv` documentation](https://docs.python.org/3/library/venv.html) page for details.
|
||||
|
||||
## Pyenv
|
||||
|
||||
`pyenv` lets you easily switch between multiple versions of Python. It's simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well.
|
||||
Visit the [`pyenv` site](https://github.com/pyenv/pyenv) for instructions on how to install
|
||||
and use `pyenv`
|
||||
|
||||
## Conda
|
||||
|
||||
Conda is a convenient package manager and virtual environment.
|
||||
The file `environment.yml` is a Conda environment file that you can use to create
|
||||
a new Conda environment. Once created, you can simply activate this environment when
|
||||
working with Bumble.
|
||||
Visit the [Conda site](https://docs.conda.io/en/latest/) for instructions on how to install
|
||||
and use Conda.
|
||||
A few useful commands:
|
||||
|
||||
### Create a new `bumble` Conda environment
|
||||
```
|
||||
$ conda env create -f environment.yml
|
||||
```
|
||||
This will create a new environment, named `bumble`, which you can then activate with:
|
||||
```
|
||||
$ conda activate bumble
|
||||
```
|
||||
|
||||
### Update an existing `bumble` environment
|
||||
```
|
||||
$ conda env update -f environment.yml
|
||||
```
|
||||
@@ -20,7 +20,7 @@ You may be simply using Bumble as a module for your own application or as a depe
|
||||
module, or you may be working on modifying or contributing to the Bumble module or example code
|
||||
itself.
|
||||
|
||||
# Working With Bumble As A Module
|
||||
# Using Bumble As A Python Module
|
||||
|
||||
## Installing
|
||||
|
||||
@@ -29,52 +29,40 @@ manager, or from source.
|
||||
|
||||
!!! tip "Python Virtual Environments"
|
||||
When you install Bumble, you have the option to install it as part of your default
|
||||
python environment, or in a virtual environment, such as a `venv`, `pyenv` or `conda` environment
|
||||
|
||||
### venv
|
||||
|
||||
`venv` is a standard module that is included with python.
|
||||
Visit the [`venv` documentation](https://docs.python.org/3/library/venv.html) page for details.
|
||||
|
||||
### Pyenv
|
||||
|
||||
`pyenv` lets you easily switch between multiple versions of Python. It's simple, unobtrusive, and follows the UNIX tradition of single-purpose tools that do one thing well.
|
||||
Visit the [`pyenv` site](https://github.com/pyenv/pyenv) for instructions on how to install
|
||||
and use `pyenv`
|
||||
|
||||
### Conda
|
||||
|
||||
Conda is a convenient package manager and virtual environment.
|
||||
The file `environment.yml` is a Conda environment file that you can use to create
|
||||
a new Conda environment. Once created, you can simply activate this environment when
|
||||
working with Bumble.
|
||||
Visit the [Conda side](https://docs.conda.io/en/latest/) for instructions on how to install
|
||||
and use Conda.
|
||||
A few useful commands:
|
||||
|
||||
#### Create a new `bumble` Conda environment
|
||||
```
|
||||
$ conda env create -f environment.yml
|
||||
```
|
||||
This will create a new environment, named `bumble`, which you can then activate with:
|
||||
```
|
||||
$ conda activate bumble
|
||||
```
|
||||
|
||||
#### Update an existing `bumble` environment
|
||||
```
|
||||
$ conda env update -f environment.yml
|
||||
```
|
||||
python environment, or in a virtual environment, such as a `venv`, `pyenv` or `conda` environment.
|
||||
See the [Python Environments page](development/python_environments.md) page for details.
|
||||
|
||||
### Install From Source
|
||||
|
||||
The instructions for working with virtual Python environments above also apply in this case.
|
||||
|
||||
Install with `pip`
|
||||
Install with `pip`. Run in a command shell in the directory where you downloaded the source
|
||||
distribution
|
||||
```
|
||||
$ python -m pip install -e .
|
||||
```
|
||||
|
||||
### Install from GitHub
|
||||
|
||||
You can install directly from GitHub without first downloading the repo.
|
||||
|
||||
Install the latest commit from the main branch with `pip`:
|
||||
```
|
||||
$ python -m pip install git+https://github.com/google/bumble.git
|
||||
```
|
||||
|
||||
You can specify a specific tag.
|
||||
|
||||
Install tag `v0.0.1` with `pip`:
|
||||
```
|
||||
$ python -m pip install git+https://github.com/google/bumble.git@v0.0.1
|
||||
```
|
||||
|
||||
You can also specify a specific commit.
|
||||
|
||||
Install commit `27c0551` with `pip`:
|
||||
```
|
||||
$ python -m pip install git+https://github.com/google/bumble.git@27c0551
|
||||
```
|
||||
|
||||
# Working On The Bumble Code
|
||||
When you work on the Bumble code itself, and run some of the tests or example apps, or import the
|
||||
module in your own code, you typically either install the package from source in "development mode" as described above, or you may choose to skip the install phase.
|
||||
@@ -106,3 +94,8 @@ Setting `PYTHONPATH` locally with each command would look something like:
|
||||
```
|
||||
$ PYTHONPATH=. python examples/run_advertiser.py examples/device1.json serial:/dev/tty.usbmodem0006839912171
|
||||
```
|
||||
|
||||
# Where To Go Next
|
||||
Once you've installed or downloaded Bumble, you can either start using some of the
|
||||
[Bundled apps and tools](apps_and_tools/index.md), or look at the [examples](examples/index.md)
|
||||
to get a feel for how to use the APIs, and start writing your own applications.
|
||||
@@ -46,7 +46,7 @@ through it with Android applications as well as system-managed profiles.
|
||||
To connect a Bumble host stack to a Root Canal virtual controller instance, use
|
||||
the bumble `android-emulator` transport in `host` mode (the default).
|
||||
|
||||
!!! example "Running the example GATT server connected to the emulator"
|
||||
!!! example "Run the example GATT server connected to the emulator"
|
||||
``` shell
|
||||
$ python run_gatt_server.py device1.json android-emulator
|
||||
```
|
||||
@@ -57,7 +57,7 @@ This is an advanced use case, which may not be officially supported, but should
|
||||
versions of the emulator.
|
||||
You will likely need to start the emulator from the command line, in order to specify the `-forward-vhci` option (unless the emulator offers a way to control that feature from a user/ui menu).
|
||||
|
||||
!!! example "Launching the emulator with VHCI forwarding"
|
||||
!!! example "Launch the emulator with VHCI forwarding"
|
||||
In this example, we launch an emulator AVD named "Tiramisu"
|
||||
```shell
|
||||
$ emulator -forward-vhci -avd Tiramisu
|
||||
@@ -70,10 +70,20 @@ You will likely need to start the emulator from the command line, in order to sp
|
||||
|
||||
To connect a virtual controller to the Android Bluetooth stack, use the bumble `android-emulator` transport in `controller` mode. For example, using the default gRPC port, the transport name would be: `android-emulator:mode=controller`.
|
||||
|
||||
!!! example "connecting the Android emulator to the first USB Bluetooth dongle, using the `hci_bridge` application"
|
||||
!!! example "Connect the Android emulator to the first USB Bluetooth dongle, using the `hci_bridge` application"
|
||||
```shell
|
||||
$ bumble-hci-bridge android-emulator:mode=controller usb:0
|
||||
```
|
||||
|
||||
## Other Tools
|
||||
|
||||
The `show` application that's included with Bumble can be used to parse and pretty-print the HCI packets
|
||||
from an Android HCI "snoop log" (see [this page](https://source.android.com/devices/bluetooth/verifying_debugging)
|
||||
for details on how to obtain HCI snoop logs from an Android device).
|
||||
Use the `--format snoop` option to specify that the file is in that specific format.
|
||||
|
||||
!!! example "Analyze an Android HCI snoop log file"
|
||||
```shell
|
||||
$ bumble-show --format snoop btsnoop_hci.log
|
||||
```
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@ packages = bumble, bumble.transport, bumble.apps, bumble.apps.link_relay
|
||||
package_dir =
|
||||
bumble = bumble
|
||||
bumble.apps = apps
|
||||
bumble.apps.link_relay = apps/link_relay
|
||||
install_requires =
|
||||
aioconsole >= 0.4.1
|
||||
ansicolors >= 1.1
|
||||
@@ -65,4 +64,4 @@ development =
|
||||
documentation =
|
||||
mkdocs >= 1.2.3
|
||||
mkdocs-material >= 8.1.9
|
||||
mkdocstrings >= 0.17.0
|
||||
mkdocstrings[python] >= 0.19.0
|
||||
|
||||
Reference in New Issue
Block a user