Skip to content

Commit ea69d0e

Browse files
committed
prepair repo for apk apk reverse engineering
1 parent e0fe9ab commit ea69d0e

13 files changed

Lines changed: 495 additions & 30 deletions

.gitignore

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,18 @@ local.properties
99
*.iml
1010
.idea/
1111

12-
# Reverse-engineering workspace: generated/decompiled output and the APK itself are
13-
# never committed — see AGENTS.md §4 ("Generated code is build-time only ... must
14-
# never be committed to version control") and PROJECT.md's non-goals ("No distribution
15-
# of the original Google APK or any part of it").
16-
reverse-engineering/apk/jadx-output/
17-
reverse-engineering/apk/apktool-output/
12+
# Reverse-engineering workspace: the pulled APK(s), decompiled output (JADX/apktool), and
13+
# pbtk-extracted schemas are never committed to git — see AGENTS.md §4 ("Generated code is
14+
# build-time only ... must never be committed to version control"), PROJECT.md's non-goals
15+
# ("No distribution of the original Google APK or any part of it"), and PROJECT_RULES.md §8
16+
# rule 20 (decompiled Java/smali is a reproduction of Google's copyrighted code, banned from
17+
# this project's codebase even as research output — this holds even though it never leaves
18+
# the maintainer's own machine, since "codebase" here means this git history). Git LFS does
19+
# NOT exempt the APK file itself — anyone with repository access can still fetch an LFS
20+
# object, which is still distribution. The whole tree below (one subfolder per analyzed APK
21+
# version, per reverse-engineering/APK_VERSIONS.md) is ignored; only that small index file,
22+
# tracked outside this tree, is committed.
23+
reverse-engineering/apk/
1824
*.apk
1925

2026
# OS/editor cruft
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# APK_REVERSE_ENGINEERING_PROCEDURE.md — Pixel Buds APK Analysis Guide
2+
3+
**Purpose:** step-by-step procedure to pull, store, decompile, and analyze the official Pixel Buds
4+
companion app APK, in order to fill in the confidence-rated placeholders in `REVERSE_ENGINEERING.md`
5+
and, once wire-correlated, `PROTOCOL.md`. This document is the *how*`REVERSE_ENGINEERING.md` is
6+
the *what we found*, and `reverse-engineering/APK_VERSIONS.md` is the *which APK version(s) we've
7+
looked at*.
8+
9+
This procedure follows `AGENTS.md` §6/`DECISIONS.md` ADR-017's AI-assistance boundary throughout:
10+
an AI session may run the mechanical steps below (pulling, hashing, decompiling, running `pbtk`,
11+
keyword/string searching, explaining already-surfaced code or disassembly — including native `.so`
12+
disassembly output, per ADR-017 §4), but never decides which candidate is relevant to the protocol
13+
and never decides that something becomes a recorded HYPOTHESIS in `REVERSE_ENGINEERING.md`. Both of
14+
those remain the maintainer's calls at every step marked **[Maintainer decision]** below.
15+
16+
---
17+
18+
## 1. Prerequisites
19+
20+
### 1.1 On your computer
21+
- [ ] **JADX**, **apktool**, and **pbtk** installed (`WORKSTATION_PREPARATIONS.md`'s "Reverse
22+
engineering tools: JADX, apktool, pbtk" section) — pbtk's own dependencies (Python ≥ 3.10,
23+
PySide6, python-protobuf, and `jad`/`dex2jar` for some extractor scripts) confirmed there too.
24+
- [ ] **Android platform-tools** (`adb`) installed and on your `PATH` (already required by
25+
`CAPTURE_BLUETOOTH_HCI_SNOOP.md` §1.1).
26+
- [ ] `sha256sum` (standard on Fedora).
27+
28+
### 1.2 On the phone
29+
- [ ] The official Pixel Buds companion app installed under the maintainer's own Google account,
30+
on the maintainer's own device — this is the provenance basis `PROJECT_RULES.md` §8 rule 20
31+
requires; see `reverse-engineering/APK_VERSIONS.md`'s provenance column.
32+
- [ ] USB debugging enabled (same as `CAPTURE_BLUETOOTH_HCI_SNOOP.md` §1.2).
33+
34+
### 1.3 Reading this session should already have done
35+
- [ ] `AGENTS.md` §4/§6 (proto-schema extraction rules, sign-off requirement).
36+
- [ ] `DECISIONS.md` ADR-017 (the current AI-assistance boundary — supersedes the older ADR-003).
37+
- [ ] `REVERSE_ENGINEERING.md`'s own template and status legend.
38+
39+
---
40+
41+
## 2. Pulling and Storing a New APK Version
42+
43+
1. Find the package name and installed version:
44+
```bash
45+
adb shell dumpsys package com.google.android.apps.wearables.maestro.companion | grep -E "versionName|versionCode"
46+
```
47+
2. Find the APK path(s) — **a companion app this size may be split** (base + density/language/ABI
48+
splits), so `pm path` can legitimately return more than one line. Pull **all** of them:
49+
```bash
50+
adb shell pm path com.google.android.apps.wearables.maestro.companion
51+
# package:/data/app/.../base.apk
52+
# package:/data/app/.../split_config.xxhdpi.apk <- pull this one too, if present
53+
mkdir -p "reverse-engineering/apk/v<versionName>-<versionCode>/"
54+
adb pull /data/app/.../base.apk "reverse-engineering/apk/v<versionName>-<versionCode>/"
55+
adb pull /data/app/.../split_config.xxhdpi.apk "reverse-engineering/apk/v<versionName>-<versionCode>/" # repeat per split
56+
```
57+
3. Hash every pulled file and record the result:
58+
```bash
59+
sha256sum reverse-engineering/apk/v<versionName>-<versionCode>/*.apk
60+
```
61+
4. **[Maintainer decision — recording provenance is mechanical, but confirm the row is accurate]**
62+
Add a row to `reverse-engineering/APK_VERSIONS.md`'s table: version dir, file(s), SHA-256 per
63+
file, versionName/versionCode, pull date, source device, and provenance (how it was obtained —
64+
e.g. "Play Store, maintainer's own Google account, installed on the maintainer's own device").
65+
5. Remember: **nothing under `reverse-engineering/apk/` is ever committed** — it's fully covered by
66+
`.gitignore` (the APK itself, `jadx-output/`, `apktool-output/`, `pbtk-output/`). Only the
67+
`reverse-engineering/APK_VERSIONS.md` row you just added is git-tracked.
68+
69+
### 2.1 Diff / re-check pass against the previous version
70+
71+
A new companion-app release doesn't mean starting analysis from zero. Before decompiling from
72+
scratch:
73+
74+
1. If a previous version's `apktool-output/` still exists locally, diff the two versions' DEX class
75+
lists (e.g. `diff <(unzip -l v<old>/base.apk | grep '\.dex$') <(unzip -l v<new>/base.apk | grep '\.dex$')`,
76+
or compare `apktool`'s own `smali/` directory trees after decompiling both) to see whether the
77+
set of classes changed at all before re-running a full keyword search.
78+
2. Re-check every class/finding already recorded in `REVERSE_ENGINEERING.md`'s "Identified relevant
79+
classes" section and "Correlation status with `PROTOCOL.md`" table against the new version — note
80+
whether each one still exists, moved, or was removed/renamed, rather than assuming prior findings
81+
still apply unchanged.
82+
3. **[Maintainer decision]** Whether a shifted/renamed class still represents the same behavior (and
83+
whether any existing `PROTOCOL.md` FACT needs re-verification against the new version) is a
84+
relevance judgment — flag candidates, don't silently carry forward or silently invalidate a prior
85+
finding.
86+
87+
---
88+
89+
## 3. Decompiling and Schema Extraction
90+
91+
```bash
92+
cd "reverse-engineering/apk/v<versionName>-<versionCode>/"
93+
94+
# Readable Kotlin/Java (primary source for keyword search and citations)
95+
jadx -d jadx-output/ base.apk
96+
97+
# Resources, manifest, smali (fallback when JADX misdecompiles something)
98+
apktool d base.apk -o apktool-output/
99+
100+
# .proto schema extraction (see WORKSTATION_PREPARATIONS.md for the real, confirmed pbtk scope —
101+
# it is NOT limited to Java/DEX; pbtk-from-binary targets native .so files too, though whether it
102+
# actually succeeds against libmaestro/libgfps's specific binaries is unconfirmed until tried)
103+
pbtk-jar-extract base.apk pbtk-output/
104+
pbtk-from-binary apktool-output/lib/<abi>/libmaestro.so pbtk-output/ # once §4's native .so pass locates it
105+
```
106+
107+
Record the exact **tool versions used** (`jadx --version`, `apktool --version`, pbtk's `pipx list`
108+
output) in `reverse-engineering/APK_VERSIONS.md`'s row for this version — decompiler output (line
109+
numbers, class layout) can shift between versions, so a file+line citation is only reproducible if
110+
the decompiler version is pinned too.
111+
112+
---
113+
114+
## 4. Keyword Search and Search-Efficiency Techniques
115+
116+
This is the mechanical step ADR-017 permits an AI session to run directly. Search results are
117+
**candidates**, not findings — every candidate goes to the maintainer for the relevance call before
118+
anything is recorded in `REVERSE_ENGINEERING.md`.
119+
120+
1. **Start from `AndroidManifest.xml`** (`apktool-output/AndroidManifest.xml`, not the obfuscated
121+
binary form) — find services/receivers registered against Bluetooth-related intents/actions
122+
first. This anchors which classes are worth reading closely before any blind keyword search.
123+
2. **Search for string literals, not just class/method names** — log tags, notification channel
124+
names, broadcast action strings, and SharedPreferences keys survive ProGuard/R8 obfuscation even
125+
when class names don't (obfuscation renames identifiers, not string constants).
126+
3. **Check `qzed/pbpctrl`'s public documentation first**, as a research accelerant — protocol
127+
*knowledge* only, per `AGENTS.md` §12/`README.md`'s attribution boundary (never copy code; every
128+
candidate this surfaces still needs independent re-verification against this project's own
129+
APK/captures, not taken on `pbpctrl`'s word).
130+
4. Search `jadx-output/` for the keyword list in `REVERSE_ENGINEERING.md` §Method (BLE/GATT classes,
131+
RFCOMM/Fast-Pair classes, HID classes, package-name fragments, `.proto`-generated-class markers).
132+
5. **Apply the exclusion list below during this step itself** — don't collect candidates from these
133+
areas and filter them out later; skip past them at search time.
134+
6. Present candidates to the maintainer: file path, line number, the matched keyword/string, and a
135+
one-line note on why it looked relevant. **[Maintainer decision]** which candidates get written up
136+
in `REVERSE_ENGINEERING.md`, and at what confidence tier.
137+
138+
### 4.1 Exclusion list — noise to skip past, not to investigate
139+
140+
A fully decompiled app exposes far more surface area than any single capture. The following are
141+
explicitly **out of scope** (`PROJECT.md` non-goals, `DECISIONS.md` ADR-008) — do not spend search
142+
or read time on them even if they surface incidentally:
143+
144+
- `AccountLinking` (any package/class matching this name)
145+
- `OwnershipTransfer` (any package/class matching this name)
146+
- `AccessoryNonOwner` (the Accessory Non-Owner Service — see ADR-008)
147+
- Any Firebase-, Analytics-, or Crashlytics-named package or class (`com.google.firebase.*`,
148+
`*Analytics*`, `*Crashlytics*`, or equivalent — see `AGENTS.md` §1's Zero-GMS rule)
149+
150+
If one of these surfaces incidentally while searching for something else, note it as
151+
"out-of-scope, skipped" (mirroring how `DECISIONS.md` ADR-008 already treats incidental
152+
Account-Linking/Non-Owner traffic in captures) and move on — do not follow the call graph into it.
153+
154+
---
155+
156+
## 5. Analysis Approach
157+
158+
1. Write up each maintainer-approved candidate in `REVERSE_ENGINEERING.md`'s "Identified relevant
159+
classes" section, using its template — **every finding cites the exact decompiled file and line
160+
number** (e.g. `jadx-output/sources/com/google/.../Xy2.java:142`), not only a class name.
161+
2. Label every finding FACT / HYPOTHESIS / ASSUMPTION / OPEN QUESTION per `PROJECT_RULES.md` §1 —
162+
static analysis alone is never 🟢 FACT for a *protocol* claim (only for "this code exists and
163+
looks like X"); a protocol-behavior claim needs capture correlation first.
164+
3. For every HYPOTHESIS recorded, add a short note on how it could be confirmed or refuted against a
165+
capture — which action/Test-ID (`TESTPLAN_BLUETOOTH_HCI_SNOOP.md`) would need to be captured —
166+
per `REVERSE_ENGINEERING.md`'s template (see `PROJECT_RULES.md` §4's hypothesis-test discipline,
167+
already used for captures).
168+
4. Once a finding is cross-checked against a real capture, promote it **directly** into `PROTOCOL.md`
169+
— there is no intermediate working-notes buffer (`PROTOCOL_NOTES.md` was retired 2026-08-15, see
170+
`PROJECT_RULES.md` §2 rule 5). Update `REVERSE_ENGINEERING.md`'s "Correlation status with
171+
`PROTOCOL.md`" table so the same finding is never independently "rediscovered" in both documents.
172+
5. **[Maintainer decision, per `AGENTS.md` §6]** Promoting anything to 🟢 FACT in `PROTOCOL.md`, or
173+
writing/superseding a `DECISIONS.md` ADR, still requires explicit maintainer sign-off — an AI
174+
session may propose and draft, never commit either as settled.
175+
176+
---
177+
178+
## 6. Notes & Gotchas
179+
180+
- **Never hand-reconstruct a `.proto` schema from getter/setter names alone** — field *names*
181+
recovered from JADX are not proof of the actual wire field *numbers*, which determine binary
182+
compatibility. Use `pbtk`'s actual extraction output (§3), not a guessed schema.
183+
- **JADX can misdecompile obfuscated/optimized constructs** — when a decompiled method looks
184+
suspicious or incomplete, cross-check against the `apktool` smali output before trusting it.
185+
- **Reflection-based code stays invisible to static analysis** — if a call site is never found
186+
despite a class clearly needing one, that's a sign dynamic analysis (Frida) may be needed; log any
187+
such experiment in the relevant capture's `CAP-NNN-FINDINGS.md` first, per `PROJECT_RULES.md` §4.
188+
- **Native `.so` disassembly is in scope for AI mechanical assistance** (`DECISIONS.md` ADR-017 §4),
189+
on the same terms as DEX/Java work: search, list, and explain disassembly output; never decide
190+
relevance or promote a finding. Whether `pbtk-from-binary` actually succeeds against
191+
`libmaestro`/`libgfps`'s specific binaries (vs. a stripped protobuf-lite descriptor pool) is
192+
unconfirmed until tried — see `WORKSTATION_PREPARATIONS.md`.
193+
- **A class or method existing in the APK does not prove it's exercised by any specific action** in
194+
`TESTPLAN_BLUETOOTH_HCI_SNOOP.md` — treat every static finding as 🟡 HYPOTHESIS until a capture
195+
shows the corresponding traffic (`REVERSE_ENGINEERING.md`'s own "Known limitations" section).
196+
- **Nothing under `reverse-engineering/apk/` is ever committed** — not the APK, not `jadx-output/`,
197+
not `apktool-output/`, not `pbtk-output/`. Only `reverse-engineering/APK_VERSIONS.md` and this
198+
procedure document are git-tracked. Double-check `git status` before committing anything from a
199+
reverse-engineering session.
200+
- **Never independently promote a finding to 🟢 FACT, and never commit a new/superseding
201+
`DECISIONS.md` ADR**`AGENTS.md` §6/§15, unaffected by ADR-017's mechanical-assistance boundary.
202+
203+
---
204+
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/APK_REVERSE_ENGINEERING_PROCEDURE.md - https://tedsluis.github.io/opencontrolpixelbudspro2/APK_REVERSE_ENGINEERING_PROCEDURE

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,28 @@ for the "definition of done" that will mark v1.
299299
specifically; `CAP-011`'s stale Case reading, the `flag` field's meaning, and the burst's
300300
trigger stay open, unaffected by the promotion.
301301

302+
- **2026-08-30: Phase 2 (APK reverse engineering) groundwork — governance, storage, and procedure.**
303+
The maintainer explicitly requested AI assistance with the *mechanical* parts of APK decompiling
304+
and proto-schema extraction; `DECISIONS.md` ADR-017 (superseding ADR-003, sign-off obtained on
305+
the exact wording before it was added) records the new boundary: an AI session may search, list
306+
candidates, run `pbtk`, and explain already-surfaced code or native `.so` disassembly output —
307+
the maintainer explicitly placed native `.so` disassembly assistance in scope too — but never
308+
decides relevance or promotes a `REVERSE_ENGINEERING.md` finding; `AGENTS.md` §6/§15's FACT/ADR
309+
sign-off requirement is unchanged. Also added: `WORKSTATION_PREPARATIONS.md`'s pbtk section,
310+
corrected against pbtk's own README rather than assumed (it has two extractors — Java/DEX and
311+
native-binary-with-reflection-metadata — so native `.so` extraction isn't ruled out the way this
312+
project first assumed, only unconfirmed against `libmaestro`/`libgfps` specifically); a versioned
313+
APK storage layout (`reverse-engineering/apk/v<versionName>-<versionCode>/`, indexed in the
314+
git-tracked `reverse-engineering/APK_VERSIONS.md`; the APK/decompiled/pbtk output itself is never
315+
committed, per an updated `.gitignore` covering the whole `reverse-engineering/apk/` tree);
316+
`APK_REVERSE_ENGINEERING_PROCEDURE.md` (pull/store → diff-against-previous-version → decompile →
317+
`pbtk` extract → keyword search, with an explicit out-of-scope exclusion list for
318+
AccountLinking/OwnershipTransfer/AccessoryNonOwner/Firebase-Analytics-Crashlytics); and
319+
`REVERSE_ENGINEERING.md` template updates (mandatory file+line citations, a per-finding
320+
hypothesis-to-capture-test link, and the same non-destructive-rewrite-in-place convention
321+
`CAP-NNN-FINDINGS.md` files use). `TODO.md`'s Phase 2 checklist updated to reflect this groundwork
322+
without checking off any actual analysis work, since no APK has been pulled yet.
323+
302324
### Removed
303325

304326
- `PROTOCOL_NOTES.md`, `EXPERIMENTS.md` (retired 2026-08-15, see above).

0 commit comments

Comments
 (0)