feat(core): apply autolock settings from menu
What changed, and why it matters
This commit adds a new battery-specific auto-lock setting to Trezor hardware wallets. Users can now set a separate screen-lock delay for when the device is running on battery power, distinct from the existing USB-powered auto-lock delay. The change wires up the on-device menu, the settings-applier, and the Python companion library. There is no indication this fixes a security bug; it appears to be a missing feature for a new power-management capability.
No immediate security action required. Treat as a normal feature commit. Reviewers may want to verify that AUTOLOCK_DELAY_BATT_MIN_MS/MAX_MS are defined consistently and that the user confirmation flow cannot be bypassed by a host sending ApplySettings directly.
Security signals we found
New settings field with explicit min/max bounds and user confirmation
No changelog entry, but title is a feature commit ('feat(core)')
No evidence of memory-unsafe changes, authentication bypass, or cryptographic changes
Evidence from the diff
The patch extends ApplySettings to accept auto_lock_delay_battery_ms, adds range validation (AUTOLOCK_DELAY_BATT_MIN_MS/MAX_MS), requires user confirmation, persists the value via storage_device.set_autolock_delay_battery_ms(), and exposes the field in trezorlib’s device.apply_settings(). The homescreen device menu now shows two auto-lock rows (battery and USB) and routes each to the correct setting. The feature is guarded by utils.USE_POWER_MANAGER, so it only applies to devices with a power manager.
Changed components
core/src/apps/homescreen/device_menu.pycore/src/apps/management/apply_settings.pypython/src/trezorlib/device.pyInspect captured patch +38 / −10
diff --git a/core/src/apps/homescreen/device_menu.py b/core/src/apps/homescreen/device_menu.py
index 66971a691..81aa8379d 100644
--- a/core/src/apps/homescreen/device_menu.py
+++ b/core/src/apps/homescreen/device_menu.py
@@ -45,10 +45,13 @@ def get_auto_lock_delay() -> tuple[str, str] | None:
if not config.has_pin():
return None
- delay = storage_device.get_autolock_delay_ms()
- # TODO: the second value is mocked by using the same value
- formatted = strings.format_autolock_duration(delay)
- return (formatted, formatted)
+ autolock_delay_batt = storage_device.get_autolock_delay_battery_ms()
+ autolock_delay_usb = storage_device.get_autolock_delay_ms()
+
+ autolock_delay_batt_fmg = strings.format_autolock_duration(autolock_delay_batt)
+ autolock_delay_usb_fmt = strings.format_autolock_duration(autolock_delay_usb)
+
+ return (autolock_delay_batt_fmg, autolock_delay_usb_fmt)
async def handle_device_menu() -> None:
@@ -231,23 +234,36 @@ async def handle_device_menu() -> None:
from apps.management.apply_settings import apply_settings
try:
+ if menu_result is DeviceMenuResult.AutoLockUSB:
+ duration_ms = storage_device.get_autolock_delay_ms()
+ min_ms = storage_device.AUTOLOCK_DELAY_USB_MIN_MS
+ max_ms = storage_device.AUTOLOCK_DELAY_USB_MAX_MS
+ else:
+ duration_ms = storage_device.get_autolock_delay_battery_ms()
+ min_ms = storage_device.AUTOLOCK_DELAY_BATT_MIN_MS
+ max_ms = storage_device.AUTOLOCK_DELAY_BATT_MAX_MS
+
auto_lock_delay_ms = await interact(
trezorui_api.request_duration(
title=TR.auto_lock__title,
- duration_ms=storage_device.get_autolock_delay_ms(),
- min_ms=storage_device.AUTOLOCK_DELAY_USB_MIN_MS,
- max_ms=storage_device.AUTOLOCK_DELAY_USB_MAX_MS,
+ duration_ms=duration_ms,
+ min_ms=min_ms,
+ max_ms=max_ms,
description=TR.auto_lock__description,
),
br_name=None,
)
# Necessary for the style check not to raise type error
assert isinstance(auto_lock_delay_ms, int)
- await apply_settings(
- ApplySettings(
+ if menu_result is DeviceMenuResult.AutoLockUSB:
+ settings = ApplySettings(
auto_lock_delay_ms=auto_lock_delay_ms,
)
- )
+ else:
+ settings = ApplySettings(
+ auto_lock_delay_battery_ms=auto_lock_delay_ms,
+ )
+ await apply_settings(settings)
except ActionCancelled:
pass
finally:
diff --git a/core/src/apps/management/apply_settings.py b/core/src/apps/management/apply_settings.py
index f1400830b..0d9f2ccda 100644
--- a/core/src/apps/management/apply_settings.py
+++ b/core/src/apps/management/apply_settings.py
@@ -61,6 +61,7 @@ async def apply_settings(msg: ApplySettings) -> Success:
homescreen_length = msg.homescreen_length # local_cache_attribute
label = msg.label # local_cache_attribute
auto_lock_delay_ms = msg.auto_lock_delay_ms # local_cache_attribute
+ auto_lock_delay_battery_ms = msg.auto_lock_delay_battery_ms
use_passphrase = msg.use_passphrase # local_cache_attribute
passphrase_always_on_device = (
msg.passphrase_always_on_device
@@ -79,6 +80,7 @@ async def apply_settings(msg: ApplySettings) -> Success:
and passphrase_always_on_device is None
and display_rotation is None
and auto_lock_delay_ms is None
+ and auto_lock_delay_battery_ms is None
and msg_safety_checks is None
and experimental_features is None
and hide_passphrase_from_host is None
@@ -124,6 +126,14 @@ async def apply_settings(msg: ApplySettings) -> Success:
await _require_confirm_change_autolock_delay(auto_lock_delay_ms)
storage_device.set_autolock_delay_ms(auto_lock_delay_ms)
+ if auto_lock_delay_battery_ms is not None and utils.USE_POWER_MANAGER:
+ if auto_lock_delay_battery_ms < storage_device.AUTOLOCK_DELAY_BATT_MIN_MS:
+ raise ProcessError("Auto-lock delay too short")
+ if auto_lock_delay_battery_ms > storage_device.AUTOLOCK_DELAY_BATT_MAX_MS:
+ raise ProcessError("Auto-lock delay too long")
+ await _require_confirm_change_autolock_delay(auto_lock_delay_battery_ms)
+ storage_device.set_autolock_delay_battery_ms(auto_lock_delay_battery_ms)
+
if msg_safety_checks is not None:
await _require_confirm_safety_checks(msg_safety_checks)
safety_checks.apply_setting(msg_safety_checks)
diff --git a/python/src/trezorlib/device.py b/python/src/trezorlib/device.py
index 020b63f05..2d018494c 100644
--- a/python/src/trezorlib/device.py
+++ b/python/src/trezorlib/device.py
@@ -49,6 +49,7 @@ def apply_settings(
homescreen: Optional[bytes] = None,
passphrase_always_on_device: Optional[bool] = None,
auto_lock_delay_ms: Optional[int] = None,
+ auto_lock_delay_battery_ms: Optional[int] = None,
display_rotation: Optional[messages.DisplayRotation] = None,
safety_checks: Optional[messages.SafetyCheckLevel] = None,
experimental_features: Optional[bool] = None,
@@ -65,6 +66,7 @@ def apply_settings(
use_passphrase=use_passphrase,
passphrase_always_on_device=passphrase_always_on_device,
auto_lock_delay_ms=auto_lock_delay_ms,
+ auto_lock_delay_battery_ms=auto_lock_delay_battery_ms,
display_rotation=display_rotation,
safety_checks=safety_checks,
experimental_features=experimental_features,
Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.