Skip to content

Background updates: reliable scheduling + consolidated permissions UI#135

Merged
1technophile merged 7 commits into
developmentfrom
feat/background-alarm-doze
Jun 1, 2026
Merged

Background updates: reliable scheduling + consolidated permissions UI#135
1technophile merged 7 commits into
developmentfrom
feat/background-alarm-doze

Conversation

@1technophile

Copy link
Copy Markdown
Member

Summary

Two-part change to make BLE background-sensor refresh actually fire when the
phone is in Doze / app-standby, and to make the permission story for it
discoverable rather than scattered across a popup and an inline Settings row.

Scheduling — fire on time through Doze

  • Honour the user-set "Update interval" as the Android background-scan cadence
    (cad508e).
  • Drive the background-work tick via a setAndAllowWhileIdle AlarmManager
    alarm, briefly holding a WAKE_LOCK so the scan window actually runs in deep
    doze (68617e4, 36e049f).
  • Use the exact AllowWhileIdle variant when SCHEDULE_EXACT_ALARM is
    granted; fall back to inexact otherwise. Prompt the user to grant it on
    Android 12+ where it is not auto-granted (3290db6).
  • Bug fix: the prompt's intent action was the AlarmManager constant name
    (android.app.action.REQUEST_SCHEDULE_EXACT_ALARM), which is not a valid
    intent action — so startActivity silently failed and the in-app
    "Allow exact alarms" button did nothing. Corrected to
    Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM (9e8aaf2).

Permission UX — one page, every required toggle

  • New BackgroundPermissions.qml screen with three live-status rows
    (✓/✗ + per-row "fix" action):
    • Background location — Android-mandated for BLE-in-background;
      runtime dialog.
    • Exact alarms (API 31+) — opens the system "Alarms & reminders" screen.
    • Battery optimisationnew: one-tap
      REQUEST_IGNORE_BATTERY_OPTIMIZATIONS system dialog so Doze /
      app-standby do not defer the work alarms. Manifest entry is flagged in a
      comment as Play-policy-sensitive.
  • Settings now routes to this page from both the "Enable background updates"
    toggle ON-path and the inline "Beta" button, replacing the 2-step
    PopupBackgroundUpdates popup and the standalone element_exact_alarm
    row (both removed).
  • Rows refresh both on a short timer after a request returns and on activity
    resume, so they flip green the moment the user grants the permission and
    comes back. A Connections { onSystrayChanged } keeps the Settings toggle
    visual in sync with the underlying setting after the page sets it (the
    previous popup's imperative onClosed re-sync went away with it).
  • JNI plumbing: TheengsAndroidService.isIgnoringBatteryOptimizations /
    requestIgnoreBatteryOptimizations; PermissionManager exposes a
    Q_PROPERTY + NOTIFY pair so QML rebinds when the OS state changes
    (5ff9623).

Test plan

  • Android 15 (API 35) emulator: clean first-run; all three rows tap →
    proper system surface → return → row flips green; foreground service
    runs; Settings toggle reflects enabled state.
  • Android 16 (API 36) emulator: same flow; exact-alarm intent resolves
    to the dedicated "Alarms & reminders → Allow setting alarms and
    reminders" screen (proving the action-string fix).
  • No QML errors in adb logcat Qt:V Theengs:V *:S across the walkthrough.
  • Physical phone in forced Doze (adb shell dumpsys deviceidle force-idle)
    — sanity-check that the AllowWhileIdle alarm fires through deep doze
    with the battery-opt exemption granted.
  • Verify on at least one OEM with aggressive battery policy (Xiaomi /
    OPPO / Huawei) that the "About battery savers" link + autolaunch
    guidance is sufficient.

🤖 Generated with Claude Code

1technophile and others added 7 commits May 24, 2026 21:29
…ence

The background "Update interval" setting (updateIntervalBackground, 5-360 min)
was exposed in the UI as if it governed how often sensor values refresh, but it
only drove the AndroidService work-timer's DeviceManager rebuild + MQTT
reconnect. Actual background scanning ran on a self-restarting 60s listen loop
(deviceDiscoveryFinished -> listenDevices_start), so the setting had no effect
on scan/publish frequency (device-measured: continuous ~60s regardless).

Make the setting real on Android: in the background service (daemon mode) stop
self-restarting the listen loop; AndroidService::gotowork() already fires every
updateIntervalBackground minutes and starts one ~60s listen window, so the radio
now idles between windows. The foreground UI manager (m_daemonMode == false, on
mobile and desktop) keeps re-arming, so the live view and continuous system-tray
scanning are unchanged. HIL_BENCH_MODE keeps its continuous active scan.

Desktop never reads this value (it scans continuously on mains power), so hide
the interval picker on desktop and reword the desktop legend to stop implying an
interval.

Device-verified (LG V30 / Android 9 and S10e / Android 12): background scanning
is one ~60s window per interval (5 min -> ~4 min idle; 10 min -> ~9 min idle),
foreground stays continuous.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the AndroidService QTimer (a non-wakeup timerfd, deferred to doze
maintenance windows — device-measured ~1-3h gaps in true deep doze) with a
setAndAllowWhileIdle AlarmManager alarm scheduled from Java. The alarm fires
through deep doze (unthrottled while the app is battery-Unrestricted), wakes
the CPU, holds a 65s partial wakelock across the scan window, and re-enters
C++ via a registered native (nativeOnWorkAlarm -> gotowork on the Qt thread),
which reschedules the next alarm.

Prototype for validating doze cadence; production should consider
setExactAndAllowWhileIdle + USE_EXACT_ALARM for precise timing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use setExactAndAllowWhileIdle (gated by canScheduleExactAlarms()) so the
background refresh fires at the user's exact Update interval through deep
doze, instead of setAndAllowWhileIdle which Android batches (~+50% drift,
measured ~7.8min for a 5min setting). Declares SCHEDULE_EXACT_ALARM
(auto-granted API 31-32, user-grantable 33+); degrades to the inexact
AllowWhileIdle alarm when the permission isn't granted or a race throws
SecurityException, so it never crashes and still pierces doze.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
On API 34+ SCHEDULE_EXACT_ALARM is denied by default, so the exact-alarm
path silently fell back to inexact (~+50% interval drift). Surface it:
PermissionManager gains exactAlarmPermission (canScheduleExactAlarms via
JNT) + requestExactAlarmPermission (opens Settings -> Alarms & reminders
via ACTION_REQUEST_SCHEDULE_EXACT_ALARM). Settings shows an 'Allow' row
under the interval picker when background updates are on but the grant is
missing; re-checked on resume so it clears after the user grants. No-op /
auto-granted on API <=33 and non-Android.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The intent action used to send the user to the "Alarms & reminders"
settings screen was the AlarmManager constant name, which is not an
intent action — the real action is Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM
("android.settings.REQUEST_SCHEDULE_EXACT_ALARM"). With the old string,
startActivity() failed to resolve and the in-app "Allow exact alarms"
button silently did nothing. Verified on the Android 15 and Android 16
emulators that the corrected action opens the proper "Alarms & reminders
/ Allow setting alarms and reminders" screen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the 2-step "background updates" popup and the standalone
"Allow exact alarms" inline row in Settings with one dedicated page
that walks the user through every permission needed for reliable
background sensor refresh:

- Background location (Android-mandated for BLE-in-background; runtime
  dialog).
- Exact alarms (API 31+; opens the system "Alarms & reminders" screen).
- Battery optimisation (new — one-tap REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
  system dialog so Doze / app-standby don't defer background-work alarms).

Each row shows live grant status (green ✓ / grey ✗) refreshed both on a
short timer after a request returns and on activity resume, so the user
sees the row flip as soon as they grant the permission and come back.

Settings now navigates to this page (instead of the popup) from both the
"Enable background updates" toggle ON path and the "Beta" inline button;
when the user returns, settingsManager.systray reflects the background-
location grant (preserves prior behaviour). A Connections handler on
settingsManager.onSystrayChanged keeps the toggle visual state in sync
with the underlying setting after the page sets it (the previous popup's
imperative onClosed re-sync went away with it).

Adds the JNI plumbing and manifest entry for battery-opt:
- TheengsAndroidService.isIgnoringBatteryOptimizations() / .requestIgnoreBatteryOptimizations()
- PermissionManager.checkBatteryOptimizationPermission() / .requestBatteryOptimizationPermission()
  with a Q_PROPERTY/NOTIFY pair so QML rebinds when the OS state changes.
- AndroidManifest.xml: REQUEST_IGNORE_BATTERY_OPTIMIZATIONS (flagged as
  Play-policy-sensitive in a comment).
- MobileApplication.qml onResume now also re-checks battery-opt alongside
  the existing exact-alarm re-check.

Removes the now-superseded qml/popups/PopupBackgroundUpdates.qml and its
Settings instance + onClosed handler. The old element_exact_alarm block
in Settings.qml is gone — exact alarms now live on the page as part of
the guided flow.

Verified end-to-end on the Android 15 (API 35) and Android 16 (API 36)
emulators: all three rows grant correctly and turn green, the
foreground service runs, and the Settings toggle reflects the enabled
state on return.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@1technophile 1technophile merged commit 20b8c2e into development Jun 1, 2026
10 checks passed
@1technophile 1technophile deleted the feat/background-alarm-doze branch June 1, 2026 15:01
1technophile added a commit that referenced this pull request Jun 2, 2026
Documentation fell behind the 1.5.0 release train after PR #130. Fold the
four user-facing PRs that landed since into the existing 1.5.0 section
of the changelog and refresh the affected pages on the doc site.

- use.md: note the new MQTT-down banner in the device list; replace the
  manual "Permissions/Battery/Unrestricted" walkthrough with the
  consolidated in-app Background updates screen (background location +
  exact alarms + battery-optimisation rows); document that the "Update
  interval" setting now actually drives the Android background scan
  cadence (one ~60 s window per interval, idle in between).
- build.md: require libsecret-1-dev on Linux; add the new
  ENABLE_SECURE_STORAGE CMake option for QtKeychain-backed credential
  storage.
- privacy.md: bump the last-updated date; note that broker credentials
  are persisted in the OS-managed secure store rather than plaintext
  settings.
- CHANGELOG.md: under the existing [1.5.0] entry, add the MQTT-down
  banner, consolidated Background updates page, and AllowWhileIdle
  exact-alarm scheduler to Added; the scan-cadence fix and the
  ENABLE_MBEDTLS=OFF guard to Fixed; and credential storage to Security.

PRs covered: #131 (MQTT sentinels), #132 (credential secure storage),
#133 (background scan cadence), #135 (background-alarm-doze train,
including the consolidated Background updates page and the exact
AllowWhileIdle scheduler).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1technophile added a commit that referenced this pull request Jun 6, 2026
Bump the 1.5.0 release date to 2026-06-06 and fold in the user-facing
changes that landed between #130 and the cut: TheengsDecoder v2.3.0
(#137), MQTT sentinels + sampling caption (#131), credential secure
storage (#132), background-update scheduling and consolidated
permissions UI (#133, #135), the About-screen decoder version and
build number (#139), the iOS mbedTLS static-link fix (#138), and the
device_bm26 ENABLE_MBEDTLS guard (#134).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1technophile added a commit that referenced this pull request Jun 6, 2026
Bump the 1.5.0 release date to 2026-06-06 and fold in the user-facing
changes that landed between #130 and the cut: TheengsDecoder v2.3.0
(#137), MQTT sentinels + sampling caption (#131), credential secure
storage (#132), background-update scheduling and consolidated
permissions UI (#133, #135), the About-screen decoder version and
build number (#139), the iOS mbedTLS static-link fix (#138), and the
device_bm26 ENABLE_MBEDTLS guard (#134).

Co-authored-by: Florian <1technophile@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant