Android Doze mode kills foregreound MediaPlayer service

387 views Asked by At

I've been researching awhile about how to keep active a constantly running audio playback in the background (online radio). For last I made a foreground service for it and its works for the most phones, but not on Samsung Android P and above... (as this article show in the "Foreground service limitations" section: https://proandroiddev.com/android-foreground-service-restrictions-d3baa93b2f70) I heard that there is a advanced tool for audio playback called ExoPlayer. Could this lib help me out?

I'v been tried these solutions:

  • ping google.com every 2 sec
  • request battery optimization ignoring
  • set wake lock for mediplayer with: setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) (still in use)

Starting the service:

viewModel.isMusicControlServiceNeedToStart.observe(this, Observer {
        if (it) {
            val intent = Intent(this, MusicControlForegroundServiceImpl::class.java).apply { action = ACTION_SHOW_MUSIC_CONTROL }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(intent) else startService(intent)
        } else {
            stopService(Intent(this, MusicControlForegroundServiceImpl::class.java))
        }
    })

The service itself:

class MusicControlForegroundServiceImpl : Service(), KoinComponent {

private val notificationManager: NotificationManager by inject()
private val radioManager: RadioManager by inject()
private val context: Context by inject()
private val preferences: Preferences by inject()

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (intent != null && intent.action == ACTION_SHOW_MUSIC_CONTROL) {
        val lastSelectedRadio = preferences.getJSON(Globals.LAST_SELECTED_RADIO_KEY, Radio::class.java)
                ?: return START_NOT_STICKY
        val notification = notificationManager.createMediaControlNotificationIfNeeded(context, lastSelectedRadio)
        startForeground(1, notification)
    }
    return START_NOT_STICKY
}

override fun onTaskRemoved(rootIntent: Intent?) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) startForegroundService(rootIntent) else startService(rootIntent)
    super.onTaskRemoved(rootIntent)
}

override fun onDestroy() {
    if (!notificationManager.musicControlServiceRestart) radioManager.release()
    synchronized(MUSIC_CONTROL_SERVICE_LOCK) { notificationManager.musicControlServiceRestart = false }
    synchronized(MEDIA_PLAYER_LOCK) { radioManager.lastPlayedMediaUrl = null }
    stopForeground(true)
    super.onDestroy()
}

override fun onBind(intent: Intent?): IBinder? = null

}

The notification creation:

 override fun createMediaControlNotificationIfNeeded(context: Context, selectedRadio: Radio): Notification {
    val resultIntent = Intent(context, RadioDetailActivity::class.java)
    val resultPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
        addNextIntentWithParentStack(resultIntent)
        getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
    }
    val playIntent = Intent(context, NotificationReceiver::class.java).apply {
        putExtra(MusicState::class.java.name, MusicState.PLAY)
    }
    val pauseIntent = Intent(context, NotificationReceiver::class.java).apply {
        putExtra(MusicState::class.java.name, MusicState.PAUSE)
    }

    val notificationManager = NotificationManagerCompat.from(context)

    @Suppress("DEPRECATION") val builder = NotificationCompat.Builder(context, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_notification_icon)
            .setContentTitle(selectedRadio.name)
            .setDefaults(0)
            .setOngoing(true)
            .setNotificationSilent()
            .addAction(
                    R.drawable.ic_notification_pause,
                    context.getString(R.string.pause),
                    PendingIntent.getBroadcast(context, 1, pauseIntent, 0)
            )
            .addAction(
                    R.drawable.ic_notification_play,
                    context.getString(R.string.play),
                    PendingIntent.getBroadcast(context, 2, playIntent, 0)
            )
            .setStyle(
                    androidx.media.app.NotificationCompat.MediaStyle().setMediaSession(
                            MediaSessionCompat(
                                    context,
                                    RadioDetailActivity::class.java.name
                            ).sessionToken
                    )
            )
            .setContentIntent(resultPendingIntent)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
                CHANNEL_ID,
                context.getString(R.string.app_name),
                NotificationManager.IMPORTANCE_LOW
        )
        notificationManager.createNotificationChannel(channel)
        builder.setChannelId(CHANNEL_ID)
    }

    return builder.build()
}

If you need any other resources please let me know and help if you can! I'm struggling with this problem for weeks now.. :(

UPDATE Now I throw my media control notification in every 2 minutes to update previous, so the app can survive like 30 minutes on the affected phone, but still not a working solution...

0

There are 0 answers