An over-the-air firmware update is the only feature in a connected product where a software bug produces dead hardware. A crash in your app is a bad review; a botched OTA is a returned device, an RMA, or a recall. And the transport you are relying on is a phone — a device that can wander out of range, sleep, get killed by the OS, or run out of battery mid-flash. So the design principle is not “make the transfer reliable.” It is: assume the transfer fails at the worst possible moment, and make that survivable. In practice that means a dual-bank layout where the running firmware is never overwritten, a signature the device verifies before it swaps, anti-rollback to block downgrades, and a confirm-or-revert step so firmware that cannot boot undoes itself. The app’s job is narrower than most teams assume. Here is the architecture.
This is a companion to our BLE security deep-dive, which argues that the mobile app must be treated as untrusted. OTA is where that principle stops being theoretical.
1. Why OTA is different from every other feature
Every other bug in a mobile product is recoverable. A crash gets a hotfix. A bad API response gets a retry. A broken screen gets a patch release next week.
A bricked device gets a courier.
That asymmetry should drive every decision below. The cost of an OTA failure is not measured in app-store rating points; it is measured in hardware, shipping, support time, and — if the product is in someone’s home or on someone’s body — trust you do not get back. Design accordingly: not for the happy path, but for the moment the user walks out of range with 40% of an image transferred.
2. The failure model: everything that interrupts a transfer
Before architecture, enumerate what actually goes wrong. A BLE OTA has to survive all of this:
- The user walks out of range mid-transfer.
- The phone sleeps, or the OS kills the app in the background.
- The Bluetooth connection drops for ordinary radio reasons.
- The phone’s battery dies. Or the device’s battery dies — worse.
- A chunk arrives corrupted.
- The user is sent the wrong image for their hardware revision.
- An attacker sends a malicious or downgraded image deliberately.
- Power is lost during the flash write itself.
Note that only some of these are attacks. Most are Tuesday. An architecture that only defends against attackers, but not against a user putting their phone in their pocket, will still brick devices.
3. The device-side design: make bricking structurally impossible
This is the core of the article, and the part the mobile team does not own — but must insist on. No mobile app architecture can compensate for a device that flashes in place. If the device overwrites its running firmware as chunks arrive, an interrupted transfer is a brick, and nothing you do in Dart or Swift can prevent it.
The device must have:
- Dual-bank (A/B) flash layout. The incoming image is written to a staging slot while the running firmware stays completely intact. Nothing about the live system is touched during transfer. An interrupted transfer wastes bandwidth, not hardware.
- Signature verification in the bootloader, on the complete image. The device only swaps after it has verified the whole staged image. Verification happens after the transfer, on the device — never in the app.
- Anti-rollback (version monotonicity). The bootloader refuses an image older than what is installed, so an attacker cannot force a downgrade to a version with a known vulnerability.
- Confirm-or-revert. After the swap, the new firmware must actively confirm itself on first successful boot. If it does not (because it crashes on startup), the bootloader reverts to the previous image on the next reset. This is the self-healing property that saves you from shipping firmware that bricks on boot.
- A protected bootloader. The bootloader itself must not be overwritable by an OTA, or a bad update can destroy the very mechanism that would recover it.
- A battery gate. The device refuses to begin an update below a safe charge threshold. A power loss during the final flash write is one of the few genuinely unrecoverable moments.
If your hardware team gives you a single-bank, flash-in-place OTA and asks the app to “just be careful,” the correct response is that the risk is unmanageable at the app layer. Escalate it; do not paper over it.
4. The transport: BLE was not designed for this
Bluetooth LE was designed to move small sensor readings, not megabytes of firmware. The defaults are brutal:
- The default ATT MTU is 23 bytes, leaving only 20 bytes of usable payload per write.
- The spec allows an ATT MTU up to 517 bytes — negotiate up, always.
- Data Length Extension (Bluetooth 4.2+) raises the maximum link-layer payload from 27 to 251 bytes. MTU is negotiated at the ATT layer; DLE at the link layer. You need both.
- Connection interval matters, and not always intuitively — a longer interval can yield higher throughput if it lets more packets move per connection event.
With MTU negotiated up, DLE enabled, write-without-response for the bulk transfer, and sensible flow control, a well-tuned BLE transfer lands in the tens of kilobytes per second. That is workable for firmware — and it still means a meaningful image takes minutes, not seconds. Every minute is another minute in which the user can walk away. Design the UX for a transfer that takes real time (section 8).
Do not send the image as an undifferentiated stream. Chunk it, and verify integrity at two levels: a checksum per chunk so a corrupted packet is caught and retried immediately, and a hash/signature over the whole image that the device validates before the swap.
5. Resumability: the transfer will be interrupted
Because a transfer takes minutes and the world is hostile, resume is not an optimization — it is the feature. Restarting a five-minute transfer from zero on every dropped connection produces an OTA that users simply refuse to run, which means devices that never get security patches.
The mechanism is simple and belongs in your GATT protocol design: the device tracks how many bytes of the staging slot it has already accepted, and exposes that offset. On reconnect, the app reads the offset and continues from there rather than starting over.
For that to be safe, the device must persist the offset and the partial image across a reset, and must still validate the complete image before swapping — a resumed transfer gets no less scrutiny than a fresh one.
6. What the mobile app is actually responsible for
Given a correct device design, the app’s job is narrow and mostly about not dying:
- Keep the connection alive and the app alive for the duration. On Android, run the transfer under a foreground service with a visible notification; a background transfer will be killed by Doze or the OS’s background limits. On iOS, understand Core Bluetooth’s background constraints — you cannot assume a long transfer survives backgrounding.
- Request a faster connection priority for the transfer, and negotiate MTU/DLE up front.
- Drive chunking, flow control, and retry of individual chunks.
- Track and resume from the device’s offset (section 5).
- Show honest progress, including that the update is still running and the user must stay close.
- Verify it is sending the right image for that device’s hardware revision and current version — a convenience check, not a security control.
- Fail safe. If the app dies, nothing is broken; the device still has its running firmware and a partial staging slot.
Notice what is not on this list: deciding whether the firmware is trustworthy. That is the device’s job.
7. Security: sign on the server, verify on the device
Our BLE security deep-dive shows how much of a BLE protocol is recoverable just by reading an Android HCI snoop log — no APK reverse-engineering needed. Apply that lesson here: anything the app knows, an attacker knows.
- Sign the firmware image on your build server with a private key that never leaves it. The bootloader verifies with an embedded public key. If signature checking lives in the app, an attacker bypasses the app entirely and writes to the characteristic directly.
- Never ship the signing key, or any master key, in the app binary. As we cover in hardening a Flutter MVP, obfuscation does not hide secrets — it only renames symbols.
- Enforce anti-rollback on the device, so a signed-but-old vulnerable image cannot be replayed.
- Use per-device credentials so compromising one unit does not compromise the fleet.
- Encrypt the image only if the firmware itself is the IP you are protecting. Encryption is not what stops bricking or spoofing — signing is.
- Authenticate who may start an update. Otherwise anyone in radio range can initiate one, which is a denial-of-service vector against a battery-powered device even if they cannot install anything.
8. UX that prevents bricks
A surprising share of OTA failures are design failures:
- Gate on battery — both devices. Refuse to start below a safe threshold on the peripheral, and warn on the phone.
- Tell the user this takes minutes and they must stay nearby. An unexplained progress bar invites them to walk away.
- Never offer a “Cancel” that leaves the device in an ambiguous state. With dual-bank, cancel is safe by construction — say so.
- Resume silently on reconnect rather than showing a scary failure.
- Do not auto-start an update while the device is in use, if the device does something safety-relevant.
9. How to test an OTA
You cannot claim an OTA is safe because it worked once on a desk. Build an interruption matrix and run every cell on real hardware:
| Interruption | Expected result |
|---|---|
| Walk out of range mid-transfer | Resume on reconnect; running firmware intact |
| Kill the app mid-transfer | Device unaffected; resume on relaunch |
| Phone backgrounded / screen off | Transfer continues (Android foreground service) or resumes |
| Device power-cycled mid-transfer | Staging slot discarded or resumed; boots old firmware |
| Corrupted chunk injected | Chunk checksum fails; chunk retried |
| Corrupted whole image | Signature check fails; no swap; old firmware boots |
| Wrong-hardware image | Rejected by the device |
| Downgrade attempt (older signed image) | Rejected by anti-rollback |
| New firmware crashes on boot | Bootloader reverts to previous image |
| Low battery at start | Update refused |
If any row ends in “device is dead,” the architecture is wrong — not the test.
10. The checklist
- Dual-bank flash; running firmware never overwritten during transfer.
- Signature verified on the device, on the complete image, before swap.
- Anti-rollback enforced by the bootloader.
- Confirm-or-revert on first boot.
- Bootloader itself protected from OTA overwrite.
- Battery gate on the peripheral.
- MTU negotiated up; DLE enabled; per-chunk checksum plus whole-image hash.
- Resume from a device-tracked offset, not restart.
- Android foreground service; iOS background limits understood.
- No keys in the app; signing done on the build server.
- The full interruption matrix passes on real hardware.
Where MaboaSoft fits
BLE and IoT companion apps are one of the things we build, and OTA is the part where the mobile app, the firmware, and the security model have to agree — which is exactly where products with separate app and hardware teams tend to get hurt. If you are designing or reviewing an OTA path, book a 20-minute call, or look at how we work.
FAQ
How do you stop a BLE firmware update from bricking the device? You make bricking structurally impossible on the device rather than trying to make the transfer perfect. Use a dual-bank layout: the new image is written to a staging slot while the running firmware stays intact, and the bootloader only swaps to the new image after it has verified the signature and integrity of the complete image. Add anti-rollback so an older image cannot be installed, and a confirm-or-revert step so firmware that fails to boot successfully is automatically rolled back on the next reset. With that design, an interrupted or corrupted transfer wastes bandwidth instead of destroying hardware.
Should the mobile app verify the firmware image before sending it? The app can check, but the device must verify. The phone is an untrusted transport: it can be rooted, its traffic can be intercepted, and its code can be reverse-engineered. Sign the firmware image on your build server with a private key, and have the bootloader verify the signature with a public key embedded on the device before it ever swaps to the new image. If verification only happens in the app, an attacker simply bypasses the app and flashes whatever they want.
Why is BLE so slow for firmware updates? Because Bluetooth LE was designed to move small sensor readings, not megabytes. The default ATT MTU is 23 bytes, which leaves only 20 bytes of usable payload per write. You must negotiate a larger MTU — up to 517 bytes is allowed by the spec — and enable Data Length Extension at the link layer, which raises the maximum link-layer payload from 27 to 251 bytes. Combined with write-without-response and sensible flow control, a well-tuned transfer reaches the tens of kilobytes per second, which is workable for firmware but still means a large image takes minutes, not seconds.
How should the app handle an interrupted BLE firmware transfer? Assume every transfer will be interrupted and design for resume rather than restart. Have the device track how many bytes of the staging slot it has received and expose that offset, so that when the app reconnects it can ask where to continue instead of starting over. Restarting a multi-minute transfer from zero on every dropped connection is how OTA becomes a feature users refuse to run. Crucially, an interrupted transfer must never leave the device in a broken state — with a dual-bank design the running firmware is untouched until the swap.
Sources (accessed 14 July 2026):
- Bluetooth SIG / Punch Through, BLE ATT MTU, DLE, and message sizing — default ATT MTU of 23 bytes (20 usable), the 23–517 range, and Data Length Extension raising the link-layer payload from 27 to 251 bytes. Throughput figures are practitioner measurements and vary by stack and hardware.
- Nordic Semiconductor, DFU bootloader validation — signed init packet and bootloader-side image validation in a secure DFU flow.
- Android Developers, Foreground services and Apple, Core Bluetooth background processing — platform constraints on long-running BLE work.