John
John

Reputation: 6065

Do Samsung phones running Android 13 have an intent bug?

I've got a curious bug appearing in my app on Samsung phones running Android 13, and not appearing in my app on any other Android phones running my app.

I have a foreground service that is called through an intent. There is only one place in my entire codebase that creates this intent.

After the service has been created and destroyed (ie an intent has been created at least once) that service sometimes restarts by itself, without my code creating a new intent. (I've put breakpoints on the code that creates the intent, and it's definitely not being called.)

It's sporadic, but when it starts happening it happens under specific circumstances: immediately after any sort of Room database write, the service's onBind(intent:Intent) function lights up with the last-run intent. The intent can be minutes old, possibly even older though I've not tested for that.

This code base is years old and it's never happened until Samsung Android 13 phones ran it.

Is there some change to Android 13 I am failing to keep up with, that means intents should be handled differently to stop them starting a service more than once?

Is there some filter I can use in onBind(intent:Intent) to stop old intents starting the service? (As far as I can tell the replayed intents are identical to the original, but maybe there is some way to tell?)

One error that this has caused is for thousands of intents to flood the service, throwing the error IllegalStateException Too many bind requests(999+). When I search for that error, I see a GitHub discussion about Samsung Android 13 phones suffering that same error in another context. (https://github.com/aws-amplify/amplify-android/issues/2204)

Could it be a Samsung Android 13 problem, in which the phone is randomly replaying intents?

(God knows Samsung phones have other bugs I have had to deal with over the years. Their implementation of MediaRecorder has a bug they just won't fix!)

But if that were so wouldn't there be thousands of developers complaining about this?

I just can't figure it out.

Here are the relevant sections of code:

PlayerServiceControllerImpl.kt

private val playerServiceConnection = object : ServiceConnection {
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        val binder = service as PlayerService.ServiceBinder
        playerService = binder.getService()
        serviceIsBound = true
    }

    override fun onServiceDisconnected(name: ComponentName) {
        serviceIsBound = false
    }

}

private fun startService(memo: Memo) {
    val intent = Intent(context, PlayerService::class.java)
    context.bindService(intent, playerServiceConnection, Context.BIND_AUTO_CREATE)
    if (!serviceIsStarted) {
        ContextCompat.startForegroundService(context, intent)
        serviceIsStarted = true
    }
}

PlayerService.kt

inner class ServiceBinder : Binder() {
    fun getService(): PlayerService = this@PlayerService
}

override fun onBind(intent: Intent): IBinder {
    return serviceBinder
}

private fun startForegroundService(memo: Memo) {
    val notificationIntent = Intent(this, MainActivity::class.java)
    notificationIntent.putExtra(ARG_MEMO_ID, memo.uuid.toString())
    val pendingIntent = PendingIntent.getActivity(
        this,
        0,
        notificationIntent,
        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
    )
    notificationBuilder = NotificationCompat.Builder(this, PLAYER_CHANNEL_ID)
    val notification = notificationBuilder.setContentTitle("Ready to play ${memo.name}")
        .setSmallIcon(R.mipmap.ic_launcher_round)
        .setContentIntent(pendingIntent)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .build()
    notification.flags = Notification.FLAG_ONGOING_EVENT
    startForeground(PLAYER_NOTIFICATION_ID, notification)
}

Upvotes: 6

Views: 1765

Answers (1)

CommonsWare
CommonsWare

Reputation: 1007359

I'm not overriding onStartCommand

That may be contributing to your problem. The return value of onStartCommand() tells the OS what you want to have happen if the process is terminated to free up system RAM. The default response restarts the service at a later point. My guess is that you should return START_NOT_STICKY instead to have it not be restarted.

Upvotes: 0

Related Questions