Skip to content

fix(app-update): android downloadAPK Range/.partial resume#58

Open
huhuanming wants to merge 3 commits into
mainfrom
fix/android-apk-resume-download
Open

fix(app-update): android downloadAPK Range/.partial resume#58
huhuanming wants to merge 3 commits into
mainfrom
fix/android-apk-resume-download

Conversation

@huhuanming
Copy link
Copy Markdown
Contributor

Summary

  • Android downloadAPK had no Range support and treated the only possible cache hit as "fully-downloaded + ASC-verified or delete". When a download dropped mid-transfer the next attempt deleted the residual bytes and restarted from byte zero.
  • ASC-fetch failure during a network outage was collapsed into "existing APK invalid" → partial got wiped before resume could ever help.

What changed

Mirrors the proven react-native-bundle-update.downloadBundle pattern:

  • In-flight bytes live in <filePath>.partial; the final path only ever holds a fully transferred APK. Old half-baked files at the final path are promoted to .partial for one-shot migration of existing installs.
  • Resume via Range: bytes=<offset>-. Handles 206 (append), 200 (server ignored Range — restart from byte zero), and 416 with Content-Range recovery for the crashed-just-before-rename case.
  • verifyExistingApk now returns an ApkVerifyOutcome tri-state (Valid / HashMismatch / Indeterminate). Indeterminate (ASC unreachable, ASC parse fail) preserves the partial and bubbles up a transient IOException so the JS retry layer can wait for network instead of deleting bytes preemptively.

Test plan

  • Trigger an update on Android, toggle airplane mode mid-download, restore network → next attempt resumes via Range: bytes=<offset>- and progress continues from where it left off.
  • Toggle airplane mode → existing APK invalid, deleting and re-downloading must NOT appear in logs; instead expect cannot verify existing APK (ASC unavailable); preserving file and aborting this attempt.
  • Force a server-side build replacement during a paused download → next attempt should fail SHA verify after full transfer and delete the corrupt final.
  • Verify pre-existing residual APK from old (pre-resume) builds is promoted to .partial on the first run after upgrade.
  • CI consumers (monorepo PR with patch-package) must build and pass for android (Apk + GooglePlay/Huawei skip path).

Companion monorepo PR: OneKeyHQ/app-monorepo#11623 (or whatever number — links patch-package wrapper until next release).

…tate verify

The Android downloadAPK path wrote bytes straight to the final filePath and
verified an existing APK by booleanly comparing it against a freshly-fetched
SHA256SUMS.asc. When the network dropped mid-download every retry path landed
on "ASC unreachable → existing APK invalid → delete" and restarted from byte
zero, throwing away the previously-downloaded bytes on every reconnect.

Mirror the proven react-native-bundle-update pattern:
  - In-flight bytes live in <filePath>.partial; the final path only ever
    holds a fully transferred APK. Old half-baked files at the final path
    are promoted to .partial so existing installs benefit from resume on
    the next attempt.
  - Resume via `Range: bytes=<offset>-`; handle 206 (append), 200 (server
    ignored Range → restart), and 416 with Content-Range recovery for the
    crashed-just-before-rename case.
  - verifyExistingApk returns an ApkVerifyOutcome tri-state — Valid /
    HashMismatch / Indeterminate. Indeterminate (ASC fetch failure, ASC
    parse failure, etc.) preserves the partial and bubbles up a transient
    error so the JS retry layer can wait for network instead of wiping
    the bytes preemptively.
…ck, 206 start check

Follow-up on the Range/.partial resume work:

- Extract the three-times-duplicated promote+verify+dispatch into `tryPromoteAndVerify` so future tweaks to promotion semantics happen in one place.
- `rollbackFinalToPartial` falls back to a stream copy when the same-fs rename fails, so a transient FS hiccup cannot wipe the only copy of an already-downloaded payload.
- Validate `Content-Range: bytes start-end/total` on 206 responses — if start ≠ requested offset (CDN bug / proxy rewrite), demote to a full restart instead of appending mis-aligned bytes and only catching it at SHA verify.
- 416 + hash mismatch now surfaces "server build changed mid-download" instead of the misleading raw "HTTP 416".
- Type the deferred-verification error as `ApkVerificationDeferredException` (extends IOException) so JS retry layer can branch on class name instead of substring-matching the message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant