You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// InstallerLaunched is a handoff; Updated is reported only by reconciliation.
115
+
}
116
+
```
117
+
118
+
| Operation | Contract |
119
+
|---|---|
120
+
|`RunAsync(currentVersion, ct)`| Check → download/verify → persist intent → launch installer. Existing pending state returns `PendingUpdateExists` without another install. |
121
+
|`ReconcileAsync(currentVersion, ct)`| Offline startup check. Installed version equal to or newer than the pending target returns `Updated`; the old version remains `AwaitingInstallation` or `RecoveryRequired`. No intent returns `NoPendingUpdate`. |
122
+
|`RetryAsync(currentVersion, ct)`| Explicit recovery of a pending attempt: reconcile first, then rediscover and verify the same target. A changed server target returns `RecoveryRequired` without overwriting the earlier handoff. Never installs a persisted path or reuses stored credentials. Use `RunAsync` again after failures that left no pending intent. |
123
+
|`AbandonAsync(ct)`| Explicitly forget pending tracking, including corrupt state. Does **not** cancel Android installation, delete APKs, or roll back the app/data. |
124
+
125
+
`StateChanged` reports named stages and a terminal outcome, rather than treating a generic “completed” notification as
126
+
installation success. Pass an `IUpdateEventDispatcher` to `CreateCoordinator` for Avalonia UI dispatch, as shown below.
127
+
Check `Outcome` and `FailureReason`: `InstallerLaunched`, `NoUpdate` and `Updated` have different meanings.
128
+
Subscribe to `AddListenerDownloadProgressChanged` for byte/speed progress and register `AddListenerUpdatePrecheck` before
129
+
starting operations for optional-update policy (`true` still means skip; forced updates bypass it). These are forwarded
130
+
through the coordinator, so factory users do not need access to its underlying bootstrap.
131
+
The legacy `CreateDefault` / `IAndroidBootstrap` API remains available and unchanged.
132
+
133
+
The factory stores only a versioned attempt ID, original/target versions, timestamp and handoff phase in
134
+
`<NoBackupFilesDir>/generalupdate/pending-update.json`. No APK path, URL, package credentials or exception is serialized.
135
+
The file is atomically replaced from a flushed temporary file in the same directory. Corrupt, oversized or unknown-schema state
136
+
fails closed instead of silently starting another update. A write failure before handoff prevents installer launch; uncertainty
137
+
after handoff remains pending for reconciliation. This protects process-restart recovery, not arbitrary storage hardware failure.
138
+
For a custom location/store, pass `pendingStore: new JsonPendingUpdateStore(privatePersistentPath)` (services namespace);
139
+
do not place the record in a cache, shared downloads folder or backup-restored location.
140
+
The default JSON store holds an exclusive `.lock` file lease for the entire workflow, preventing cooperating coordinator
141
+
instances/processes using the same state path from overwriting each other's intent. Do not delete that lock file while in use.
142
+
Custom stores can implement `IPendingUpdateStoreLeaseProvider`; otherwise the host must enforce a single coordinator.
143
+
Separate state paths do not protect a shared APK staging directory, so use one coordinator per staging directory.
144
+
145
+
**Recovery policy:** reconcile on each app launch and when returning from the installer, using the actual installed version.
146
+
An unchanged version is not proof the user rejected installation—it may still be in progress. Offer explicit retry or abandonment;
147
+
do not automatically loop on either. If the server now offers a different target, reconcile the earlier handoff or explicitly abandon
148
+
its tracking before starting a new attempt. A successful reconciliation confirms the observed version, not application health or successful
149
+
data migration. Silent installation, automatic relaunch, OS downgrade/rollback, signed manifests and APK identity preflight are not
150
+
provided. The host still supplies installation permissions/FileProvider configuration and must validate device behavior.
151
+
94
152
### Server-Driven Version Validation
95
153
96
154
`ValidateAsync(currentVersion, cancellationToken)` only needs the version installed on the device: the component queries
@@ -234,12 +292,13 @@ Cancellation while waiting for the gate still throws `OperationCanceledException
234
292
cancellation during verification returns a canceled result. Notification exceptions are logged and isolated, whereas a pre-check
235
293
exception produces a failed validation result. Installed-version confirmation is still not part of disposal or a completed event.
236
294
237
-
Use a single host coordinator and a private staging directory for the full check → download/verify → install sequence.
295
+
Use a single coordinator and a private staging directory for the full check → download/verify → install sequence.
238
296
Only hand the returned verified path to the installer; do not modify or remove the APK while installation may be reading it.
239
297
The public installer method also supports independent calls, so it does not establish verification provenance for arbitrary paths.
240
-
Persist the target version before handoff, reconcile the actual installed version on next launch, and clear obsolete staging files
241
-
only when no update/installer is using them. Keep resumable partial files for a bounded retention period.
242
-
Permission prompting, actual installation outcome, app relaunch and recovery from a bad release or data migration remain host/platform
298
+
`CreateCoordinator` handles target-version persistence and next-launch reconciliation; call `ReconcileAsync` at startup with the
299
+
actual installed version. With the low-level API, implement that tracking in the host. Clear obsolete staging files only when no
300
+
update/installer is using them. Keep resumable partial files for a bounded retention period.
301
+
Permission prompting, app relaunch and recovery from a bad release or data migration remain host/platform
243
302
responsibilities; they are not made reliable merely by a successful installer intent.
244
303
245
304
## Source Review and Production Readiness
@@ -260,13 +319,20 @@ retained with revision-pinned evidence; **its defect descriptions refer to the p
260
319
| Callback errors | Synchronous notification subscriber, dispatcher and logger exceptions cannot replace operation outcomes. A throwing pre-check fails validation instead of bypassing host policy. |`BootstrapLifecycleTests`: throwing subscribers/loggers/dispatchers and fail-closed pre-check. |
261
320
| Package license | NuGet metadata now declares Apache-2.0, matching the existing LICENSE. | MSBuild property evaluation against LICENSE. |
262
321
263
-
Validation after remediation: **160/160 core tests passed** (no failures or skips), including HEAD-rejection fallback and
322
+
Validation of the earlier remediation: **160/160 core tests passed** (no failures or skips), including HEAD-rejection fallback and
264
323
authentication-policy failures returning terminal validation results before provider/network invocation.
265
-
The local Android build could not run because the `android` workload is missing (`NETSDK1147`); current PR CI requires approval.
324
+
At that stage the local Android build was blocked by the missing `android` workload (`NETSDK1147`).
266
325
Tests for these fixes use the existing .NET core test project; they are not Android device installation tests.
267
-
The fixes do **not** add desktop support, installer completion callbacks, automatic restart/rollback, independent manifest signing,
268
-
APK identity preflight, persisted workflow state, directory-wide coordination, or a runnable Avalonia sample.
269
-
UI dispatch, verified-path handoff, cache retention and next-launch reconciliation remain explicit host responsibilities described above.
326
+
The coordinator addition now provides persistent intent tracking, complete-attempt orchestration, offline installed-version
327
+
reconciliation, explicit retry/abandon recovery and stage-specific outcomes (see the new integration section).
328
+
Coordinator validation: **57 focused tests and all 217 core tests passed**. With the Android workload installed,
329
+
the Android library **Release build succeeded**, including the default factory. Coverage includes real HTTP downloader/storage/hash
330
+
integration with fake transport/installer, persistent state across recreated coordinators, uncertain handoff, concurrency and recovery.
331
+
The build retains the existing dependency advisory noted below and three XML-documentation warnings. PR CI still requires approval;
332
+
no device/emulator installation, restart or application-health validation was performed.
333
+
The fixes do **not** add desktop support, native installer completion callbacks, automatic restart/rollback, independent manifest signing,
334
+
APK identity preflight, or a runnable Avalonia sample.
335
+
UI dispatch, providing the actual installed version, invoking reconciliation at startup and cache retention remain host responsibilities.
270
336
Metadata-provider/storage extensibility and production dependency/device validation remain follow-up work, not silently resolved findings.
271
337
272
338
### Historical scope and evidence (before remediation)
@@ -373,7 +439,9 @@ Snapshots are in-memory and do not establish verified-package provenance or cras
373
439
shutdown and post-install reconciliation. The default logger is no-op; production hosts need stage/failure telemetry without credentials.
374
440
375
441
**Dependency risks:**[AndroidX Core is the runtime package dependency; SourceLink is private build tooling][review-project].
376
-
No dependency advisory audit or transitive inventory was performed for this assessment, so version age alone is not a vulnerability finding.
442
+
The original assessment did not include a dependency advisory audit or transitive inventory.
443
+
The subsequent coordinator build reports a pre-existing `Microsoft.Build.Tasks.Git` 8.0.0 advisory
444
+
([GHSA-23fw-v26w-5fgq](https://github.com/advisories/GHSA-23fw-v26w-5fgq)); this change does not update dependencies.
377
445
Validate the resolved dependency graph, Android workload/toolchain compatibility and packaged artifact on supported devices before release.
378
446
379
447
### Original validation evidence and remaining production release gates
0 commit comments