Skip to content

[Feature]: Add a one-shot updater for already-provisioned Android radios #12

Description

@awatchar

Problem to solve

Android changes currently require connecting each supported radio and having a developer/Codex drive the update and verification interactively. Repeating the same ADB discovery, APK installation, model selection, provisioning, reboot, and validation steps consumes time and tokens and makes operator results less consistent.

Issue #6 covers first-time one-shot provisioning and release packaging. This issue is specifically for a reusable one-shot updater for already-provisioned devices.

An operator should be able to download a reviewed update bundle, connect each existing radio in turn, double-click one launcher, and receive a clear result without needing Codex, a source checkout, Gradle, Android Studio, or knowledge of model-specific commands.

Proposed artifacts

Add a distinct update entrypoint rather than overloading first-time provisioning:

Update Minimum Device.cmd
scripts/update-minimum-device.ps1

Include both in the standalone GitHub Release bundle alongside the signed APK, checksum, existing guarded model helpers, and operator documentation.

The existing Provision Minimum Device.cmd remains the workflow for a new/reset device. The new updater is for devices that already have Minimum identity and managed configuration.

Supported update targets

Only explicitly supported and verified profiles:

  • T56 — UNIPRO / ZX
  • T99 — Youdotech / QM011
  • RYKS — ELINK / ym_258

Generic or unknown Android hardware may be inventory-reported but must not receive model-specific mutations. Unknown, unauthorized, offline, recovery-mode, or ambiguous targets must be rejected before installation.

Operator workflow

Recommended zero-argument flow:

  1. Extract a signed Minimum update bundle.
  2. Connect one already-provisioned radio and authorize ADB.
  3. Double-click Update Minimum Device.cmd.
  4. The script detects and verifies the exact hardware profile.
  5. It inventories the current app/device state using sanitized output.
  6. It verifies the bundled APK identity, version, checksum, and signer compatibility.
  7. It installs the APK as an in-place upgrade without clearing app data.
  8. It applies only required, versioned, model-gated migrations or setting checks.
  9. It starts Minimum and waits for Ready.
  10. It reboots only when the release/migration requires it, then verifies Ready again.
  11. It confirms that Device ID and managed configuration were preserved.
  12. It reports PASS, WARN, or FAIL and saves a sanitized local report.
  13. It prompts the operator to disconnect the completed radio and connect the next one, or exit.

The script may process one physical device at a time for safe reboot correlation while allowing a single operator session to update several devices sequentially.

Core requirements

Safe device selection

  • Auto-detect the active ADB server on supported ports 5037/5041.
  • Require exactly one selected/authorized device for the recommended flow.
  • Support explicit -Serial or -TransportId for advanced use.
  • Verify manufacturer and model before any mutation.
  • Reject duplicate/ambiguous serial situations safely.
  • Re-identify the same physical/profile candidate after reboot without assuming the ADB transport ID is stable.
  • Never install or run model-specific logic on unknown hardware.

APK and release verification

  • Use the signed APK shipped in the reviewed GitHub Release bundle by default.
  • Verify SHA-256 before installation.
  • Verify application ID is exactly se.lublin.mumla.
  • Verify version name/code against bundle metadata.
  • Verify signer compatibility with the installed app before attempting an upgrade.
  • Refuse an unintended downgrade by default.
  • Require an explicit, prominently warned recovery option for any permitted downgrade.
  • Never download and execute an unverified “latest” artifact automatically.
  • If optional release download is added, pin the selected version and verify release metadata, checksum, package identity, and signer before use.

Preserve existing device state

  • Use an in-place APK update; never call uninstall or clear app data.
  • Capture the existing six-character Device ID before update and verify the same ID afterward.
  • Preserve active/pending/last-known-good managed configuration.
  • Preserve the selected channel and approved per-device settings unless a documented migration changes them.
  • Do not require portal registration for an already-registered device.
  • Stop safely if Minimum identity/configuration cannot be read or verified.
  • Do not export secrets or a complete app-data backup to the workstation.
  • Report whether rollback is possible before applying a release with an irreversible data migration.

Versioned migrations instead of full reprovisioning

  • Keep initial provisioning and routine updating as separate modes.
  • Define a versioned migration contract keyed by installed version/build and target version.
  • Run only migrations/settings checks required by the target release.
  • Make each migration idempotent and model-gated.
  • Record which migrations were APPLIED, ALREADY_OK, SKIPPED, or FAILED.
  • Reuse guarded T56/T99/RYKS helpers only through safe report/check or explicit migration interfaces.
  • Do not repeatedly remove OEM apps, rewrite Wi-Fi, reopen consent UI, or reapply unrelated provisioning on every APK update.
  • Integrate required policy changes such as cellular roaming/readiness from [Bug]: T56 stays on 3G with weak signal; provision roaming and validate LTE readiness #11 only after those changes are implemented and accepted.

Health and acceptance checks

Before mutation:

  • Confirm adequate battery or external power.
  • Confirm the package and current installed version.
  • Confirm Device ID is readable.
  • Record current Minimum readiness and sanitized config version when available.
  • Stop or warn if the radio is currently transmitting.

After installation:

  • Confirm the target version is installed.
  • Start Minimum and wait for the managed state to stabilize.
  • Confirm the same Device ID.
  • Confirm configuration loads without falling out of last-known-good safety.
  • Confirm connection and expected Ready state within a bounded timeout.
  • Verify model-specific minimum checks without claiming untested hardware behavior.
  • Reboot only when required or when the operator requests full acceptance.
  • After reboot, confirm unattended launch and Ready when reboot acceptance is run.
  • Report an actionable reason for every WARN or FAIL.

Do not treat APK installation success alone as update success.

Multi-device sequential session

Provide an optional loop mode, for example -UpdateSession, that lets an operator update the fleet one radio at a time:

  • Generate a unique sanitized session ID.
  • Wait for exactly one authorized supported device.
  • Update and verify that device completely.
  • Require disconnection before accepting the next device, preventing accidental double-processing.
  • Detect a device already completed in the current session and ask before rechecking it.
  • Continue with later devices after a per-device failure while retaining the failure result.
  • End with a summary table grouped by model and result.
  • Return a nonzero exit code when any requested device failed.
  • Do not persist raw serial numbers; use Device ID or a salted/session-local alias where identification is needed.

Example summary:

Minimum update session
Target version: 3.x.x
T56 / A1B2C3  PASS  updated, Ready after reboot
T99 / D4E5F6  WARN  updated, reboot check skipped
RYKS / G7H8J9 FAIL  signer mismatch; no changes made

Totals: 1 PASS, 1 WARN, 1 FAIL

Dry-run and recovery

  • Support -WhatIf/dry-run inventory that performs no mutation.
  • Support a report-only compatibility check before a deployment day.
  • Keep a bounded pre-update state record containing no secrets.
  • On installation/migration failure, do not clear data or silently retry indefinitely.
  • If the old APK remains installed, relaunch it and report the device state.
  • Document recovery for signature mismatch, insufficient storage, offline/reboot timeout, app crash, config failure, and interrupted USB.
  • Make it safe to rerun after interruption.
  • Never claim rollback success without verifying installed version, Device ID, configuration, and Ready.

Reporting and privacy

Save a timestamped sanitized JSON and human-readable text report locally. Include:

  • Session ID
  • Coarse device profile
  • Device ID when safe/required for fleet operations
  • Previous and target Minimum versions
  • APK/checksum/signature verification result
  • Migrations/checks run and their outcomes
  • Pre/post readiness
  • Reboot acceptance result
  • Overall PASS, WARN, or FAIL
  • Actionable error category

Exclude:

  • ADB/Android/USB serials from persisted reports
  • IMEI, IMSI, ICCID, phone number
  • Wi-Fi credentials
  • Server passwords and channel tokens
  • Device bearer tokens
  • Certificate fingerprints
  • Exact private coordinates
  • Full bugreports, app data, or unredacted logs

Console output must also avoid printing secrets.

Maintainability and token reduction

  • Keep device facts and migration rules in one testable source of truth instead of duplicating model logic across launchers.
  • Use stable symbolic result/error codes so a report can be reviewed without repeating the entire interactive session.
  • Add Pester or equivalent tests for selection, version comparison, checksum rejection, signer mismatch, migration dispatch, rerun/idempotency, summary generation, and secret redaction.
  • Add mocked ADB transcript tests for T56, T99, RYKS, unknown hardware, multiple targets, reboot transport changes, timeouts, and partial failure.
  • Add a concise “paste this report when requesting help” section so future diagnosis needs only the sanitized failure report.
  • Keep normal successful updates fully operator-driven and reserve developer/Codex interaction for new failures or hardware acceptance.

Release integration

  • Package the updater in every supported provisioning/update GitHub Release.
  • Generate a manifest binding launcher/scripts/APK/checksums to the exact release tag.
  • Test the extracted bundle on a clean Windows workstation without the source repository.
  • Document Windows/ADB prerequisites and the double-click flow.
  • Document the difference between Provision New Device and Update Existing Device.
  • Do not label a release stable until the updater is physically verified on every model claimed as supported.
  • Record untested model/release combinations as known limitations.

Acceptance criteria

  • An operator can update a previously provisioned supported radio by double-clicking one launcher.
  • No Codex/developer-guided command sequence is required for the normal success path.
  • No source checkout, Gradle, or Android Studio is required.
  • T56, T99, and RYKS are detected and routed correctly.
  • Unknown or ambiguous hardware is rejected before mutation.
  • APK provenance, checksum, identity, version, and signer are verified.
  • Update does not uninstall the app or clear data.
  • Device ID and managed configuration are preserved and verified.
  • Only required versioned/model-gated migrations run.
  • Repeated execution is idempotent.
  • Minimum reaches Ready after update; post-reboot Ready is verified when required.
  • A sequential session can update several radios one at a time and produce one summary.
  • Partial failures do not erase earlier results or prevent safe continuation.
  • Reports contain enough sanitized evidence for follow-up while excluding secrets and unique hardware/subscriber identifiers.
  • Automated script tests pass.
  • Real-device acceptance is recorded separately for T56, T99, and RYKS.
  • The operator documentation explains recovery and when to escalate.

Safety constraints

  • Never uninstall Minimum or clear its data automatically.
  • Never rewrite Android, USB, modem, or subscriber identifiers.
  • Never bypass APK signer verification.
  • Never apply one model's hardware mappings to another model.
  • Never assume a successful ADB command proves the resulting state; read back and verify.
  • Never transmit PTT as part of unattended update automation.
  • Never publish credentials, tokens, private configuration, exact private coordinates, or unsanitized device logs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions