Skip to content

perf(mobile): use HTTP/2 on Android through app-wide Cronet engines - #9435

Merged
brow merged 2 commits into
mainfrom
mobile-http2-android
Nov 13, 2026
Merged

brow merged 2 commits into
mainfrom
mobile-http2-android

Conversation

@brow

@brow brow commented Nov 13, 2026 •

Copy link
Copy Markdown
Collaborator

Follows #8897 (iOS HTTP/2), now on main.

What

Android gets the same app-wide shared sessions as iOS, backed by Cronet (cronet_http) instead of NSURLSession:

  • One Cronet engine per traffic class (API, media), so requests to a host multiplex over one HTTP/2 connection and gzip is inflated natively, off the Dart isolate.
  • HTTP/2 on, HTTP cache off, QUIC off. QUIC would be a separate behavior change, and cronet_http's changelog lists DNS failures with QUIC on cellular.
  • Streamed uploads (media and voice notes) stay on dart:io on Android: CronetClient reads a request body into memory before sending it, which would break upload progress and copy large attachments in full.
  • If no Cronet provider is available (no Google Play services), the engine fails to build and the app keeps dart:io, as today.
  • Transport error classification covers Cronet failures alongside NSURLSession ones, so invite and pairing errors keep their friendly messages.

Measurements

Android emulator (API 36, arm64), profile builds of this branch and main, signed in to the Feedback community. 8 cold starts per build, alternating builds in blocks of 4. Times are for relay /query requests started in the first 8 s.

main (dart:io) this branch (Cronet)
First query response 6.99 s 1.99 s
Startup burst settled 8.36 s 2.78 s
Burst after the first connection 1.44 s 0.77 s
Query latency after the first connection, median / p90 165 / 1567 ms 106 / 158 ms

Unexplained stall: most of main's startup gap is a ~5 s stall before its first response on every cold start. Its cause is unconfirmed; my guess is dart:io loading its CA store on the emulator, which HTTP/2 itself wouldn't fix, so the first two rows likely overstate the win on real devices. The rows after the first connection exclude the stall and are the fairer comparison. I also tried emulated EDGE throttling, but the emulator's throttle did not slow its traffic, so there are no slow-link numbers yet.

Regressions

None found. Devices without Play services keep dart:io. Cleartext http:// requests keep the handle's own dart:io client, as on iOS.

Testing

  • Full mobile suite passes (flutter test, 4784 tests).
  • Automated app run on the emulator: onboarding, Feedback nsec import, channel and DM list load, and 16 cold starts with request timing. Media loading wasn't separately exercised.
  • No visible UI change, so no screenshots.

@brow
brow force-pushed the mobile-http2-android branch from eb00637 to d926e06 Compare November 13, 2026 01:34
@brow
brow force-pushed the mobile-http2-android branch from d926e06 to dd58314 Compare November 13, 2026 02:27
@brow
brow force-pushed the mobile-http2-android branch 2 times, most recently from 6e7070b to 36d8868 Compare November 13, 2026 15:29
Base automatically changed from mobile-http2 to main November 13, 2026 18:49
@brow
brow force-pushed the mobile-http2-android branch from 36d8868 to 490ca3f Compare November 13, 2026 18:51
@brow
brow marked this pull request as ready for review November 13, 2026 19:06
@brow
brow requested a review from a team as a code owner November 13, 2026 19:06
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-13T19:11:38.324920Z 490ca3f Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@brow
brow deployed to codex-review November 13, 2026 19:06 — with GitHub Actions Active
@github-actions github-actions Bot added the codex-security-review-current The posted Codex security review matches its recorded range. label Nov 13, 2026
@github-actions

github-actions Bot commented Nov 13, 2026 •

Copy link
Copy Markdown

🔐 Codex Security Review

Note: This is an automated, security-focused review generated by Codex.
Use it as a supplement to human review; false positives are possible.

Scope

  • Exact PR diff: 59ae028a360242d43ffbfdafca8a76eda00484d9...96aff9233f532b4005c9df56975373f355753a0b
  • Model: gpt-6.1-sol

💡 Click "edited" above to see earlier reviews for this PR.


Review Summary

Overall Risk: NONE

No concrete security, correctness, or reliability regressions found in the authorized PR range.

Findings

No concrete security, correctness, or reliability findings were identified.

Notes

  • Review used read-only inspection; builds and tests were not run.
  • Cronet dependency source was unavailable locally, limiting verification of native transport behavior.

Generated by Codex Security Review |
Requested by: @brow |
Workflow run

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 490ca3f4ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

debugPrint('Cronet is unavailable, using dart:io: ${error.message}');
return null;
}
return CronetClient.fromCronetEngine(engine);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve streaming uploads when selecting the Android transport

For Android HTTPS media uploads, this also replaces the streaming transport used by MediaUploadService._sendUploadRequest. However, CronetClient.send in cronet_http 1.10.1 drains the entire request with await stream.toBytes() and copies it into a Java buffer before starting the network request. Consequently, the composer's upload progress reaches 100% before transmission begins, and supported 100 MB attachments require additional full-body allocations that can cause memory failures on constrained devices. Keep streamed uploads on dart:io or use a genuinely streaming Cronet upload implementation. Validate backpressure through the selected production transport: the existing test uses _AbortRecorder, so it cannot detect this regression.

AGENTS.md reference: AGENTS.md:L243-L247

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Fixed in 96aff92. On Android, streamed requests with a body (media and voice note uploads) now keep the handle's dart:io client, so upload progress and memory behave as before; streamed GETs and buffered requests still use Cronet. A new test covers the routing.

CronetClient reads a request body into memory before sending it, so
upload progress would reach 100% before transmission and large
attachments would be copied in full. Streamed requests with a body keep
the handle's dart:io client on Android.

Signed-off-by: Tom Brow <[email protected]>
@brow
brow force-pushed the mobile-http2-android branch from 490ca3f to 96aff92 Compare November 13, 2026 19:17
@brow
brow deployed to codex-review November 13, 2026 19:17 — with GitHub Actions Active
@github-actions github-actions Bot added codex-security-review-current The posted Codex security review matches its recorded range. and removed codex-security-review-current The posted Codex security review matches its recorded range. labels Nov 13, 2026

@kazemek21 kazemek21 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kyle reviewed this PR and the Nexus review-tracker notes and approved with no comments.

Posted by Codex (gpt-6.1-sol) on behalf of kylez

@brow
brow merged commit d1a8c36 into main Nov 13, 2026
53 checks passed
@brow
brow deleted the mobile-http2-android branch November 13, 2026 21:34

This branch was successfully deployed

1 active deployment
codex-review — 96aff923 Deployed Nov 13, 2026 by brow via Run Codex Security Review #15018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex-security-review-current The posted Codex security review matches its recorded range.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants