Repository navigation
feat(mobile): restore scoped connections after Activity recreation - #12247
Conversation
|
Native E2E verification for
Modern instrumentation used Reproduction command from Debug APK SHA-256: The initial verification found an invalid test setup that reinserted a retired browser ID. It was corrected into independent deletion/origin-change fixtures; the single synthetic overlap was repaired with an encrypted backup while preserving other profiles. No app-data reset or product bypass was used. Final results above are from corrected fixtures; the independent baseline reproduction remained unchanged. Two clean self-audit passes and a separate code review found no actionable issue in the final commit. Scope: native fixtures, not production daemon/H5 or a real voice provider. Actual OS process-kill/relaunch, physical-device and TalkBack acceptance are not claimed. Unsent drafts and native operations are intentionally not restored. The global CLI cannot exercise this Android feature. Full Phase 2 still depends on per-device server credentials/revocation and background-notification work; this PR does not claim to complete those. 中文:此提交在 Windows/Java17/Gradle8.2.1 下构建通过,JVM 25 项通过,lint 0 错误、6 项既有依赖提示。API36 完整原生套件 15 通过/1 条件跳过,包含全部 8 项恢复场景;API26 为 5 通过/11 能力跳过,另通过真实 Connect 界面验证旧提供程序拒绝创建 WebView。独立基线测试在旧实现失败,在本实现不修改测试即可通过。验证了真实 Activity 重建、真实渲染进程终止、重建后仍需明确 Retry、从当前存储重新注入凭据、同源配置隔离及重命名/删除/地址和令牌轮换。初次测试错误地重用已退役浏览器 ID,现已拆分独立夹具;仅修复单个测试条目并保留加密备份与其他配置,没有清空应用数据或绕过产品检查。最终提交经过两轮干净自审及独立审查。以上不代表生产 H5、真实语音服务、系统杀进程后恢复、真机或 TalkBack 验收;未发送草稿和原生操作不恢复。逐设备服务端凭据撤销与后台通知仍是独立 Phase 2 工作。 Final test-only follow-up 中文补充:最终测试提交修复独立复现的空闲预连接导致夹具停止问题,新增存活回归;相同独立诊断及全部16项原生测试已重跑。较早重命名超时没有记录当时 socket 原因,因此两者关系仍是推断,产品恢复代码未变。按 CI 要求正常合并上游 |
# Conflicts: # packages/mobile-shell/README.md # packages/mobile-shell/app/src/main/java/com/qwen/mobileshell/MainActivity.kt # packages/mobile-shell/app/src/main/res/values/strings.xml
|
October 1: the current head is Current-head upstream CI update (September 29): native build/unit/lint and Android API 26/36 passed, together with Lint & Static, no-AK integration, desktop-shell checks and Web Shell smoke. The Ubuntu core suite has one failed cold-scan latency assertion and 33,155 passing tests. This is a real assertion failure, distinct from the old RPC timeout; the core tree matches main. Author-side rerun was denied for lack of upstream rights. Exact logs and maintainer rerun request. 9月29日当前提交上游 CI:原生构建/单测/lint、Android API26/36、静态检查、no-AK 集成、桌面和 Web Shell smoke 均通过。Ubuntu core 套件一项冷扫描延迟断言失败,33,155项通过;这是实际断言失败,与旧 RPC 超时不同,core 子树与 main 相同。作者因缺少上游权限无法重跑。精确日志及维护者重跑请求。 September 29 update — current head Merged current main Local Windows/JDK17: debug, unsigned release and instrumentation APKs compile; 25 JVM tests and lint pass. No new local device acceptance is claimed. Current-head CI must rerun; the earlier Ubuntu RPC timeout and precheck flag described below belong to the previous head. The request for maintainer triage of the negative security-fixture precheck remains open. Please evaluate this updated integration when that gate is cleared; no bypass or false security acknowledgement has been added. 中文:已合并当前 main,保留连接恢复及 #12126 文件选择器的独立状态、导航/销毁取消和失败恢复逻辑,普通前向推送保留历史。三类 APK 编译、25 项 JVM 和 lint 通过,不声称新的本地设备验收。需当前提交 CI;下方旧 Ubuntu RPC 超时和 precheck 属于前一提交。负面安全夹具的人工 precheck 分流请求仍待维护者处理,未绕过门槛。 Earlier verification, tied to its original commitsThe Ubuntu The precheck's |
…-recovery # Conflicts: # packages/mobile-shell/README.md # packages/mobile-shell/app/src/main/java/com/qwen/mobileshell/MainActivity.kt
Maintainer verification: PR #12247 — feat(mobile): restore scoped connections after Activity recreationVerdict: 中文摘要
Central claim and A/B proofClaim: after Android recreates the Activity, the app restores the selected connection and its validated session/workspace route in a fresh WebView, with credentials re-read from the encrypted vault; a cold launch without saved state must not restore. Method: one host-driven scenario executed identically against both builds on the same emulator — add a loopback profile (origin
Witness: Gates (both arms, author's own command set)
The skip on both arms is Vacuity: mutation matrix (11 rows, one mutation at a time, restore + clean-tree check between rows)JVM rows gate
Witness: Sibling-shape sweep (scratch probe, not part of the PR)Beyond the committed fixtures, a scratch JUnit probe ( FindingsTwo coverage gaps, both completeness notes rather than defects; neither is load-bearing for the central claim:
No correctness, security, or credential-hygiene findings: the saved Bundle carries only UUID-validated profile/browser IDs, an allowlisted navigation triple, and the retry flag (asserted by the committed Parcel-marshall scan, which this round re-ran); malformed or foreign saved state fails closed to the Connections list; token/origin rotation and deletion invalidate restoration because Precheck flag: the Not covered
MethodologymacOS 15.7.7 arm64; Temurin JDK 17.0.20.1; Android SDK (platforms;android-34, build-tools 34.0.0, emulator 37.3.2) assembled in a scratch toolchain dir; Gradle 8.2.1 zip SHA-256 verified against the PR-pinned value before seeding the wrapper cache. Emulator: API 36 Evidence captures
Local maintainer verification round by @wenshao — full harness scripts, raw logs and JUnit XML retained in |
|
@qwen-code /triage |
qqqys
left a comment
There was a problem hiding this comment.
Approved at 5dd0e3e2. No blocking issue: I read the production surface myself, and the two independent verification reports filed against this same commit both find the behaviour correct, with the only standing item being a deliberate UX simplification rather than a defect.
What I read
The feature's whole trust decision lives in two small new classes, and both fail closed:
ConnectionNavigation.fromFieldsaccepts a session or workspace only against[A-Za-z0-9_-]{1,128}and a context only from a two-value allowlist, returning null otherwise.capturebounds the URL to 8192 characters, requiresOriginPolicy.isSameOrigin, accepts only an empty path,/, or/session/<id>, and rejects unknown query keys and duplicated keys by falling back to an empty navigation. So a hostile or merely odd visited URL degrades to "restore the connection with no deep route", never to an injected one.ConnectionRecoveryholds no credential and no origin — its own comment states both must come from a fresh vault read — andfromBundlere-validates the profile and browser IDs as UUIDs and re-runs the navigation validation, so a corrupt or tampered saved state is rejected rather than trusted.
In MainActivity, the ordering is right at the two seams that matter. onRenderProcessGone and showConnectionError both snapshot recovery before calling destroyConnection(), which now nulls it, so the snapshot survives the teardown; destroyConnection nulling it is also what makes returning to the Connections list safe. showConnectionError additionally gained if (view !== webView) return, so an error from a stale view can no longer tear down the live connection — strictly stronger than the base behaviour it replaces. onSaveInstanceState re-captures only when the active view's URL is same-origin with the profile origin, and writes exactly the six validated fields alongside the pre-existing file-picker-in-flight flag.
The four removed strings have no remaining references, and the replacement copy states plainly that unsent text is not restored.
The one item that stands
Collapsing "Cannot reach Qwen Code" plus its address-and-certificate hint, and "WebView stopped", into a single "Connection interrupted" screen means a user can no longer tell a dead daemon from a dead renderer. No machine-readable field carried the cause on either side, so nothing observable to code regressed, and the single explicit-retry screen is what the design doc describes. That is a product decision, and the maintainer approved this head after it; I am not restating it as a finding.
Coverage gaps worth keeping visible
The sandboxed verification at this commit reports 152/152 assertions passing and proves the central claim load-bearing by A/B against the base — the restored route is byte-identical, and the base loses all navigation context. Its verdict is findings rather than pass purely on coverage: the 8192 cap and the non-/session/ path fallback are load-bearing but unpinned by any committed test (their mutants survive the JVM suite while the harness catches them), ConnectionRecovery has no JVM test at all, and the two UUID regexes are redundant defence. A missing test is not a blocking defect here, because the guards those mutants target were each driven and observed correct, but the gaps are real and would be cheap to close — the device test in this PR cannot cover the JVM-side parsing.
The same report disproves eight plausible defects by measurement, including the two I would have raised: url() concatenating origin + "session/…" without a separator is safe because canonicalRoot() always builds the origin with a trailing slash and the vault re-validates it, measured across six origin shapes; and the uncapped isSameOrigin call in doUpdateVisitedHistory is not a denial-of-service surface, measured flat at 0.46 ms against 100,000-character hostile inputs.
Coverage statement: I read both new classes in full, every MainActivity hunk, and the string resources. I did not read the 323-line device test or the navigation unit test line by line, and no emulator lane runs the device test here.
CI at this head: 15 checks pass, none failing and none pending.
Head branch was pushed to by a user without write access
|
October 3 integration follow-up: @wenshao @qqqys thank you for the independent verification and approvals. The merge-conflict demon made one more appearance after #12127 landed; I have integrated current main One integration guard is important: a microphone-authorized connection must not be saved for automatic Activity recovery. I explicitly exclude it from saved recovery state, preserving the accepted return-to-Connections policy. Text-only connections still restore; connection/renderer failures still offer explicit Retry after destroying the old view. The new regression uses a persisted fixture profile and real WebView setup, checks that no recovery Bundle is saved for an authorized microphone connection, and checks Connections after recreation. Both design languages and README state the same contract. At this head, debug, unsigned release, and instrumentation APKs compile; all 25 JVM tests pass; Android lint passes. An independent source audit confirmed the picker/microphone cancellation and late-result reservations are retained. The new device regression has been compiled, not executed locally: no emulator or physical device is connected. Please run/review fresh upstream device checks before merging. I have read the sandbox report as coverage feedback, not a new production defect: the navigation-length/path and recovery-serialization Suggestions remain explicit follow-up work, while the simplified interrupted-connection screen retains the accepted product decision. I will not call the interrupted triage workflow a pass. Current head: Current-head CI update: Android Mobile Shell has now passed, including native build/unit/lint and the API 26/API 36 device jobs. This is upstream device evidence on 中文说明当前 head 的 CI 更新:Android Mobile Shell 已通过,包括原生构建/单元测试/lint 及 API 26/API 36 设备任务。这是 |
Maintainer verification round 2: PR #12247 — feat(mobile): restore scoped connections after Activity recreationVerdict: This is a follow-up round: the head moved from 中文摘要
Previous-finding status (carried from the 2026-10-02 rounds, re-measured at
|
| # | Finding (source round) | Severity | Status at new head |
|---|---|---|---|
| 1 | 8192-char URL cap boundary unpinned (local round M6 / CI round F2-M4) | coverage gap | Stands — re-measured: mutant J3 (> 8192 → > 8191) survived the JVM suite again (ConnectionNavigationTest byte-identical to the verified old head) |
| 2 | Connection-error path's preserved route unpinned (local round D4) | coverage gap | Stands — re-measured: mutant D4 (drop the captured route in showConnectionError) went OK (10 tests) again |
| 3 | ConnectionRecovery has zero JVM coverage; guards observable only on device (CI round F1) |
coverage gap | Stands — grep ConnectionRecovery app/src/test/ still empty; mutants R1/R2 were again killed only by the device suite |
| 4 | Non-/session/ path fallback unpinned; "${origin}settings" fixture cannot distinguish it (CI round F2-M8) |
coverage gap | Stands — mutant J4 (else -> return root → else -> null) survived again |
| 5 | UUID regexes in fromBundle are redundant defence behind ProfileVault.decode() (CI round F3) |
informational | Stands — input closure (ConnectionRecovery.kt, ProfileVault.kt) byte-identical to the verified old head |
| 6 | Per-commit attribution out of reach in the shallow CI checkout (CI round F4) | scope note | Superseded — local full history: 11 commits base..head, all reachable; the aggregate diff was verified |
| 7 | Two failure messages collapsed into one (CI round, "stands — appears intentional") | cosmetic UX note | Stands — head still replaces connection_failed/renderer_stopped with the single connection_interrupted; consistent with the design doc's single explicit-Retry intent |
| 8 | PR body cited stale commit OIDs (CI round Corrections) | doc correction | Fixed — the body now cites f521dfd7 and 1a5aae80d9; both match the live refs |
Delta since the last verified head
git diff 5dd0e3e2..f521dfd7 on the PR surface: MainActivity.kt gains the microphone plumbing from merged #12127 plus this PR's new guard in onSaveInstanceState (if (microphoneAuthorized) recovery = null); ConnectionRecovery.kt, ConnectionNavigation.kt and ConnectionNavigationTest.kt are byte-identical to the previously verified head; ConnectionRecoveryDeviceTest.kt gains microphoneAuthorizedConnectionIsNotSavedOrAutomaticallyRestored; docs/README record the exclusion in both languages (section structure matches). The effective PR diff against the new base remains 10 files, +627/−25.
Central claim and A/B proof (re-measured)
Claim: after Android recreates the Activity, a text-only connection resumes in a fresh WebView on its validated session/workspace route with credentials re-read from the vault; a microphone-authorized connection is excluded; a cold launch without saved state does not restore.
Method: one host-driven scenario per arm on the same API 36 emulator — add a loopback profile (http://127.0.0.1:18080, synthetic token) through the native UI, connect, history.replaceState to /session/ab-cd_12?workspace=wk_34&context=live via raw CDP, then Home → am kill (task and saved instance state survive, process dies) → relaunch. This exercises the onCreate(bundle) restore path on a real WebView, the path the committed device tests approximate with ActivityScenario.recreate() — and it is the process-death case the PR's own e2e plan lists as distinct from recreation.
| Cell | Before recreation | After recreation |
|---|---|---|
base 1a5aae80 (11/11 assertions) |
fixture loaded, vault token attached, route set | Connections list, no WebView — the bug signature |
head f521dfd7 (15/15 assertions) |
identical | fresh WebView, route byte-identical, token re-attached from a fresh vault read |
| head cold-launch control | — | am force-stop + relaunch → Connections list, no spurious restore |
Both arms were driven twice (second pass for screenshots after a harness capture fix) with identical 11/11 and 15/15 results. Witness: 01-ab-base-recreation.png, 02-ab-head-restored.png; device screens 03/04/05.
New delta proof: microphone exclusion is load-bearing and pinned
- The committed regression test
microphoneAuthorizedConnectionIsNotSavedOrAutomaticallyRestoredexecuted and passed in the head device suite (status 0, not a capability skip). - Mutation MIC (delete
if (microphoneAuthorized) { recovery = null }inonSaveInstanceState) turned exactly that test red — the new guard is load-bearing and its test is non-vacuous. - Sibling probe (scratch, since removed): microphone-authorized connection → renderer terminated → recreation. Measured outcome: the explicit Retry screen survives (
retry=true connections=false, no WebView auto-restore). This matches the design text: renderer death destroys the connection and clears the microphone flag before the Retry screen, so what survives is an explicit-choice screen, not an automatic recovery.
Gates (both arms, author's own command set)
./gradlew --no-daemon :app:assembleDebug :app:assembleRelease :app:testDebugUnitTest :app:lintDebug :app:assembleDebugAndroidTest
| Gate | base 1a5aae80 |
head f521dfd7 |
|---|---|---|
| Build (debug + unsigned release + androidTest APK) | success | success |
| JVM unit tests | 21/21 | 25/25 (= 21 + 4 ConnectionNavigationTest) |
| lintDebug | 0 errors (7 warnings) | 0 errors (7 warnings) |
Device suite (API 36, WebView 133.0.6943.137, requireProfileIsolation=true) |
41 pass / 1 capability skip (42) | 51 pass / 1 capability skip (52 = 42 + 10 recovery tests) |
| Pristine control after the 13-row matrix | — | OK (52 tests), 25/25 JVM, clean tree |
The skip on both arms is the pre-existing oldProviderFailsClosedBeforeCreatingNamedProfileOrWebView (this image's WebView is above the floor it tests). Lint liveness was proven by planting an API-29 call (webViewRenderProcess, minSdk 26): lintDebug failed at the planted line 455, then the tree was restored. The Gradle wrapper jar's SHA-256 matches the committed gradle-wrapper.jar.sha256 on both arms; the PR touches no build/launcher/CI files. Witness: 07-device-suites.png.
Vacuity: mutation matrix (13 rows, restore + clean-tree check between rows)
| Row | Mutation (file) | Expected | Observed | Killed by / classification |
|---|---|---|---|---|
| J1 | identifier regex allows dots (Navigation) | killed | killed | untrustedOrUnsupportedNavigationFallsBackToRoot |
| J2 | context allowlist neutralized (Navigation) | killed | killed | savedFieldsAreValidatedBeforeReconstruction |
| J3 | URL cap 8192→8191 (Navigation) | survive | survived | coverage gap (carried #1) |
| J4 | non-/session/ path falls through to query parsing (Navigation) |
survive | survived | coverage gap (carried #4) |
| J5 | positive control: url() drops the query (Navigation) |
killed | killed | retainsOnlySupportedNavigation |
| R1 | fromBundle skips navigation validation (Recovery) |
killed | killed | malformedSavedIdentityOrRouteIsRejected (session=s#token=secret accepted → red) |
| R2 | findProfile drops the browserId conjunct (Recovery) |
killed | killed | renameRestoresButCredentialRotationDoesNot |
| D1 | restore bypassed, list always shown (MainActivity) | killed | killed | recreation tests (Connection did not create a WebView) |
| D2 | saved-state bundle write removed (MainActivity) | killed | killed | recreation tests |
| D3 | doUpdateVisitedHistory tracking removed (MainActivity) |
killed | killed | terminatedRendererRequiresRetryEvenAfterRecreation |
| D4 | connection error loses the captured route (MainActivity) | survive | survived | coverage gap (carried #2) |
| D5 | renderer death auto-reconnects instead of explicit Retry (MainActivity) | killed | killed | terminatedRendererRequiresRetryEvenAfterRecreation (device-side run finished 10 tests/1 failed; host adb stream stalled and the device logcat supplied the tally) |
| MIC | mic exclusion guard removed (MainActivity) | killed | killed | microphoneAuthorizedConnectionIsNotSavedOrAutomaticallyRestored |
Every kill was verified to fail on the intended assertion (quoted messages in the per-row logs), not on compile or fixture errors; J5 proves the JVM gate can go red; the device rows' kills prove the instrumentation gate live. The two survivors are the known carried coverage gaps — completeness reporting, not merge conditions. Witness: 06-mutation-matrix.png.
Findings
No new defects. The two carried coverage gaps (#1/#2 in the status table) are unchanged and remain Suggestions: a one-line >8192 boundary fixture, a settings?workspace=w fixture entry, and a connection-error-after-success device fixture would pin them. ConnectionRecovery's lack of JVM tests (#3) is mitigated only on the API 36 CI lane; a pure fromFields factory (as sketched in the 2026-10-02 CI round) would make those guards JVM-testable. None of this blocks the central claim, which is proven load-bearing above.
Not covered
- API 26 lane not re-run locally (no x86 image provisioned here). It capability-skips every recovery test by design (WebView 58, no
MULTI_PROFILE); upstream CI'sKeystore and profiles (API 26)and(API 36)lanes are both green on this exact head, as is the Ubuntu core suite whose earlier cold-scan failure the body reported. - Physical devices, TalkBack, production credentials, microphone+download combined behavior — per the PR's own stated scope.
- The reviewer-plan step "open the file picker, recreate, and return its old result" was not driven end-to-end; the file-picker code is untouched by this diff and its 17 device tests pass on both arms. Reservation semantics were verified by reading only.
- Repo-wide TS gates not run locally: the diff touches no TypeScript surface; upstream CI covers them green at this head.
- Per-commit attribution of the pre-
5dd0e3e2history was not re-exercised commit-by-commit (the previous round verified the same aggregate content); the delta since then is exactly the two commits analyzed above.
Methodology
macOS 15.7.7 arm64; Temurin JDK 17.0.20.1; scratch Android SDK (platforms;android-34, build-tools 34.0.0, emulator 37.3.2) in ~/qwen-verify-toolchain; Gradle 8.2.1 via the PR-pinned wrapper (SHA-256 verified). Emulator: API 36 google_apis;arm64-v8a, WebView 133.0.6943.137, private adb server (port 6037), emulator port 5600, animations disabled. Both arms built from clean git worktrees of f521dfd7 and 1a5aae80 with byte-identical Aliyun mirror routing via init script in the scratch GRADLE_USER_HOME (host cannot reach maven.google.com). The A/B harness (harness/ab-drive.mjs) drives the native UI by uiautomator bounds, the WebView by raw CDP over webview_devtools_remote, and the loopback fixture is a real Node HTTP server reached through adb reverse. The matrix runner applies each mutant by exact single-occurrence replacement (aborting the row otherwise), restores by backup copy, and gates on git status --porcelain being empty between rows. Harness scripts, raw logs (per-row matrix-*.log, device-*.log, build-*.log, lint-liveness.log), JUnit XML and all evidence images live in tmp/pr12247-verify-20261004-211527/. Assertion accounting: A/B harness 26 + JVM tests 46 + device tests executed 143 (51 + 41 + 51 pristine; assumption skips not counted) + sibling probe 1 + mutation rows 13 + lint-liveness 1 + wrapper-hash 2 = 231 pass / 0 fail.
Evidence captures
-
01-ab-base-recreation.png — base A/B terminal log: fixture connected, route set, recreation loses the connection
-
02-ab-head-restored.png — head A/B terminal log: recreation restores route + vault token; cold launch clean
-
03-base-after-recreation-connections.png — base device screen after recreation: Connections list (bug signature)
-
04-head-after-recreation-restored.png — head device screen after recreation: fresh WebView on the restored route
-
05-head-cold-launch-connections.png — head cold-launch control: no spurious restore
-
06-mutation-matrix.png — 13-row matrix as printed (11 kills, 2 carried-gap survivors)
-
07-device-suites.png — device/JVM/lint gate tallies, both arms + pristine control
Local maintainer verification round 2 by @wenshao — full harness scripts, raw logs and JUnit XML retained in tmp/pr12247-verify-20261004-211527/ of the working repo.
|
Thanks @wenshao for the second independent A/B and device verification on I will continue maintaining the Android shell. The navigation-boundary, connection-error route and recovery-serialization coverage suggestions remain follow-up work, consistent with the scope already agreed here; I will pick those up after this recovery change lands. Your merge-ready verdict and the current-head API 26/API 36 results are useful evidence for the final maintainer review. |
|
@qwen-code /triage |















What this PR does
Restores the selected text-only connection and validated session/workspace route in a fresh WebView after Activity recreation, and preserves an explicit retry screen after renderer or connection failure. This revision merges current main while retaining both recovery state and the accepted file-picker result reservation.
Current head:
f521dfd7d8c831240180b1231acdd686c1088bd0. Based on main1a5aae80d9; #11722, #12121 and #12126 are merged. This remains an independently reviewable Phase 2 slice.Why it's needed
Activity recreation previously returned to the connection list and lost navigation context. Recovery must reread the encrypted profile store rather than persist credentials or revive stale native callbacks.
Reviewer Test Plan
How to verify
Recreate an active connection and confirm its supported session/workspace route resumes in a fresh WebView. Recreate a retry screen and confirm it still requires explicit retry. Deleting or changing the profile must invalidate restoration. Inspect saved state for absence of credentials and arbitrary URLs. Open the file picker, recreate, and return its old result: it must never attach to the replacement document; a later file operation should work.
Evidence (Before & After)
The current integration preserves the recovery route/history hooks and file-picker navigation, error, destruction and saved-result hooks. Local JVM tests and APK/lint checks pass. Prior device evidence in the PR remains tied to its original commits; no device run on this integration is claimed. The previous Ubuntu test failure was a Vitest worker/RPC timeout after 33,380 passed tests, not an assertion failure; this revision needs fresh upstream CI and the already-requested maintainer triage of the precheck flag.
Tested on
Windows: debug, unsigned release and instrumentation APKs compile; 25 JVM tests and Android lint pass on this revision. No new local emulator/physical-device run, macOS or Linux execution is claimed.
Current-head upstream CI and any device checks are reported separately in GitHub; previous-head green checks are not evidence for this revision.
Current-head upstream CI update (September 29): native build/unit/lint and Android API 26/36 passed, together with Lint & Static, no-AK integration, desktop-shell checks and Web Shell smoke. The Ubuntu core suite has one failed cold-scan latency assertion and 33,155 passing tests. This is a real assertion failure, distinct from the old RPC timeout; the core tree matches main. Author-side rerun was denied for lack of upstream rights. Exact logs and maintainer rerun request.
Environment
Windows, JDK 17, Gradle 8.2.1, compile SDK 34. Native checks:
assembleDebug assembleRelease assembleDebugAndroidTest testDebugUnitTest lintDebug.Risk & Scope
Unsent text and pending native operations are not resumed; a cold launch without saved state starts at Connections. Real devices, production credentials and combined microphone/download behavior remain separate acceptance work. A precheck flag on negative security fixtures requires maintainer review; this PR does not bypass that gate.
Design: English / 简体中文.
Linked Issues
Refs #11704, #11722, #12121, #12126.
中文说明
本 PR 的改动
Activity 重建后在新的 WebView 恢复选定纯文本连接及验证过的会话/工作区路由;渲染器或连接失败后保留明确的重试页。本次合并当前 main,同时保留恢复状态和已接受文件选择器的结果占用状态。
当前提交:
f521dfd7d8c831240180b1231acdd686c1088bd0。基于 main1a5aae80d9;#11722、#12121 和 #12126 已合并。这仍是可独立审阅的 Phase 2 切片。为什么需要
此前 Activity 重建会返回连接列表并丢失导航上下文。恢复必须重新读取加密配置存储,不得持久化凭据或恢复过期原生回调。
审阅者测试计划
验证方法
重建活动连接,确认在新的 WebView 恢复支持的会话/工作区路由。重建重试页仍应要求明确重试。删除或更改配置应使恢复失效。检查保存状态不含凭据及任意 URL。打开文件选择器后重建并返回旧结果,旧结果不得关联新文档;后续文件操作应可用。
前后证据
当前集成保留恢复路由/历史钩子,以及文件选择器的导航、错误、销毁和结果保存钩子。本地 JVM、APK 构建和 lint 通过。PR 先前设备证据仍仅对应各自提交,不声称在本次集成运行过设备测试。旧 Ubuntu 失败发生于 33,380 项通过后的 Vitest worker/RPC 超时,并非断言失败;本次需要新的上游 CI,以及已请求的维护者 precheck 人工分流。
测试平台
Windows:本次 debug、未签名 release 和 instrumentation APK 编译成功;25 项 JVM 测试及 Android lint 通过。不声称完成新的本地模拟器/真机、macOS 或 Linux 运行。
当前提交的上游 CI 和设备检查由 GitHub 单独报告;前一提交的绿色检查不作为本次证据。
9月29日当前提交上游 CI:原生构建/单测/lint、Android API26/36、静态检查、no-AK 集成、桌面和 Web Shell smoke 均通过。Ubuntu core 套件一项冷扫描延迟断言失败,33,155项通过;这是实际断言失败,与旧 RPC 超时不同,core 子树与 main 相同。作者因缺少上游权限无法重跑。精确日志及维护者重跑请求。
环境
Windows、JDK17、Gradle8.2.1、compile SDK34。原生检查:
assembleDebug assembleRelease assembleDebugAndroidTest testDebugUnitTest lintDebug。风险与范围
不恢复未发送文本和待处理原生操作;没有保存状态的冷启动进入连接列表。真机、生产凭据及组合麦克风/下载行为另行验收。负面安全夹具上的 precheck 标记需要维护者审阅,本 PR 不绕过该门槛。
设计:English / 简体中文。
关联问题
关联 #11704、#11722、#12121、#12126。
October 3 integration status
Current main
1a5aae80d9is integrated after #12127 merged. Microphone-authorized connections are excluded from saved automatic recovery and return to Connections on recreation; text-only recovery and explicit Retry remain. A regression uses a persisted fixture profile and real WebView setup to check absent recovery state and Connections after recreation. README and both design languages match. On Windows/JDK 17/SDK 34, debug, unsigned release and instrumentation APK builds, 25 JVM tests and Android lint pass. The new device test is compiled, not executed locally; fresh upstream device/CI checks and review remain required. The sandbox's navigation/recovery coverage Suggestions are recorded in #13111, not claimed as production defects or silently marked fixed. The interrupted triage workflow is not a pass.中文:#12127 合入后,本次集成 main
1a5aae80d9。已授权麦克风的连接不写入自动恢复状态,Activity 重建后返回 Connections;纯文本恢复及明确 Retry 保留。回归测试使用已持久化夹具配置和真实 WebView 初始化,检查恢复状态缺失及重建后 Connections。README 和双语设计一致。Windows/JDK 17/SDK 34 上 debug、未签名 release、instrumentation APK 构建、25 项 JVM 测试和 Android lint 通过。新设备测试仅编译,未在本地执行;仍需当前提交的上游设备/CI 检查及审查。沙箱报告的导航/恢复覆盖 Suggestions 记录在 #13111,不称为生产缺陷或静默标记已修复。中断的 triage 工作流不算通过。