- Add Capabilities.supportsLockdown (atLeast V2_8_0) and gate the security-screen
lockdown toggle visibility on it, consistent with the other firmware-version
capability flags. SecurityConfigScreen computes Capabilities from the connected
node's firmware_version; LockdownModeSetting takes a `supported` flag.
- Reword the enable-lockdown warning: it's reversible (disable with passphrase, or
a full erase), so drop the inaccurate "permanent/irreversible" framing and the
red error styling; rename strings lockdown_irreversible_* -> lockdown_enable_*.
- Update CapabilitiesTest for the V2_8_0 floor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The CI shard-core allTests job compiles commonTest for the iosSimulatorArm64
target, which surfaced issues the JVM-only local run missed:
- Update stale test fakes to the current interfaces: FakePassphraseStore
(maxSessionSeconds on savePassphrase) and core/takserver's FakeCommandSender /
FakeServiceRepository (lockdown send + state members). These predate this
change but only the Native test compile catches them.
- Rename the DISABLED test: Kotlin/Native rejects commas in backtick names.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Make lockdown a runtime, user-toggleable setting rather than a one-way lock:
- Thread a `disable` flag through the lockdown send path (CommandSender,
LockdownCoordinator, MeshActionHandler, RadioController, AIDL, UIViewModel)
so the app can send LockdownAuth{passphrase, disable=true} to decrypt
storage and leave lockdown.
- Add LockdownState.Disabled and map LockdownStatus.State.DISABLED; clear the
stored passphrase and session authorization when a device reports DISABLED
(or when the user disables it), so we never auto-unlock a disabled device.
- Add a "Lockdown mode" switch to the security settings screen
(LockdownModeSetting): enable from DISABLED via a set-passphrase dialog with
a one-time irreversible-SWD warning + explicit confirm; disable from UNLOCKED
via a passphrase prompt; "Lock now" and session info while unlocked. The
setting is hidden when the device never reports lockdown_status (non-capable).
- Tests for the disable round-trip and DISABLED mapping; refresh fakes/strings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Moves core/proto to PR #927 head (c834f6b), which adds LockdownAuth.disable
and LockdownStatus.State.DISABLED on top of develop. That tree has no
meshtastic.Marti message, so the Marti prune left over from the master merge
was unused and broke generateCommonMainProtos; remove it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
End-to-end plumbing for LockdownAuth.max_session_seconds (per-boot
uptime cap on the unlocked session; 0 = unlimited).
Wire:
- CommandSenderImpl populates LockdownAuth.max_session_seconds in the
outbound admin packet (clamped non-negative).
Coordinator + persistence:
- LockdownCoordinator.submitPassphrase gains optional maxSessionSeconds
(default 0); persisted alongside boots/hours and replayed by
auto-unlock so cached sessions keep the operator's cap on reconnect.
- StoredPassphrase gains a new field with a default of 0 so existing
call sites stay source-compatible.
- LockdownPassphraseStore (Android EncryptedSharedPreferences impl):
reads/writes the new field with a `_maxSessionSeconds` key suffix;
legacy entries decode to 0.
- LockdownPassphraseStore (JVM file-backed impl): bumps the per-entry
on-disk serialization from 3-line to 4-line; legacy 3-line entries
still decode (treated as maxSessionSeconds=0).
IPC + radio plumbing:
- IMeshService.sendLockdownUnlock AIDL gains a 4th int parameter.
- MeshService stub, MeshActionHandler, RadioController interface, and
both impls (AndroidRadioControllerImpl, DirectRadioControllerImpl)
thread the field through.
- FakeIMeshService, FakeRadioController, FakeLockdownCoordinator
updated to match.
UI:
- LockdownDialog adds a single optional "Session cap (minutes)" field
below the boots/hours row. Operators enter minutes for ergonomics;
the dialog multiplies by 60 before passing to the coordinator. Blank
or 0 = unlimited (firmware default).
- UIViewModel.sendLockdownUnlock gains the new param with default 0.
- New string resources: lockdown_session_minutes,
lockdown_session_minutes_help. Strings re-sorted via
scripts/sort-strings.py.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pulls in protobufs PR #916 which adds LockdownAuth.max_session_seconds
(uint32, field 5) — per-boot uptime cap on the unlocked session.
Wire-compatible (proto3 default 0 = unchanged behaviour).
Note: this pointer is on the upstream `develop` branch — `master` has
not absorbed it yet. Will need updating if the maintainer prefers a
master pointer at merge time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>