Prd/docs/runbooks/pixel10a-flash-manual.md
Lucy Doupalů be9f14ce34 Helpdesk - operator console + patched GrapheneOS Dialer for call handling
A small helpdesk system: an office Pixel running a patched GrapheneOS Dialer
answers technician calls, records both call legs as separate channels, and a
Ruby backend transcribes them through Whisper and files an AI summary against
the caller.

Squashed to a single commit for sharing. No credentials are included; secrets
live outside the repo in /etc/helpdesk/env on the server or a gitignored
.claude/env.local locally. See .claude/env.local.example for the shape.

Start at README.md, then docs/architecture.md.
2026-07-27 18:50:32 +02:00

16 KiB

Pixel 10a "stallion" - safe flash manual (self-built GrapheneOS, bootloader stays UNLOCKED)

A validation flash of a self-built userdebug/test-keys image. The bootloader stays UNLOCKED - we are deliberately NOT locking it (locking on test keys is the one real brick). The procedure was verified against the actual build artifacts on 2026-07-09; the facts that make it safe:

  • Always use the build-matched fastboot/adb from the tree that produced the image you're flashing: <tree>/out/host/linux-x86/bin/. On this build box that's /build/grapheneos/out/… (the 2026-07 full-image build lives at /scratch/graphene-out/…). No system platform-tools are installed, on purpose.
  • fastboot-info.txt contains NO flash bootloader or flash radio line → flashall never touches firmware → no anti-rollback brick vector from this flash.
  • android-info.txt is the require gate (board=stallion plus exact version-bootloader / version-baseband strings - the 2026-07 build required stallion-17.0-15199480 / g5400i-260317-260429-B-15308590). The match is exact string equality and aborts safely on any mismatch, older or newer. Read your build's own file before flashing.
  • Flash sequence driven by fastboot-info.txt: boot/init_boot/dtbo/vendor_kernel_boot/pvmfw/ vendor_boot → vbmeta → reboot fastbootd → update-super → system/system_dlkm/system_ext/product/ vendor/vendor_dlkm.

This is written for two people: [YOU] is whoever holds the phone and presses its buttons; [ME] is whoever drives the terminal on the build box. Fine if that's the same person, or a human and an assistant.


Before we start - what YOU need

All items below are phone-side [YOU] actions unless noted.

  • [YOU] Charge the phone to a comfortable level (ideally 50%+). A power loss mid-flash can hard-brick it.
  • [YOU] A known-good USB-C DATA cable - the one that shipped with the device is safest. Connect it directly to a rear/motherboard USB port on the build box. No USB hub, no front-panel port. Bad cables/hubs are the single most common cause of flashing failures. (grapheneos.org/install/cli)
  • [YOU] Headset splitter UNPLUGGED for the entire session. The Pixel 10a has one USB-C port; a headset splitter puts it in audio-accessory mode and fastboot/adb will not enumerate at all (the CC pins carry analog audio, not USB). (source.android.com/docs/core/interaction/accessories/headset/usb-adapter)
  • [YOU] Enable Developer options: Settings → About phone → tap Build number 7 times.
  • [YOU] Enable USB debugging: Settings → System → Developer options → USB debugging ON. (When you first plug in, tap Allow on the RSA-key prompt.)
  • [YOU] Enable OEM unlocking: Settings → System → Developer options → OEM unlocking ON. This requires internet (the stock OS checks the device wasn't sold carrier-locked). Without it, fastboot flashing unlock is refused. (grapheneos.org/install/cli, source.android.com/.../locking_unlocking)
  • [YOU] Back up anything you care about. Unlocking the bootloader triggers a mandatory factory data reset - ALL data is erased, including photos, videos, and app data. This is unavoidable and expected. (source.android.com/.../locking_unlocking)
  • [ME] (PC-side, no phone action) Rescue kit staged locally - I will pre-download and verify the official Google stock stallion factory image + full-OTA zip and the official GrapheneOS stallion release before we begin, so recovery needs no network.

Golden rules (don't brick it)

  1. NEVER run fastboot flashing lock. These images are signed with the AVB public test key; a locked bootloader can't verify them and drops to AVB RED = unbootable = brick. Stay UNLOCKED (orange). (grapheneos.org/build, source.android.com/.../boot-flow)
  2. NEVER turn the OEM-unlocking toggle OFF. If it's off while the OS is unbootable, fastboot flashing unlock is refused = hard brick with no recovery. Keep it ON throughout. (grapheneos.org/install/cli)
  3. NEVER downgrade firmware. Do not flash an older bootloader/radio than the phone already has. Tensor anti-rollback is eFuse-enforced and permanent, even while unlocked. For this flash we leave stock firmware alone (see rule 4). (developers.google.com/android/images)
  4. flashall does NOT flash firmware - and we do NOT flash the self-built firmware. We flash only the OS + vbmeta, leaving Google's stock bootloader/radio in place. (Our fastboot-info.txt contains no flash bootloader/flash radio line, confirming this.)
  5. NEVER interrupt a flash. No unplugging, no button presses, no Ctrl-C, no host sleep. A partial firmware/update-super write is a classic brick. Never run snapshot-update cancel. (grapheneos.org/install/cli)
  6. NEVER use --force to push past a requirements not met! abort. That check is a real guardrail (exact string match on bootloader/baseband); forcing past a firmware mismatch can leave the device non-booting.

The procedure

I will use the build-matched tools (fastboot 37.0.0-2026070800) at /scratch/graphene-out/host/linux-x86/bin/ - no system platform-tools are installed, and these exactly match the build. udev/permissions may require me to run fastboot under sudo.

Step 1 - [YOU] Complete the "Before we start" checklist

Confirm: phone charged, headset splitter unplugged, USB-C data cable direct to a rear port, Developer options + USB debugging + OEM unlocking all ON, data backed up. Tell me when done.

Step 2 - [ME] Prepare the PC environment and verify tools

# point at the out/ of the tree that built the image you're flashing:
export OUT_TREE=/build/grapheneos/out          # or /scratch/graphene-out for the 2026-07 image
export PATH=$OUT_TREE/host/linux-x86/bin:$PATH
export ANDROID_PRODUCT_OUT=$OUT_TREE/target/product/stallion
fastboot --version    # must report the version your build shipped (2026-07 image: 37.0.0-2026070800)

Step 3 - [ME] Reboot the phone to the bootloader

With the phone booted and USB debugging authorized:

adb reboot bootloader

(Alternative if adb isn't authorized - [YOU] power off, then hold Volume-Down while powering on.)

Step 4 - [YOU] Confirm bootloader mode on screen

The screen shows a red warning triangle and the words "Fastboot Mode". Tell me you see it. (grapheneos.org/install/cli)

Step 5 - [ME] Confirm the PC sees the phone

fastboot devices        # must list a serial - if blank, I'll retry under sudo
fastboot getvar product # must return the stallion product string

If no serial appears, we STOP and fix the USB link (cable/port/permissions) before anything else.

Step 6 - [ME] Read current device state (read-only, non-destructive) - MANDATORY pre-flash check

fastboot getvar unlocked
fastboot flashing get_unlock_ability
fastboot getvar current-slot
fastboot getvar version-bootloader
fastboot getvar version-baseband

What I check (this is the anti-rollback / mismatch gate):

  • This build requires exactly version-bootloader=stallion-17.0-15199480 and version-baseband=g5400i-260317-260429-B-15308590 (from android-info.txt). The require check is exact string equality - it aborts on any mismatch, older or newer, and does not do version math.
  • Because we are not flashing firmware, the requirement is that the phone's current stock firmware matches these strings. If it differs in either direction, we STOP and reconcile firmware from the official Google stallion factory image before flashing the OS - we do not --force.
  • Anti-rollback STOP condition: if the phone's current bootloader/baseband is newer than this build's, do not proceed to flash anything firmware-related; downgrading Tensor firmware is a permanent brick. (developers.google.com/android/images)
  • unlocked should read yes and get_unlock_ability should confirm unlockable. Tell me what the on-screen "Device state" line says as a cross-check.

Step 7 - [ME/YOU] Unlock the bootloader only if not already unlocked - DATA WIPE POINT

If Step 6 already shows unlocked: yes, skip this step (no-op).

Otherwise [ME] runs:

fastboot flashing unlock

[YOU] - the phone shows a warning prompt. Use a Volume button to move the selection to "UNLOCK THE BOOTLOADER", then press Power to confirm. This wipes ALL data (factory reset). (grapheneos.org/install/cli, source.android.com/.../locking_unlocking)

Step 8 - [ME] Flash the OS via flashall - DATA WIPE POINT (-w)

This is the correct path for our raw m output (there is no signed factory zip / flash-all.sh). flashall reads fastboot-info.txt and drives the exact sequence: flash boot/init_boot/dtbo/vendor_kernel_boot/pvmfw/vendor_bootflash --apply-vbmeta vbmetaauto-reboots into fastbootdupdate-super (repartitions super from super_empty.img) → flash the six logical partitions (system, system_dlkm, system_ext, product, vendor, vendor_dlkm) → erase userdata/metadata. This does NOT flash bootloader or radio. (source.android.com/docs/setup/build/running)

fastboot flashall -w
  • -w wipes userdata/metadata - correct for a first validation flash.
  • [YOU] Do NOT touch the phone, cable, or terminal until it finishes. The phone will reboot itself into fastbootd mid-sequence - that is normal, do not interrupt it. (grapheneos.org/install/cli)
  • On this A/B device, flashall sets the flashed slot active and by default also populates the secondary slot - desirable so both slots hold this build. I will not pass --skip-secondary, and will not manually mark the other slot active.
  • If it aborts with requirements not met!, we STOP (see rule 6 / Step 6) - I will not --force.

Step 9 - [ME] Trigger first boot

fastboot reboot

Step 10 - [YOU] Confirm first boot and verified-boot state

  • The phone shows the orange "device is unlocked / can't be verified" warning for ~10 seconds, then boots. This is the expected, supported state for an unlocked test-keys build - not an error, and not a reason to lock. (It is strictly ORANGE, not yellow.) (source.android.com/.../boot-flow, grapheneos.org/build)
  • Let it reach the setup wizard / home screen. Tell me it booted.
  • [YOU] IMPORTANT: if the setup wizard offers a final toggle that would disable OEM unlocking (often checked by default), leave OEM unlocking ENABLED - do not accept disabling it. (grapheneos.org/install/cli)

Step 11 - [ME] Verify the running build

Once booted with USB debugging re-authorized ([YOU] tap Allow on the RSA prompt):

adb shell getprop ro.build.fingerprint
# expect: google/stallion/stallion:17/CP2A.260605.012/2026070800:userdebug/test-keys

Match confirms the self-built image is running. Done.


If something goes wrong

Why you're safe: the bootloader is UNLOCKED and OEM-unlocking is ON. That means fastboot still works even when Android won't boot, so essentially every bad flash here is a recoverable soft brick - you just re-flash a known-good image. An unlocked + OEM-unlock-on device is recoverable in software. (grapheneos.org/install/cli)

Recovery path, in order:

  1. [YOU] Power off, hold Volume-Down while powering on → back to Fastboot Mode (red triangle).
  2. [ME] Re-run the OS flash: fastboot flashall -w (drives update-super + fastbootd correctly - never hand-flash system/vendor from bootloader mode; they only flash in fastbootd).
  3. [ME/YOU] If the flashed slot won't boot, prefer re-flashing the good image to the active slot over switching slots. (Slot-switching can trigger an anti-rollback fallback if the other slot holds older firmware.)
  4. Rescue to stock: flash the pre-staged official Google stallion factory image / full-OTA (developers.google.com/android/images), or use flash.android.com (Android Flash Tool, works from Fastboot Mode even when the OS won't boot - needs OEM unlocking, which we have). (source.android.com/docs/setup/test/flash) Then re-flash the self-built image.
  5. Only if truly hard-bricked (no display, no fastboot, no charge): Tensor SoCs have no public EDL/unbrick route → Google RMA. This is why rules 1-5 above matter more than any recovery step.

Do NOT, during recovery: run fastboot flashing lock, disable OEM unlocking, flash older firmware, or interrupt a write.


Known unknowns (confirm live before/at the relevant step)

  • [Step 6, CRITICAL] Device anti-rollback / firmware match for the 10a specifically. Google's May-2026 ARB advisory names Pixel 10 / 10 Pro / 10 Pro XL / 10 Pro Fold - the 10a "stallion" is NOT on that published list, so its ARB status is unverified from docs. The controlling fact is the on-device value, not the press release. We must read version-bootloader/version-baseband (both slots if in doubt) at Step 6 and confirm the phone is not newer than this build before proceeding. If newer → STOP.
  • [Step 6] fastboot getvar unlocked exact behavior on stallion is unverified; that's why Step 6 also uses flashing get_unlock_ability and the on-screen "Device state" readout as cross-checks.
  • [Step 6/8] Firmware string match. Since we don't flash firmware, the stock bootloader/radio on the phone must equal the android-info.txt require strings. If they don't match, flashall will abort (correctly). We confirm live and, if needed, reconcile via the official Google factory image - never --force.
  • [Step 8] Secondary-slot / vendor_kernel_boot handling on stallion - flashall populates both slots by default; before executing I'll cross-check the sequence against fastboot-info.txt (already read: no firmware lines present) and, if a firmware reconcile is ever needed, against the official stallion factory flash-all command list.
  • [Out of scope now - future relock only] AVB vbmeta rollback index enforcement while unlocked is documented for LOCKED but ambiguous for UNLOCKED. We stay unlocked and use flash --apply-vbmeta vbmeta (verity/verification stay enabled, matching the build's Flags:0); we do not add --disable-verity/--disable-verification, and we do not flash a vbmeta with a lower rollback index than the device stored. Relock is a separate, deferred, higher-risk step requiring your own release/AVB keys - not this pass.

The files that decide everything above live in the build output: fastboot-info.txt (the flash sequence; confirms no firmware lines) and android-info.txt (the require gate) under $OUT_TREE/target/product/stallion/, with the matching tools in $OUT_TREE/host/linux-x86/bin/. Read them for the tree you're about to flash; don't trust a manual older than your build.