Skip to content

Issuing: Run ProvisioningService off the UI dispatcher - #612

Draft
gp-iaik wants to merge 1 commit into
release/5.9.2-rcfrom
perf/provisioning-off-main
Draft

gp-iaik wants to merge 1 commit into
release/5.9.2-rcfrom
perf/provisioning-off-main

Conversation

@gp-iaik

@gp-iaik gp-iaik commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on release/5.9.2-rc.

The issuance steps ran on the dispatcher of their caller, i.e. the UI dispatcher. The stack trace of a failed token request shows HttpClientKt.execute resumed from AndroidUiDispatcher.performTrampolineDispatch. As a result:

  • signing DPoP proofs and client attestation PoPs, parsing metadata and responses, and validating credentials ran between frames;
  • every resumption after a request waited for a free frame.

Changes

  • Every public suspend function of ProvisioningService runs in offMain { }, i.e. withContext(Dispatchers.Default):

    • loadCredentialMetadata, parseCredentialMetadata, startProvisioningWithAuthRequest;
    • resumeWithAuthCode, refreshCredential, decodeCredentialOffer;
    • loadCredentialWithOffer, keepCredentialsUnderReview.

    This covers every caller, instead of changing each LaunchedEffect or view model.

  • Callbacks are safe off the main thread: onProgress is LoadingStatusService::set and statusUpdater is CredentialValidityService.updateStatus, both of which only set StateFlows.

  • Opening the browser is safe off the main thread: IntentService.openIntent calls openUrl, which uses the application context with FLAG_ACTIVITY_NEW_TASK on Android and dispatches to the main queue on iOS.

  • The cached clients, client ID, and attestation flag are @Volatile, as they are now accessed from several threads. Their check-then-act races are the same as before.

Checks: ran :androidApp:compileDebugKotlin, :shared:compileKotlinMetadata and :shared:testAndroidHostTest; all pass.

Not done: issuing on a device; iOS.

🤖 Generated with Claude Code

The issuance steps were called from the UI and ran on its dispatcher, e.g. resumeWithAuthCode was running in AndroidUiDispatcher according to a stack trace: signing DPoP proofs and attestations, parsing metadata and responses, and validating credentials ran between frames, and each resumption after a request waited for the UI thread.

Every public suspend function of ProvisioningService now runs on Dispatchers.Default. Its callbacks only set StateFlows, and opening the browser is safe off the main thread on Android (application context, new task) and dispatches to the main queue on iOS. The cached clients and configuration, now read and written from several threads, are volatile.
@gp-iaik
gp-iaik force-pushed the perf/provisioning-off-main branch from 89c1e81 to f7f1acf Compare October 30, 2026 10:07
@gp-iaik
gp-iaik changed the base branch from feature/launchpad-2026-post to release/5.9.2-rc October 30, 2026 10:07
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