- Architecture overview, BLE and WebSocket protocols - All 19 modules with command IDs validated against smali source - BLE packet format with CRC-16/MODBUS pseudocode - Complete WsCmd index (~300 commands) - Connection flow with pseudocode examples - Methodology section explaining APK decompilation process
209 lines
7.1 KiB
Markdown
209 lines
7.1 KiB
Markdown
# Connection Flow
|
|
|
|
## Complete Connection Sequence
|
|
|
|
```
|
|
Step 1: BLE Discovery Step 4: WebSocket Step 6: Commands
|
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
│ SCAN for │ │ Connect to │ │ Send WsPacket│
|
|
│ "DWARF*" │ │ ws://IP:9900 │ │ (moduleId+cmd)│
|
|
│ devices │ │ ?client_id= │ │ │
|
|
│ via BleManager│ │ <clientId> │ │ Receive │
|
|
└──────┬───────┘ └──────┬───────┘ │ WsPacket │
|
|
│ │ │ (response/ │
|
|
Step 2: GATT Connect Step 5: Keepalive │ notification)│
|
|
┌──────────────┐ ┌──────────────┐ └──────────────┘
|
|
│ Connect to │ │ DwarfPing → │
|
|
│ Service DAF2 │ │ ← DwarfEcho │
|
|
│ (UUID DAF2) │ │ (periodic) │
|
|
└──────┬───────┘ └──────────────┘
|
|
│
|
|
Step 3: Auth
|
|
┌──────────────┐
|
|
│ Write │
|
|
│ ReqGetconfig │
|
|
│ on DAF3 │
|
|
│ → Receive │
|
|
│ ResGetconfig │
|
|
│ on DAF4 │
|
|
└──────────────┘
|
|
```
|
|
|
|
## Step 1-3: BLE Phase
|
|
|
|
### Pseudocode: BLE Authentication
|
|
|
|
```python
|
|
# 1. Scan for DWARF devices
|
|
devices = ble_scan(filter_name="DWARF*")
|
|
|
|
# 2. Connect to GATT service
|
|
gatt = ble_connect(device, service_uuid="0000DAF2-0000-1000-8000-00805f9b34fb")
|
|
|
|
# 3. Authenticate
|
|
auth_request = BleProto.ReqGetconfig(
|
|
cmd=1, # GetWiFiConfig
|
|
blePsd="DWARF_12345678", # default password
|
|
clientId="my-app-client-id"
|
|
)
|
|
ble_packet = build_ble_packet(cmd=1, data=auth_request.SerializeToString())
|
|
ble_write(characteristic=DAF3, data=ble_packet)
|
|
|
|
# 4. Receive response
|
|
response_data = ble_notify(characteristic=DAF4)
|
|
response_packet = parse_ble_packet(response_data)
|
|
config = BleProto.ResGetconfig()
|
|
config.ParseFromString(response_packet.dataPayload)
|
|
|
|
device_ip = config.ip # e.g., "192.168.88.1"
|
|
```
|
|
|
|
### Pseudocode: WiFi Configuration (STA mode)
|
|
|
|
```python
|
|
# Configure device to connect to local WiFi
|
|
sta_request = BleProto.ReqSta(
|
|
cmd=3, # SetSTAMode
|
|
autoStart=1,
|
|
blePsd="DWARF_12345678",
|
|
ssid="MyHomeWiFi",
|
|
psd="home_password",
|
|
clientId="my-app-client-id"
|
|
)
|
|
ble_packet = build_ble_packet(cmd=3, data=sta_request.SerializeToString())
|
|
ble_write(characteristic=DAF5, data=ble_packet)
|
|
|
|
# Receive STA response
|
|
sta_data = ble_notify(characteristic=DAF6)
|
|
sta_packet = parse_ble_packet(sta_data)
|
|
sta_response = BleProto.ResSta()
|
|
sta_response.ParseFromString(sta_packet.dataPayload)
|
|
device_ip = sta_response.ip # IP for WebSocket
|
|
```
|
|
|
|
## Step 4: WebSocket Phase
|
|
|
|
### Pseudocode: WebSocket Connection
|
|
|
|
```python
|
|
import websocket
|
|
import protobuf
|
|
|
|
# Connect to device WebSocket
|
|
ws = websocket.connect(f"ws://{device_ip}:9900/?client_id={client_id}")
|
|
|
|
# Start keepalive thread
|
|
def keepalive():
|
|
while connected:
|
|
ping = BleProto.DwarfPing(
|
|
vocaltype=1, # VT_PING
|
|
timestamp=current_time_ms(),
|
|
magic=session_magic_bytes
|
|
)
|
|
packet = WsPacket(
|
|
majorVersion=2,
|
|
minorVersion=3,
|
|
deviceId=device_id,
|
|
moduleId=0, # NONE
|
|
cmd=0,
|
|
type=0, # request
|
|
data=ping.SerializeToString(),
|
|
clientId=client_id
|
|
)
|
|
ws.send(packet.SerializeToString())
|
|
sleep(5) # 5-second interval
|
|
```
|
|
|
|
### Pseudocode: Send Command
|
|
|
|
```python
|
|
def send_command(ws, device_id, module_id, cmd, payload_bytes):
|
|
"""Send a command via WebSocket."""
|
|
packet = WsPacket(
|
|
majorVersion=2,
|
|
minorVersion=3,
|
|
deviceId=device_id,
|
|
moduleId=module_id,
|
|
cmd=cmd,
|
|
type=0, # request
|
|
data=payload_bytes,
|
|
clientId=client_id
|
|
)
|
|
ws.send(packet.SerializeToString())
|
|
|
|
# Example: Start tracking
|
|
track_req = TrackProto.ReqStartTrack(x=500, y=300, w=100, h=100, camId=0)
|
|
send_command(ws, device_id=4, module_id=7, cmd=14800,
|
|
payload_bytes=track_req.SerializeToString())
|
|
|
|
# Example: Open tele camera
|
|
camera_req = CameraProto.ReqOpenCamera(binning=False, rtspEncodeType=0)
|
|
send_command(ws, device_id=4, module_id=1, cmd=10000,
|
|
payload_bytes=camera_req.SerializeToString())
|
|
```
|
|
|
|
## BLE Packet Binary Format
|
|
|
|
```
|
|
Byte Field Size Description
|
|
---- ----------------- ---- -----------
|
|
0 magic 1 0xAA (constant)
|
|
1 protocolVersion 1 Protocol version (typically 1)
|
|
2 cmdInstruction 1 BLE command (0-7)
|
|
3 packageSequence 1 Current packet number (0-based)
|
|
4 totalPackages 1 Total packets for this message
|
|
5-6 extendedProtocol 2 Extended protocol (big-endian, usually 0)
|
|
7-8 validDataLength 2 Length of protobuf payload (big-endian)
|
|
9..N dataPayload N Protobuf-encoded BleProto message
|
|
N+1..N+2 crc 2 CRC-16/MODBUS (poly=0x8005, init=0xFFFF, reflected)
|
|
N+3 endMarker 1 0x0D (constant)
|
|
|
|
Total = validDataLength + 12 bytes
|
|
```
|
|
|
|
### CRC-16/MODBUS Parameters
|
|
|
|
- Polynomial: 0x8005 (reflected: 0xA001)
|
|
- Initial value: 0xFFFF
|
|
- Reflect input: Yes
|
|
- Reflect output: Yes
|
|
- XOR out: 0x0000
|
|
- Computed over: header (9 bytes) + payload (N bytes), **excluding** CRC and end marker
|
|
|
|
## WsPacket Binary Format
|
|
|
|
```
|
|
Field Proto# Type Description
|
|
-------------- ------ ---- -----------
|
|
majorVersion 1 int32 Always 2
|
|
minorVersion 2 int32 Always 3
|
|
deviceId 3 int32 Target device (0-6, see DeviceType)
|
|
moduleId 4 int32 Module (0-18, see module table)
|
|
cmd 5 int32 Command ID (see WsCmd index)
|
|
type 6 int32 0=request, 1=response, 2=notification, 3=reply
|
|
data 7 bytes Protobuf-encoded module-specific payload
|
|
clientId 8 string App client identifier
|
|
```
|
|
|
|
Serialized using standard protobuf proto3 encoding.
|
|
|
|
## Device Models
|
|
|
|
| Model | deviceId | Codename | Max Name | Special |
|
|
|-------|----------|----------|----------|---------|
|
|
| Unknown | 0 | — | — | — |
|
|
| DWARF 2 | 1 | — | 21 | — |
|
|
| DWARF 3 | 2 | — | 19 | — |
|
|
| *(gap)* | 3 | — | — | *unused* |
|
|
| DWARF mini | 4 | Bilbo | 15 | No Panorama Create, `indexBilbo.html` |
|
|
| DWARF 4 | 5 | — | 19 | — |
|
|
| DWARF DRAGON | 6 | — | — | — |
|
|
|
|
## Ports & Endpoints
|
|
|
|
| Service | URL/Port | Note |
|
|
|---------|----------|------|
|
|
| WebSocket | `ws://<ip>:9900/?client_id=<id>` | Primary control channel |
|
|
| WebSocket (alt) | Ports 4409 (0x1139), 4410 (0x113A) | Secondary ports |
|
|
| Firebase | `dwarflab-a7062.appspot.com` | Cloud backend |
|
|
| S3 | `dwarf-bucket-02-1251529954` | OTA storage | |