OTA Architecture
The OTA campaign pushed successfully to 10,000 vehicles, then bricked unit #10,001. How do you prevent that?
OTA (Over-The-Air) updates write to the inactive A/B slot while the active slot keeps the vehicle running, then reboot into the new slot on success. Virtual A/B reduces storage overhead by snapshotting blocks instead of duplicating entire partitions. UpdateEngine (AAOS) orchestrates download → verify → apply → mark pending → reboot → post-boot verification. Slot metadata (bootctrl in misc) tracks current slot, boot success, retry counts, and unbootable flags. Rollback is automatic if the new slot fails verification or post-boot health checks within a merge/rollback window. OTA is a state machine with cryptographic gates, not a simple file copy.
You trigger OTAs with lb campaigns. This page is the machinery behind the button: slots, snapshots, metadata, and the state transitions you’ll grep in update_engine logs.
A/B Update Scheme: Two Copies, One Active
Section titled “A/B Update Scheme: Two Copies, One Active”Classic A/B OTA maintains duplicate physical partitions:
Active: boot_a + super (system_a, vendor_a, ...) + vbmeta_aInactive: boot_b + super (system_b, vendor_b, ...) + vbmeta_b| Phase | Active Slot | Inactive Slot |
|---|---|---|
| Normal operation | Running OS | Stale or empty |
| OTA download | Running OS | Unchanged (payload cached separately) |
| OTA apply | Running OS | Being written |
| Reboot pending | Running until reboot | Marked pending/active |
| First boot after OTA | Unused | Boot attempt |
| Success | Becomes fallback | Confirmed active |
| Failure | Fallback boot | Marked unbootable |
OTA always builds on the inactive slot while the active slot keeps the vehicle running.
A/B OTA is changing tires while driving — the car runs on three wheels while the fourth is swapped. You only commit weight to the new tire (reboot) after it’s bolted on (apply complete + verified). If the new tire wobbles (boot failure), you put the old one back (slot fallback).
Virtual A/B: Storage-Efficient Evolution
Section titled “Virtual A/B: Storage-Efficient Evolution”Full A/B duplicates large partitions, expensive on 64 GB UFS programs. Virtual A/B (Android 11+) uses copy-on-write snapshots:
| Classic A/B | Virtual A/B |
|---|---|
Two full super copies |
One super + snapshot COW overlay |
| Higher storage cost | ~40–50% less space for same safety |
| Simple mental model | More complex merge phase |
| Direct partition write | snapshotd tracks changed blocks |
Virtual A/B flow additions:
- Create snapshot before apply
- Apply payload to snapshot/COW layer
- Boot into snapshot
- Merge snapshot into base on success (background, may take minutes)
- On failure → cancel snapshot, revert to base
Check your platform:
adb shell getprop ro.virtual_ab.enabled# true = Virtual A/BVirtual A/B merge in progress can block subsequent OTAs. If update_engine reports NEED_REBOOT or merge running, don’t start another campaign. You’ll get cryptic SNAPSHOT_MERGE errors in logs.
Full OTA Flow: Download to Confirmed Boot
Section titled “Full OTA Flow: Download to Confirmed Boot”stateDiagram-v2 [*] --> Idle Idle --> Downloading: Update available Downloading --> DownloadComplete: Payload on disk Downloading --> Idle: Cancel / network fail DownloadComplete --> Verifying: Hash + signature check Verifying --> Applying: Valid payload Verifying --> Idle: Invalid — abort Applying --> Applied: Inactive slot written Applying --> Idle: Apply error Applied --> PendingReboot: Mark slot pending PendingReboot --> FirstBoot: User/system reboot FirstBoot --> PostVerify: New slot boots PostVerify --> Success: Health OK PostVerify --> Rollback: Boot fail / dm-verity fail Success --> Merge: Virtual A/B merge (if enabled) Merge --> Idle: Merge complete Rollback --> Idle: Fallback slot active Success --> Idle: Classic A/B — donePhase Breakdown
Section titled “Phase Breakdown”| Phase | Component | What Happens |
|---|---|---|
| 1. Check | UpdateEngine / campaign agent | Query server or local package for new build |
| 2. Download | HTTP(S) / gRPC stream | Payload to /data/ota_package/ or cache partition |
| 3. Verify | UpdateEngine + OpenSSL | Signature, metadata, compatible version, rollback index |
| 4. Apply | update_engine --payload |
Binary diff or full payload → inactive slot partitions |
| 5. Finalize | bootctrl / misc | Set slot priority, mark pending reboot |
| 6. Reboot | Power manager / user consent | Automotive: often parked + ignition policy gated |
| 7. Post-boot verify | ABL + init + UpdateEngine | dm-verity, slot success marker, crash detection |
| 8. Merge | snapshotd (Virtual A/B) | COW merge into base partition |
Rollback Mechanism: Safety Net
Section titled “Rollback Mechanism: Safety Net”Rollback operates at multiple layers:
| Layer | Trigger | Action |
|---|---|---|
| Pre-apply | Signature/rollback index fail | Abort before writing inactive slot |
| Apply failure | I/O error, hash mismatch mid-write | Abort; inactive slot marked incomplete |
| Boot verification | ABL AVB failure | Try alternate slot |
| Post-boot window | Crash loop, watchdog, explicit markBootSuccessful() timeout |
Revert bootctrl to previous slot |
| User-initiated | Factory tools / recovery | Manual slot switch via fastboot |
bootctrl / Slot Metadata
Section titled “bootctrl / Slot Metadata”Stored in misc partition (or dedicated metadata region):
| Field | Purpose |
|---|---|
| Current slot | a or b; which slot booted this session |
| Priority | Higher priority slot tried first at boot |
| Successful boot flag | Set after clean boot + health check |
| Retry count | Decremented on failed boot attempts |
| Unbootable flag | Slot skipped entirely when set |
# From running Androidadb shell bootctl get-current-slotadb shell bootctl get-suffix 1 # _b suffix for slot 1adb shell bootctl dump-bootloader-controlA slot isn’t truly active until markBootSuccessful() runs. First boot after OTA is a probation period.
Delta vs Full Payloads
Full payloads replace entire partition contents: simpler, larger download. Delta payloads (binary diffs against source version) produce smaller OTA packages but require strict source version matching. Mismatch between installed build and delta source → apply failure. Campaign tools like lb must target exact build fingerprints when using deltas.
Automotive OTA Constraints
Section titled “Automotive OTA Constraints”Cockpit OTAs add policy gates beyond phone Android:
| Constraint | Reason |
|---|---|
| Vehicle speed = 0 | Safety; no reboot while moving |
| Battery / power budget | Sufficient power for download + apply + reboot |
| Ignition state | Some programs require ACC ON or engine running for apply |
| Multi-ECU coordination | Cockpit + modem + ADAS may need synchronized campaigns |
| User consent UI | AAOS Automotive settings / OEM HMI prompt |
| Differential signing | Production keys, fleet certificate pinning |
OTA debug grep list (save this):
adb logcat -s update_engine UpdateEngineadb shell dumpsys update_engineadb shell bootctl get-current-slotadb shell getprop ro.build.fingerprintStates to recognize in logs:
| Log Signal | Meaning |
|---|---|
UpdateStatus::DOWNLOADING |
Payload in progress |
UpdateStatus::VERIFYING |
Signature/hash gate |
UpdateStatus::FINALIZING |
Writing slot metadata |
UpdateStatus::UPDATED_NEED_REBOOT |
Apply done; reboot required |
ErrorCode::kDownloadTransferError |
Network issue, not a signing issue |
ErrorCode::kPayloadHashMismatchError |
Corrupt download or wrong payload |
ErrorCode::kRollbackNotPermitted |
Rollback index too low |
After OTA reboot, confirm success before celebrating:
adb shell bootctl get-current-slot # did slot flip?adb shell getprop ro.build.display.id # correct build?Page 8.1 defined A/B partition names. Page 8.4 explained AVB gates during apply and first boot. Page 8.6 maps this architecture to your lb tool’s gRPC + SOME/IP transport layer.
What matters
Section titled “What matters”- A/B OTA writes the inactive slot while the active slot runs. Reboot switches.
- Virtual A/B trades snapshot complexity for storage savings.
- Slot metadata in bootctrl governs fallback. First boot is probation until marked successful.
- OTA is a state machine. Learn UpdateEngine statuses to debug failed campaigns.
Check Your Understanding
1. During a classic A/B OTA apply phase, which slot is being written?
2. What marks an OTA slot transition as permanently successful (not subject to automatic rollback)?
3. How does Virtual A/B differ from classic A/B?