- 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
5.6 KiB
Methodology & Sources
How This Documentation Was Obtained
All information in this documentation was extracted through static analysis of the decompiled DWARFLAB Android APK (v3.4.0, build 629, package com.convergence.dwarflab). No device was needed — the entire protocol is defined in the app's code.
Decompilation
The APK was decompiled using apktool v3.4.0, which produced:
AndroidManifest.xml— permissions, activities, servicessmali/throughsmali_classes7/— 7 DEX files of dalvik bytecodelib/— native.solibraries (arm64, armeabi-v7a, x86_64)assets/— voice JSON, country JSON, keystoresres/— layouts, drawables, Lottie animations
Key Smali Files Mapped to Protocol Concepts
| Protocol Concept | Smali File | Location |
|---|---|---|
| BLE UUIDs | cg1.smali |
smali_classes4/ |
| BLE commands | j80.smali |
smali_classes4/ |
| BLE packet format | l80.smali |
smali_classes4/ |
| BLE CRC-16 | vc0.smali |
smali_classes4/ |
| WebSocket URL | v55.smali |
smali_classes4/ |
| WsPacket fields | BaseProto$WsPacket.smali |
smali_classes5/ |
| WsModuleId | WsModuleId.smali |
smali_classes4/ |
| WsCmd (all commands) | WsCmd.smali |
smali_classes4/ |
| WsMessageType | WsMessageType.smali |
smali_classes4/ |
| WsRespCode | WsRespCode.smali |
smali_classes4/ |
| DeviceType | DeviceType.smali |
smali_classes4/ |
| BluetoothPacketSender | BluetoothPacketSender.smali |
smali_classes4/ |
| ConnectionManager | ConnectionManager.smali |
smali_classes4/ |
| Proto messages | AstroProto*.smali, CameraProto*.smali, etc. |
smali_classes5/ |
Extraction Methods
1. UUID Discovery (cg1.smali)
The cg1 class contains 8 static UUID strings. Read directly from the <clinit> method — the values are string constants embedded in the bytecode.
2. Command IDs (WsCmd.smali)
The WsCmd enum contains ~300 entries. Each entry is initialized in <clinit> via <init>(String name, int ordinal, int cmd). The third parameter (cmd) is the numeric command ID. Extracted by tracing register values through the constructor calls.
Important: Kotlin enum ordinals and explicit cmd IDs are different numbers. The cmd value is what goes on the wire.
3. Module IDs (WsModuleId.smali)
The WsModuleId enum uses ordinal values only (no custom id field). Module ID = ordinal position in the enum (0-18).
4. BLE Packet Format (l80.smali)
The l80 class (obfuscated name for BluetoothPacket) encodes packets in method j():
- Magic byte
0xAAat position 0 - 9-byte header: protocolVersion, cmdInstruction, packageSequence, totalPackages, extendedProtocol (2B), validDataLength (2B)
- Payload: protobuf data
- CRC-16 computed by class
vc0over header+payload - End marker
0x0D
The CRC lookup table in vc0.smali starts with {0x0000, 0xC0C1, 0xC181, 0x0140, ...} which identifies it as CRC-16/MODBUS (reflected polynomial 0xA001 = 0x8005 reflected, init 0xFFFF).
5. WebSocket URL (v55.smali)
The URL is assembled by concatenating: "ws://" + yd3.b() (host IP) + ":9900/?client_id=" + y32.c() (client ID). Two additional constants: 0x1139 (4409) and 0x113A (4410) for secondary ports.
6. DeviceType IDs (DeviceType.smali)
The constructor signature is <init>(String name, int ordinal, int deviceId, int nameResId). The deviceId parameter is the on-wire device identifier. Careful register tracing is required because smali reuses registers:
- Unknown: deviceId=0
- DWARF_2: deviceId=1
- DWARF_3: deviceId=2
- DWARF_mini: deviceId=4 (ordinal=3, gap at deviceId=3)
- DWARF_4: deviceId=5
- DWARF_DRAGON: deviceId=6
7. Proto Message Fields (smali_classes5/com/convergence/dwarflab/proto/)
Each .smali file in the proto package corresponds to a protobuf message. Field numbers are embedded as constants in the writeTo() method (serialized using writeString(fieldNumber, value), writeInt32(fieldNumber, value), etc.). Field names are preserved in the obfuscated class names via Kotlin metadata annotations.
Limitations
- Obfuscated names: Most class names are obfuscated (e.g.,
l80,cg1,j80). Kotlin metadata annotations help recover some names. - No
.protofiles: The original.protosource files are not included in the APK. Field names and numbers are inferred from the generated Java/Kotlin code. - String literals: Some strings (UUIDs, URLs, keys) are embedded directly and can be extracted with certainty. Others require contextual inference.
- Behavioral details: Timing, retry logic, and UI flow are inferred from the code but not directly observable without a device.
- Version-specific: This documentation corresponds to APK v3.4.0. Newer or older versions may differ.
Verification
All data points were cross-validated in two passes:
- First pass: Initial extraction from smali source code
- Second pass: Independent re-reading of each smali file to confirm register values, field numbers, and constants
Key verifications:
- All 8 BLE UUIDs confirmed from
cg1.smali - All 19 module IDs confirmed from
WsModuleId.smali(ordinals) - All ~300 WsCmd command IDs confirmed from
WsCmd.smali(hex → decimal conversions) - All 8 WsPacket field numbers confirmed from
BaseProto$WsPacket.smali - DeviceType IDs confirmed with register tracing (including the gap at deviceId=3)
- BLE command bytes (0-7) confirmed from
j80.smali - BLE packet format confirmed from
l80.smali(header layout, CRC, end marker) - WebSocket URL and ports confirmed from
v55.smali - CRC-16/MODBUS confirmed from
vc0.smalilookup table