Skip to content

fix: resume Android APK download after network reconnect#11633

Merged
originalix merged 8 commits into
xfrom
fix/android-apk-resume-download
May 14, 2026
Merged

fix: resume Android APK download after network reconnect#11633
originalix merged 8 commits into
xfrom
fix/android-apk-resume-download

Conversation

@huhuanming
Copy link
Copy Markdown
Contributor

Summary

  • Android APK download restarted from byte zero on every network reconnect; the 3-attempt / ~12s retry budget was usually burned inside the outage window.
  • Three pieces conspired: native side had no Range support, ASC fetch failures during the outage deleted the partial, and the JS retry layer wasn't network-aware.

What changed

  • patches/@onekeyfe+react-native-app-update+3.0.33.patch — carries the Android downloadAPK rewrite (.partial + Range + 416 recovery + ApkVerifyOutcome tri-state). Upstream source change: fix(app-update): android downloadAPK Range/.partial resume app-modules#58 — patch will retire on the next @onekeyfe/react-native-app-update release.
  • updateRetry.tsrunDownloadWithRetry is now NetInfo-aware: while globalNetInfo reports the device offline we camp on addEventListener (capped at 5 min) and release on the first online emission; otherwise exponential backoff (now capped at 60s). MAX_ATTEMPTS raised 3→5 so the budget survives a real Wi-Fi/cellular handover.
  • useAppUpdate.test.ts — bumps the exhaustion test to 5 retries, adds a 60s-cap test, and adds an "offline → online resumes" test. globalNetInfo merged into the components mock because the root jest.config moduleNameMapper rewrites every @onekeyhq/components subpath to the same shared mock file.

iOS / desktop scope check: iOS ReactNativeAppUpdate.swift is a no-op stub (App Store handles updates); desktop uses electron-updater which already supports Range/resume. No changes needed there.

Test plan

  • npx jest packages/kit/src/components/AppUpdate/useAppUpdate.test.ts → 128/128 pass (verified locally).
  • npx tsgo -p tsconfig.json --noEmit → exit 0 (verified locally).
  • yarn lint:staged → 0 errors (verified locally).
  • On a real Android device: trigger an APK download (~200MB), toggle airplane mode at 5%, restore network → progress resumes from 5% rather than 0%.
  • Same flow but pull the network cable on the dev machine instead of toggling airplane mode → confirm runDownloadWithRetry blocks on NetInfo until the link comes back, then resumes.
  • Force a stale local APK (cp a wrong file into the cache dir at the expected name) → verify the SHA mismatch path still deletes and re-downloads cleanly.
  • Regression on the OTA bundle path (which shared runDownloadWithRetry): trigger a JS-bundle OTA download and confirm 5-retry / NetInfo behavior doesn't break it.

When the Android app-update download lost the connection mid-transfer it
restarted from byte zero on every retry, throwing away the bytes already
on disk. Three things conspired:

  1. native-side downloadAPK had no Range support and treated the only
     possible cache hit as "fully-downloaded + ASC-verified or delete";
  2. ASC fetch failures during a network outage were collapsed into
     "existing APK invalid" so the partial got deleted by the verifier
     before resume could ever kick in;
  3. JS retry burned through 3 attempts in ~12s with no network awareness,
     so the whole retry budget was usually consumed inside the outage
     window.

Fix in three pieces:

  - Patch @onekeyfe/react-native-app-update Android downloadAPK to mirror
    react-native-bundle-update's resume model: .partial file +
    Range header + 416/Content-Range recovery + ApkVerifyOutcome tri-state
    so an unreachable ASC preserves the bytes instead of deleting them.
    Source change committed upstream in app-modules; carried here via
    patch-package until a new @onekeyfe release.
  - Rework runDownloadWithRetry to be NetInfo-aware: while
    globalNetInfo reports the device offline we camp on
    addEventListener (capped at 5 min) and release on the first online
    emission; otherwise we keep the exponential backoff (now capped at
    60s) and raise MAX_ATTEMPTS from 3 to 5 so the budget survives a
    real handover.
  - Extend the useAppUpdate jest suite: bump the exhaustion test to 5
    retries, add a 60s-cap test, and add a NetInfo "offline → online
    resumes" test. Merge globalNetInfo into the components mock because
    the root jest.config moduleNameMapper rewrites every @onekeyhq/components
    subpath to the same shared mock file.
@revan-zhang
Copy link
Copy Markdown
Contributor

revan-zhang commented May 14, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Comment thread patches/@onekeyfe+react-native-app-update+3.0.33.patch Outdated
Retry log now distinguishes offline-wait (capped at 5min) from exp backoff,
and waitForOnlineOrTimeout emits start/end markers with exit reason so
appUpdate.log timestamps match actual wait duration. Also syncs the stale
"3x retry" call-site comment to the current 5x reachability-aware policy.
…/app-monorepo into fix/android-apk-resume-download

# Conflicts:
#	apps/mobile/package.json
#	package.json
#	packages/components/package.json
@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 14, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedreact-native-get-random-values@​3.0.37100100100100100

View full report

@huhuanming huhuanming enabled auto-merge (squash) May 14, 2026 10:43
originalix
originalix previously approved these changes May 14, 2026
- Remove unreachable lastError/throw at the bottom of runDownloadWithRetry
  (the for-loop always exits via return or the attempt===MAX throw).
- Collapse the redundant exitReason='timeout' inside setTimeout — default
  value already covers the path.
- Add a jest case for the offline-wait cap: stays offline, advances 5min,
  expects the grace timer + retry to land successfully.
@originalix originalix disabled auto-merge May 14, 2026 11:02
@originalix originalix merged commit 9eb98fd into x May 14, 2026
12 checks passed
@originalix originalix deleted the fix/android-apk-resume-download branch May 14, 2026 11:02
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.

3 participants