Skip to content

🔴 Critical: battery notifications only fire while the app is open (no foreground service / persistent notification) #5

Description

@almothafar

Summary

Battery level notifications (warning / critical / full) only fire while the app is open in the foreground. As soon as the app is closed or backgrounded, monitoring stops and no alerts are delivered. There is also no persistent, ongoing battery-status notification like AccuBattery's.

Root cause

The only thing that listens for ACTION_BATTERY_CHANGED is BatteryLevelReceiver (+ PowerConnectionReceiver). These are not declared in the manifest — ACTION_BATTERY_CHANGED is a sticky broadcast that cannot be registered in AndroidManifest.xml and must be registered at runtime against a live Context.

They are registered at runtime inside PowerConnectionService:

  • app/src/main/java/.../service/PowerConnectionService.java:48-60 registers both receivers in onCreate().
  • app/src/main/java/.../ui/MainActivity.java:143 starts the service with startService(...).

But PowerConnectionService is a plain background Service — it never calls startForeground(), and the manifest declares no FOREGROUND_SERVICE permission or foregroundServiceType. On Android 8+ (this app is minSdk 26), the OS stops background services and reaps the hosting process within minutes of the app leaving the foreground. When the service is destroyed, onDestroy() unregisters the receivers (PowerConnectionService.java:65-83), so battery broadcasts stop arriving and no notifications are sent.

onStartCommand returns START_STICKY (PowerConnectionService.java:40-42), but on modern Android a backgrounded sticky service is not promptly (or reliably) restarted, and even if it were, onStartCommand does not re-register the receivers — only onCreate does.

This is also why charge cycles never increment / health stays at 100% (see the separate health-tracking issue): BatteryHealthTracker.recordBatteryState() is only called from BatteryLevelReceiver.onReceive, which is effectively only alive while the app is open.

Why "not sticky like AccuBattery"

AccuBattery's always-present notification is a foreground-service ongoing notification showing live battery %. This app has no such notification. The _pref_key_notifications_sticky preference only adds FLAG_NO_CLEAR/FLAG_ONGOING_EVENT to alert notifications (NotificationService.buildNotification, line ~328) and defaults to false — it is not a persistent live-status notification.

Proposed fix

Convert PowerConnectionService into a foreground service:

  1. Add permissions: FOREGROUND_SERVICE and an appropriate FOREGROUND_SERVICE_* type. A battery monitor most closely fits specialUse (requires a Play Console declaration) or systemExempted; evaluate per Android 14+ rules.
  2. Declare android:foregroundServiceType on the <service> in the manifest.
  3. Call startForeground(id, ongoingNotification) from the service, with a low-importance ongoing notification that shows live battery % (this doubles as the AccuBattery-style persistent notification users are asking for, and keeps the process alive so monitoring continues).
  4. Start it via startForegroundService(...) from MainActivity and from boot (see the boot auto-start issue).
  5. Re-register receivers defensively in onStartCommand (or keep them registered for the service lifetime).

Acceptance criteria

  • Battery warning/critical/full notifications fire with the app closed.
  • A persistent ongoing notification shows the current battery % and updates over time.
  • Monitoring survives the app being swiped away and resumes after reboot.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions