|
| 1 | +<!-- SPDX-License-Identifier: MIT --> |
| 2 | +<!-- Part of PyBLE (https://pyble.dev) — see /LICENSE. --> |
| 3 | + |
| 4 | +# Android recovery acceptance continuation |
| 5 | + |
| 6 | +| Field | Record | |
| 7 | +|---|---| |
| 8 | +| Date / operator | 31 October 2026; automated operator on the maintainer's Mac | |
| 9 | +| State | Completed Android continuation within the physical scope and limitations recorded below | |
| 10 | +| Physical device | Lenovo TB-J616X; Android 12 / API 31; landscape 2000 × 1200 pixels at 240 dpi | |
| 11 | +| App | `0.3.0 (9)`, normal `lib/main.dart`; isolated qualification application identity preserves the installed store app and its data | |
| 12 | +| Final runtime source | `01a7ac8acab32e926c185c8567f9b8dca08d3d68`; Android worktree runtime is byte-equivalent. The native package name alone is changed for the isolated identity | |
| 13 | +| Final profile APK SHA-256 | `afc50a741a07344047d3069c9d4a36c1111869b3286e67b0a9f8d4a61a408e89`; also verified against the installed `base.apk` | |
| 14 | +| Final arm64 AOT SHA-256 | `2ddb34eb36169466b5aa5aa32515e007354389f1ebdd276c0fdd40638e6e47a4` | |
| 15 | +| Final native DEX SHA-256 | `1b62aceb530a2d361980b18d009188994cf7c92118d22842af3a6293757b61bb` | |
| 16 | +| Original debug APK | Source equivalent to `6f5b3ea`; SHA-256 `957982f5acec969f8003d04b2d6444eecbba9af26b7392d760badb5be5d6bd0e`; original failures retained | |
| 17 | +| Earlier profile APK | Runtime equivalent to `e54a78a`; SHA-256 `5b93b0c68bfc7bfa06a3a887ab70192a4d47c4786cf82e5428005ef94b199861` | |
| 18 | +| Governing evidence | [Recovery contract](../../../specifications/App/current-draft-recovery.md#7-required-evidence-before-acceptance), [operator recipe](01_Operator_Recipe.md), [execution record](00_Execution_Record.md), [release matrix](../c1-s1/04_Release_Qualification_Matrix.md) | |
| 19 | +| Private evidence | Ignored `local/recovery-completion/android/`: exact artifact/source manifests, native assertions, fresh UI XML/screenshots, logs, initial isolated-data backup and installed-artifact hash | |
| 20 | + |
| 21 | +Both profile candidates were built from the normal application entry point. |
| 22 | +The final candidate was built after `flutter clean`, with the new Dart keyboard |
| 23 | +helper and native activity handler included. It is a qualification package; |
| 24 | +these observations do not independently establish Play delivery or production |
| 25 | +signing. The release record owns that separate packaging evidence. |
| 26 | + |
| 27 | +## Recovery and replacement observations |
| 28 | + |
| 29 | +The initial isolated Editor was empty. Its private data was backed up before |
| 30 | +qualification, and the store app was not replaced or cleared. Only disposable |
| 31 | +text and uniquely named board fixtures were used. No firmware was flashed by |
| 32 | +this Android workflow. |
| 33 | + |
| 34 | +| Recipe | Result / physical oracle | Evidence prefix | |
| 35 | +|---|---|---| |
| 36 | +| P01 | Pass on final candidate: actual Bluetooth state OFF and Wi-Fi disabled; startup presents the exact checkpoint before any connection workflow | `nativefix-all-offline-relaunch`, `nativefix-offline-bluetooth-confirmed` | |
| 37 | +| P02 | Pass: named `recovery.py` retains Unicode `✓`, punctuation, blank lines and final newline; native entry was asserted exactly before normal key-event editing | `nativefix-typing-seed`, `nativefix-continuous-expected` | |
| 38 | +| P03 | Pass: explicit recovery save acknowledges the matching text; Saved locally and board Unsaved changes remain distinct | `profile-saved-local`, `nativefix-continuous-saved` | |
| 39 | +| P04 | Pass: actual force-stop/relaunch reproduces exact named text and checkpoint, including the generated Blocks text | `nativefix-typed-offline-relaunch`, `nativefix-generated-relaunch` | |
| 40 | +| P05 | Pass: Back leaves the unresolved recovery decision; relaunch presents the same complete generated checkpoint | `nativefix-recovery-before-back`, `nativefix-recovery-after-back` | |
| 41 | +| P06 | Pass on final candidate: explicit Restore shows exact text in the disconnected local panel; connecting remains a separate action | `controlled-3dcb-local-restore` | |
| 42 | +| P07 | Pass for ordinary edit/lifecycle behavior: repeated normal key events, idle acknowledged checkpoint, backgrounding, force-stop and exact recovery | `nativefix-continuous-*`, `nativefix-all-offline-relaunch` | |
| 43 | +| P08 | Pass on final candidate: Cancel retains the exact copy; confirmed Discard commits the empty slot and another process launch has no recovery prompt | `controlled-3dcb-discard-*`, `controlled-3dcb-empty-slot` | |
| 44 | +| P09 | Pass on earlier profile: deliberately empty named `acceptance_1730.py` survives checkpoint/process relaunch as a recoverable document | `profile-empty-named-saved`, `profile-empty-named-prompt` | |
| 45 | +| P10 | Pass on earlier profile: Save and Run show their current destination and distinct write-only/write-and-run effects; Cancel preserves the document. Confirmed upload and subsequent Run produced `67891` | `profile-save-consent-dialog`, `profile-run-consent-dialog`, `profile-named-upload-consent`, `profile-hello-output-complete` | |
| 46 | +| P11 | Pass for physical moved-directory destination: Upload names the actual current directory; cancellation and confirmed upload were exercised | `profile-upload-cwd-consent`, `controlled-3dcb-cwd-upload-consent` | |
| 47 | +| P12 | Pass on final candidate: matching confirmed upload clears board dirty; a new connection session requires current-target consent again, which was cancelled | `controlled-3dcb-upload-ack`, `controlled-3dcb-new-session-consent` | |
| 48 | +| P13 | Pass on final candidate: Editor New, Board Open and Blocks handoff each protect meaningful pending Unicode text. Cancel retains it exactly; confirmed Blocks replacement installs the generated text | `nativefix-new-*`, `nativefix-board-open-*`, `nativefix-block-handoff-*` | |
| 49 | +| P14 / DR-T12 | Pass on final candidate: normal Hello example → Open in Editor → guarded replacement → Save recovery copy → Disconnect → force-stop/relaunch recovers exactly `print('Hello, PyBLE!')\n` as text | `nativefix-generated-checkpoint`, `nativefix-generated-relaunch` | |
| 50 | +| P15 | Verified physical scope: 2× recovery controls, Editor/Blocks keyboard layout, and native accessibility focus/labels with TalkBack active. Audible pronunciation and complete gesture navigation were not observed | `nativefix-named-2x-*`, `nativefix-blocks-ime-*`, `talkback-probe-*` | |
| 51 | + |
| 52 | +The generated-text recovery observation does not claim restoration of a Blocks |
| 53 | +workspace, revision history or companion files. The UI explicitly states that |
| 54 | +the local slot contains one current text draft. |
| 55 | + |
| 56 | +The retained earlier-profile observations cover unchanged storage, target-guard |
| 57 | +and transport code. The final runtime changes keyboard dismissal; its affected |
| 58 | +Editor/native-WebView behavior and Blocks handoff were repeated on the final |
| 59 | +installed artifact. This is an impact assessment, not a claim that every earlier |
| 60 | +observation was rerun on identical bytes. |
| 61 | + |
| 62 | +Deterministic transaction-phase interruption, storage failure/volatile fallback |
| 63 | +and stale-consent races remain covered by the automated fault/controller tests. |
| 64 | +The normal device UI does not provide a reliable way to force those internal |
| 65 | +phases. Their physical execution is not inferred from a successful ordinary |
| 66 | +restart. Error-panel localization/layout coverage also remains part of the |
| 67 | +host widget/semantics/golden evidence. |
| 68 | + |
| 69 | +## Physical app-only matrix |
| 70 | + |
| 71 | +Every passing core row covers edit, explicit Save, Run, observed stdout, Stop, |
| 72 | +clearing the Editor before opening the saved board file, exact readback, explicit |
| 73 | +disconnect/reconnect, and another readback. Program bytes and private action |
| 74 | +receipts are retained with each row. A Ready scan result alone is not counted. |
| 75 | + |
| 76 | +| Row | Exact profile / board alias | App candidate | Result | Raw prefix | |
| 77 | +|---|---|---|---|---| |
| 78 | +| Q02 | `esp32-4mb` / 8C9E | Final | Pass | `q8c9e` | |
| 79 | +| Q04 | `esp32-s3-n16r8` / 5646 | Final | Pass | `q5646` | |
| 80 | +| Q06 | `waveshare-esp32-s3-lcd-147b` / DA86 | Final | Pass | `qda86` | |
| 81 | +| Q08 | `esp32-c3-4mb` / C81A | Earlier profile; unchanged transport/run code | Pass, including both required real-UI Stop cases | `profile-c3-*`, `profile-tight-*`, `profile-flood-*` | |
| 82 | +| Q10 | `rpi-pico2-w` / 3DCB | Final | Pass after an explicitly retained Android-133 connection failure and retry | `q3dcb` | |
| 83 | + |
| 84 | +C3 ran both `while True:\n pass\n` and |
| 85 | +`while True:\n print(24680)\n`. The recorded UI changed from Running to |
| 86 | +Ready/Console Idle after Stop and remained connected. During the print flood, |
| 87 | +UIAutomator could not obtain an idle hierarchy; a direct live screenshot records |
| 88 | +Running and repeated output before the captured toolbar Stop action. The settled |
| 89 | +hierarchy then records Ready/Idle. No fresh wire-level latency bound or complete |
| 90 | +firmware C3 qualification is claimed. |
| 91 | + |
| 92 | +The [execution record](00_Execution_Record.md) owns the fresh immutable-program |
| 93 | +bindings and app-only scope decision. Classic ESP32 is bound to the published |
| 94 | +baseline; the two S3 boards, C3 and Pico 2 W are bound to the verified observer |
| 95 | +baseline `96497b9`. Their common HELLO version `0.6.1` is not treated as evidence |
| 96 | +of identical firmware bytes or of the published five-profile firmware gate. |
| 97 | + |
| 98 | +## Defects resolved and evidence limits |
| 99 | + |
| 100 | +| Finding | Original observation | Final observation | |
| 101 | +|---|---|---| |
| 102 | +| Landscape keyboard overflow | Original debug candidate hid the Editor and overflowed Files/navigation while IME was visible | Editor and navigation remain visible; console collapses honestly and expands after keyboard dismissal | |
| 103 | +| Native WebView keyboard remained open | Earlier profile: Blocks HTML field → Expand console retained the IME despite the successful semantic button action | Final profile: same action retains the field/workspace, expands console, and changes Android `mInputShown` from true to false | |
| 104 | +| Initial debug Blocks startup failure | Original debug retries and device/provider restart reached the startup watchdog; browser-side diagnostics alone did not establish a JS/CSP cause | Clean profile application initializes, loads/edits the example and completes real handoff/relaunch. Exact-assets native and pinned-plugin diagnostic apps also initialize. No speculative CSP/bootstrap change was made | |
| 105 | + |
| 106 | +Initial helper attempts that reused a stale hierarchy, replaced populated rich |
| 107 | +Editor text unreliably, or encountered an unrelated Android crash dialog were |
| 108 | +retained and excluded. Accepted readbacks first clear the Editor, and named-file |
| 109 | +input waits for the adopted Editor filename. The initial TalkBack gesture |
| 110 | +attempt is also excluded because service/interaction state was not controlled. |
| 111 | +A post-probe interval failed the following test's expected UI precondition and |
| 112 | +was excluded from target/discard acceptance; that complete sequence subsequently |
| 113 | +passed in a fresh uninterrupted run. No cause is inferred for those intervals. |
| 114 | + |
| 115 | +The later isolated accessibility probe used |
| 116 | +`FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES`. For Restore, Copy text and Discard |
| 117 | +recovery copy, it verified an exact visible/enabled native button, accepted |
| 118 | +accessibility-focus action, matching focus event and focused node. TalkBack |
| 119 | +remained bound before and after the probes. This verifies native focus and label |
| 120 | +exposure, not audible speech or complete screen-reader usability. Recorded |
| 121 | +accessibility-service, touch-exploration and font settings were restored in a |
| 122 | +`finally` step. Original radio settings were restored after offline checks. |
| 123 | +All board connections were explicitly released; temporary diagnostic apps, |
| 124 | +device helper files and the debugging forward were removed. The isolated |
| 125 | +qualification app/data and private evidence remain available for review. An |
| 126 | +exact ownership manifest records the disposable board fixtures for coordinated |
| 127 | +cleanup without touching other files. |
0 commit comments