Document X4 Android reconnect protocol research and recovery options

This commit is contained in:
DCCONSTRUCTIONS
2026-09-10 11:25:07 +03:00
parent 3b85299fd9
commit b536f0049a
6 changed files with 334 additions and 4 deletions
@@ -0,0 +1,102 @@
"""Read selected protobuf descriptors from the pinned SDK without loading its code.
Research only: schema presence does not establish X4 firmware support, writability,
transport availability or a complete command frame. No device/network access.
"""
import hashlib
import json
import re
import time
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SDK_SHA256 = "6d20aca1930101293308c056cef552c0beb8cbf1d6c7d567a79e95a52f9b1373"
FILES = {"options.proto", "message_code.proto", "commands/get_options.proto", "commands/set_options.proto"}
OPTIONS = {"udisk_mode", "bt_wakeup_sw"}
VALUES = {"UDISK_MODE", "UDISK_MODE_PC", "UDISK_MODE_ANDROID", "BT_WAKEUP_SW",
"PHONE_COMMAND_GET_OPTIONS", "PHONE_COMMAND_SET_OPTIONS", "PHONE_COMMAND_REBOOT_CAMERA"}
def varint(data, cursor):
result = 0
for shift in range(0, 70, 7):
value = data[cursor]
cursor += 1
result |= (value & 127) << shift
if not value & 128:
return result, cursor
raise ValueError("Invalid varint")
def fields(data, terminated=False):
result, cursor = {}, 0
while cursor < len(data):
key, cursor = varint(data, cursor)
if key == 0 and terminated:
break
number, wire = key >> 3, key & 7
if not number:
raise ValueError("Invalid field number")
if wire == 0:
value, cursor = varint(data, cursor)
elif wire == 2:
length, cursor = varint(data, cursor)
value = data[cursor:cursor + length]
cursor += length
if len(value) != length:
raise ValueError("Truncated field")
else:
raise ValueError("Unsupported descriptor wire type")
result.setdefault(number, []).append(value)
return result
def name(record):
return record[1][0].decode("utf-8")
def enum(record):
return {name(item): item[2][0] for raw in record.get(2, [])
if (item := fields(raw)) and name(item) in VALUES}
def message(record, all_fields=False):
selected = []
for raw in record.get(2, []):
field = fields(raw)
if all_fields or name(field) in OPTIONS:
selected.append({"name": name(field), "number": field[3][0],
"label": field[4][0], "type": field[5][0],
"type_name": field.get(6, [b""])[0].decode()})
enums = {name(item): values for raw in record.get(4, [])
if (item := fields(raw)) and (values := enum(item))}
return {"fields": selected, "enums": enums}
def inspect(data):
if hashlib.sha256(data).hexdigest() != SDK_SHA256:
raise ValueError("SDK bytes differ from the inspected version")
found = {}
for match in re.finditer(rb"\n([\x01-\x7f])([\w/.-]+\.proto)\x12", data):
filename = match[2].decode()
if match[1][0] != len(match[2]) or filename not in FILES:
continue
descriptor = fields(data[match.start():match.start() + 65536], terminated=True)
messages = {name(item): message(item, filename.startswith("commands/"))
for raw in descriptor.get(4, []) if (item := fields(raw))}
found[filename] = {"offset": match.start(), "messages": messages,
"enums": {name(item): enum(item) for raw in descriptor.get(5, [])
if (item := fields(raw)) and enum(item)}}
if set(found) != FILES:
raise ValueError("Required descriptors not found")
return found
if __name__ == "__main__":
data = (ROOT / "build/sdk/lib/libCameraSDK.so").read_bytes()
print(json.dumps({"session": "RESEARCH01-schema", "utc": datetime.now(timezone.utc).isoformat(),
"monotonic": time.monotonic(), "sdk_sha256": SDK_SHA256,
"source_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
"hardware_tested": False, "descriptors": inspect(data)}, indent=2))