6

I noticed some very strange behavior when testing foreground notifications on Android 9.

Situation: I have some foreground services where I don't know how long they'll need to run for, be it 1 second or a whole minute. Finally, when the service is done, it will call stopForeground(true). This used to work fine on all Android 8, 9 and 10 devices, where stopForeground(true), even if called immediately, always reliably removed the notification.

Problem: Testing on a Fairphone 3 (and I hope someone else encountered this on some other devices, because for me this is not happening on any emulator or other device), stopForeground is not working as expected. Instead of immediately removing the notification, the notification always shows for at least 5 seconds, even if I call stopForeground straight away. These 5 seconds happen to be the exact 5 second limit of the dreaded error Context.startForegroundService() did not then call Service.startForeground() - still a problem. Very peculiar! Reproducing this and checking whether your device is affected is very easy with the code below.

Inside AndroidManifest.xml:

<service
    android:name="<your package name>.TestService"
    android:exported="false" />

Class TestService.java:

package <your package name>;

import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

@TargetApi(Build.VERSION_CODES.O)
public class TestService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        showNotification();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        showNotification();
        stopForeground(true);
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private void showNotification() {
        String channelId = "TEST";
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager.getNotificationChannel(channelId) == null)
            notificationManager.createNotificationChannel(new NotificationChannel(channelId, "TEST NOTIFICATIONS", NotificationManager.IMPORTANCE_DEFAULT));
        startForeground(1, new NotificationCompat.Builder(this, channelId).setContentText("TEST NOTIFICATION").build());
    }
}

Finally, simply start the service (for example on button click) via startForegroundService(new Intent(this, TestService.class));

Has anyone else experienced this issue or is able to reproduce it with the code above? How can I fix or even just debug it, considering I'm testing on Android 9 and the behaviour is different simply because of the OEM?

0101100101
  • 5,786
  • 6
  • 31
  • 55
  • try using IntentService or JobIntentService if possible – Amit Goswami Dec 30 '19 at 06:07
  • @AmitGoswami whether to use a `Service` or `IntentService` solely depends on one's use case and is not in the scope of this question, which aims to provide a [Minimal, Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example). I tried switching the above snippet to `IntentService` and `onHandleIntent` but the effect was all the same. – 0101100101 Dec 30 '19 at 06:21
  • Hmm, so you're saying `stopForeground()` starts executing, and it blocks for ~5 seconds inside that function? – greeble31 Dec 30 '19 at 15:23
  • @greeble31 actually when debugging it seemed not to block the execution, but the notification is removed only after those 5 seconds. – 0101100101 Dec 31 '19 at 03:28
  • So your issue could be restated simply as, "foreground notification always shows for at least 5 seconds on Fairphone 3". I can see why you would find this behavior curious or even objectionable, but at the same time I think the OEM is within their rights to do things this way. Discourages people from creating sneaky foreground services that "hide" in the background by only running for a few ms at a time. Have you considered using a regular old background service, instead, for those occasions when you know you won't have to execute for very long? – greeble31 Dec 31 '19 at 15:43
  • @greeble31 correct. Yes, I considered background services. For the use case I originally provided (stopping a service immediately after starting it) you're right, it's easy to use background services instead. But they're still not an option in my case. Take a look at my updated answer where it should be clear why not. – 0101100101 Jan 02 '20 at 05:12

2 Answers2

5

Latest security patch claims that it's normal behaviour: https://issuetracker.google.com/issues/147792378

However I'm not sure why it's happening already on Android 9.

Seky
  • 66
  • 2
  • Balls! Thanks for finding that. I can imagine it's the case on Fairphone Android 9 specifically because they tend to be on the forefront when it comes to security patches, so they probably included the patch manually. – 0101100101 Jan 28 '20 at 01:36
  • Super confusing behaviour that's not mentioned anywhere in the documentation (or is it?) – sprajagopal Jun 19 '21 at 16:07
0

Try this when you start your service, I saw someone using it and it worked.

ContextCompat.startForegroundService(this, serviceIntent);

Here is the link: https://gist.github.com/codinginflow/1ab6d2689780be6d764215c3a40f2166

Tyler2P
  • 2,324
  • 26
  • 22
  • 31
manal
  • 1
  • 2