12

Background

I have a spare time app that I've been working on for some years (here), and I try to handle as many crashes as I can (I hate seeing crash reports!).

The problem

One recent crash that seem quite new, is this one:

android.app.RemoteServiceException: 
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1768)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:164)
  at android.app.ActivityThread.main (ActivityThread.java:6494)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:438)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:807)

And that's all that I see...

It seems to occur only for Android 8.1. Since the app has got to be quite popular, I'm very sure it will still occur only on Android 8.1.

What I've tried

Searching the Internet, I could find similar crashes, but in all of them, they had more clues.

So I tried to recall what the recent changes that I've done. The only changes were of migrating the support library to Android-X. The rest of the changes are very minor and I don't think they can be the cause for it.

Because I don't have any further clues, I decided to report about the Android-X part here (more information there, if needed), as I don't believe it's because of me trying to fix issues, and it looks like something very specific.

Later I integrated Firebase Crashlytics, and got this tiny additional clue above each such log:

Fatal Exception: android.app.RemoteServiceException
Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=channel_id__app_monitor pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 category=service vis=SECRET)

This is weird, because I do open the service correctly, otherwise it wouldn't have worked at all, and this is especially weird that this occurs only on Android 8.1, and not on 8.0 for example, which has the same requirement of how to start a foreground service.

This problematic notification is of a foreground service that I use to globally monitor app-related events (only from Android O, which has restrictions of BroadcastReceivers). It gets updated on 2 cases:

  1. Locale change
  2. Ticking a checkbox on some dialog that appears at some point (upon clicking on the dialog).

When I've tested both of those, they work fine, on both Android 8.0 and 8.1.

Here's the code to create/update the notification:

    @JvmStatic
    fun getUpdatedAppMonitorNotification(context: Context): Notification {
        val builder = Builder(context, context.getString(R.string.channel_id__app_monitor)).setSmallIcon(R.drawable.ic_stat_app_icon)//
                .setPriority(Notification.PRIORITY_DEFAULT).setCategory(Notification.CATEGORY_SERVICE)
        builder.setVisibility(NotificationCompat.VISIBILITY_SECRET)
        builder.setShowWhen(false)
        if (!PreferenceUtil.getBooleanPref(context, R.string.pref__avoid_showing_app_monitor_notification_dialog, false))
            builder.setContentTitle(context.getString(R.string.app_monitor__notification_title))
        if (VERSION.SDK_INT < VERSION_CODES.P) {
            val mainIntent = Intent(context, MainActivity::class.java)
                    .putExtra(MainActivity.EXTRA_OPENED_FROM_NOTIFICATION, true)
            builder.setContentIntent(PendingIntent.getActivity(context, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT))
        } else {
            val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
                    .putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
                    .putExtra(Settings.EXTRA_CHANNEL_ID, context.getString(R.string.channel_id__app_monitor))
            val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
            builder.setContentIntent(pendingIntent)
        }
        return builder.build()
    }

And the code I use in the service:

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    NotificationId.initNotificationsChannels(this)
    val notification = getUpdatedAppMonitorNotification(this)
    startForeground(NotificationId.APP_MONITOR, notification)
    ...
    return super.onStartCommand(intent, flags, startId)
}

And this is the code I call from other places, to update the notification:

    @JvmStatic
    fun updateAppMonitorNotification(context: Context) {
        if (VERSION.SDK_INT < VERSION_CODES.O)
            return
        val notification = AppMonitorService.getUpdatedAppMonitorNotification(context)
        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(NotificationId.APP_MONITOR, notification)
    }

The questions

  1. Why does it occur? Is it about Android X ? Or something wrong with the notification building?

  2. Why does it occur only on Android 8.1 ?

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • Is there a scenario where you might be trying to display the notification without having defined the channel yet? Did you accidentally provide a translation for `R.string.channel_id__app_monitor`, such that perhaps you created the channel with one name and are later trying to use it with a different one based on a locale change? – CommonsWare Jul 07 '18 at 11:02
  • Always, before I display the notification, I prepare the channels. See the "initNotificationsChannels" , and it's initialized on the very beginning anyway too, as I start the service on the onCreate of the Application class. I think I would have had a different exception for this anyway, if I tried it, no? The `channel_id__app_monitor` isn't translated, and is marked as such too. – android developer Jul 07 '18 at 11:26
  • @CommonsWare I've decided that instead of directly update the notification, I will always start the service, and let it update the notification for me. So far seems it has fixed the issue. Hope it is indeed a good fix. – android developer Jul 08 '18 at 16:30

2 Answers2

0

OK, I still don't know why this occurs, but a workaround for this that I've found, is to let the service do all of the updating of its notification, by safely just calling it via the context.startForegroundService function.

So far, by using this instead, I still don't see any crashes of this sort.

android developer
  • 114,585
  • 152
  • 739
  • 1,270
  • 2
    Hi Im getting the same problem.. Currently I started my ForegroundService then followed by the foreground notification.. It works great in 8.1 but not in 8.1.. Can you post more detailed answer how did you fix it? what do yo mean exactly bu `let the service do all of the updating of its notification`? – mboy Dec 05 '18 at 00:40
  • 2
    I solved mine.. :) I got rid of that weird error if I start the foreground notification inside `onStartCommand` and not in `onCreate()` ... starting the notification inside `onCreate()` doesn't have any problem with 8.0 though.. – mboy Dec 05 '18 at 00:52
  • I am getting the same error. android developer, could you please elaborate on what do you mean by calling it via the `context.startForegroundService`? – N0000B Jul 16 '21 at 18:17
  • @N0000B This: https://developer.android.com/reference/android/content/Context#startForegroundService(android.content.Intent) – android developer Jul 17 '21 at 19:10
  • Thanks for the comment. appreciated! Yes, I am doing something similar to start the service via `startForegroundService`. Also, I am `setting up notification` & calling `startForeground` in `onCreate` override. Could you please confirm where do you setup you notification? `onCreate` or `onStartCommand` or before you call `startForegroundService`? – N0000B Jul 20 '21 at 17:44
  • @N0000B I set on both, and check on a field whether I've initialized it already. – android developer Jul 22 '21 at 18:35
  • I think the error is due to recreating the notification channel/notification. I have added a flag to know if the service is already running and only start it if it's not running. Don't see this error yet (fingers crossed) after I made the change a few days back. – N0000B Jul 22 '21 at 23:26
0

I think this is the problem:

.putExtra(Settings.EXTRA_CHANNEL_ID, context.getString(R.string.channel_id__app_monitor))

You need to put the (unchanging) channel id, not the localized channel name

If you are able to reproduce this you can also look in your bugreport for AppSettings.*[your app] and see if there is a channel with an id matching the one you're trying to use.

Prags
  • 2,457
  • 2
  • 21
  • 38
  • The crash is reported automatically via Crashlytics. Not by me. Also, what you wrote is incorrect. The string isn't translated. Not all strings should be translated. You can set a string to be marked as non-translated, and you can also put string on files that are not in the "strings.xml" file. – android developer Jul 17 '18 at 14:19
  • Let me chk once again – Prags Jul 17 '18 at 16:11