fix: keep the device awake until an alarm or timer starts ringing
The fire broadcast's wake lock ended with the receiver, and the ring services only took their own after a database read, so a dozing device could suspend in between and ring late. Both receivers now hold a short timed wake lock across the hand-off, and the alarm service takes its own before the read.
This commit is contained in:
1 parent
b883554dc4
commit
96fc9c19cf
4 files changed
+34
-1
No files matched your search
@@ -5,6 +5,7 @@ import android.content.Intent
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import de.jeanlucmakiola.clockula.alarm.AlarmEngine
|
||||
import de.jeanlucmakiola.clockula.alarm.AlarmIntents
|
||||
import de.jeanlucmakiola.clockula.ring.HandoffWakeLock
|
||||
import de.jeanlucmakiola.floret.di.ApplicationScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -32,6 +33,7 @@ class AlarmFireReceiver : HiltBroadcastReceiver() {
|
||||
|
||||
val alarmId = intent.getLongExtra(AlarmIntents.EXTRA_ALARM_ID, 0L)
|
||||
val action = intent.action ?: return
|
||||
if (action == AlarmIntents.ACTION_FIRE) HandoffWakeLock.acquire(context, "clockula:alarm-fire")
|
||||
val pendingResult = goAsync()
|
||||
scope.launch {
|
||||
try {
|
||||
|
||||
@@ -67,6 +67,8 @@ class AlarmRingService : Service() {
|
||||
// foreground service a few seconds to post its notification, and a
|
||||
// database read is not something to spend them on.
|
||||
startForegroundPreparing()
|
||||
// Also before the read: the fire broadcast's own wake lock is gone by now.
|
||||
acquireWakeLock()
|
||||
ringing = scope.launch {
|
||||
val session = engine.ringSession()
|
||||
if (session == null) {
|
||||
@@ -89,7 +91,6 @@ class AlarmRingService : Service() {
|
||||
|
||||
private fun ring(session: RingSession) {
|
||||
startForeground(session)
|
||||
acquireWakeLock()
|
||||
|
||||
// Vibration first: the sound may take seconds to open when another app
|
||||
// plays it, and the ring must not be still until then.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package de.jeanlucmakiola.clockula.ring
|
||||
|
||||
import android.content.Context
|
||||
import android.os.PowerManager
|
||||
|
||||
/**
|
||||
* Keeps the CPU awake across the gap between an AlarmManager broadcast and the
|
||||
* service it starts. The platform's own wake lock ends with the receiver, and
|
||||
* the service takes its own only after a database read, so without this a
|
||||
* dozing device can suspend in between and ring minutes late.
|
||||
*
|
||||
* Timed rather than released: the service holds its own lock well before the
|
||||
* timeout, and a lock that outlives a crashed hand-off cannot leak.
|
||||
*/
|
||||
object HandoffWakeLock {
|
||||
|
||||
private const val TIMEOUT_MILLIS = 30_000L
|
||||
|
||||
fun acquire(context: Context, tag: String) {
|
||||
val power = context.getSystemService(PowerManager::class.java) ?: return
|
||||
runCatching {
|
||||
power.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag).apply {
|
||||
setReferenceCounted(false)
|
||||
acquire(TIMEOUT_MILLIS)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import de.jeanlucmakiola.clockula.alarm.receiver.HiltBroadcastReceiver
|
||||
import de.jeanlucmakiola.clockula.ring.HandoffWakeLock
|
||||
import de.jeanlucmakiola.clockula.timer.TimerEngine
|
||||
import de.jeanlucmakiola.floret.di.ApplicationScope
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -28,6 +29,7 @@ class TimerExpiryReceiver : HiltBroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
super.onReceive(context, intent)
|
||||
|
||||
HandoffWakeLock.acquire(context, "clockula:timer-expiry")
|
||||
val pendingResult = goAsync()
|
||||
scope.launch {
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user