Below is the code for reproducing. The foreground service simply plays a beep at 20-seconds interval (of course, my real service does a useful job for the user) in the background.
If I start the service, exit to the home screen (so that my app won't be showing), and then turn off the screen, the beep interval is not even close to 20 seconds. It's random, but like 40 to 60 seconds.
The funny thing is that if I play any long-running sound using a music player app or a podcast app, then my app can play the beep at the 20-seconds interval. I have set "Allow background usage" for my app, so I am not sure why this happens. It happens with Android 14 and 15.
What is wrong? This is not impossible due to battery saving, is it? Saving battery is meaningless if the user can't do an important background task that he wants.
class MyTestService : Service()
{
val serviceJob = SupervisorJob()
val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
var needsToQuit = false;
companion object
{
const val CHANNEL_ID = "MyForegroundServiceChannel"
const val NOTIFICATION_ID = 123456
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int
{
needsToQuit = false;
createNotificationChannel()
val notification = buildNotification("Don't kill me…")
startForeground(NOTIFICATION_ID, notification)
doSomethingPeriodically();
return START_STICKY
}
private fun doSomethingPeriodically()
{
serviceScope.launch {
while (isActive)
{
var toneGen = ToneGenerator(AudioManager.STREAM_MUSIC, 100);
toneGen.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200);
delay(20_000)
}
}
}
private fun buildNotification(contentText: String): Notification
{
return NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Don't kill me")
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.build()
}
private fun createNotificationChannel()
{
val channel = NotificationChannel(
CHANNEL_ID,
"Don't kill me",
NotificationManager.IMPORTANCE_LOW
)
val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
}
override fun onBind(intent: Intent?): IBinder?
{
return null;
}
override fun onDestroy()
{
super.onDestroy()
serviceJob.cancel()
}
}