Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Context.startForegroundService() did not then call Service.startForeground() only on Android 11 Samsung devices

I am getting this crash for only Samsung devices running android 11. Apparently the application is calling startForegroundService(intent) and requiring me to post a notification for the user to know that I am running a foreground service, but this call to startForegroundService(intent) was never made in the app source code, is it possible that Samsung made a custom implementation of android 11 and automatically calls startForegroundService(intent) whenever I call startService(intent)?

Stack trace

Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{4ab4323 u0 {app package name}/{library package name}.player.PlayerService}
android.app.ActivityThread$H.handleMessage (ActivityThread.java:2240)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loop (Looper.java:246)
android.app.ActivityThread.main (ActivityThread.java:8506)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:602)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1130)

I start the service using context.startService(intent) and the service is started in OnResume of the application and the context is the application context.

Also here is how the service is declared in the manifest

 <service
            android:name=".player.PlayerService"
            android:exported="false"
            android:foregroundServiceType="mediaPlayback"
            android:stopWithTask="false">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
  </service>

Update: I found the reason where the call to startForegroundService(Intent) is made, I am using the following receiver from android to help handle actions control devices like headphone buttons, so since I converted the app to androidx it started using the new MediaButtonReceiver

 <receiver android:name="androidx.media.session.MediaButtonReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
 </receiver>

This is the code that is executed when the Receiver receives an event

@Override
    public void onReceive(Context context, Intent intent) {
        if (intent == null
                || !Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
                || !intent.hasExtra(Intent.EXTRA_KEY_EVENT)) {
            Log.d(TAG, "Ignore unsupported intent: " + intent);
            return;
        }
        ComponentName mediaButtonServiceComponentName =
                getServiceComponentByAction(context, Intent.ACTION_MEDIA_BUTTON);
        if (mediaButtonServiceComponentName != null) {
            intent.setComponent(mediaButtonServiceComponentName);
            startForegroundService(context, intent);
            return;
        }
        ComponentName mediaBrowserServiceComponentName = getServiceComponentByAction(context,
                MediaBrowserServiceCompat.SERVICE_INTERFACE);
        if (mediaBrowserServiceComponentName != null) {
            PendingResult pendingResult = goAsync();
            Context applicationContext = context.getApplicationContext();
            MediaButtonConnectionCallback connectionCallback =
                    new MediaButtonConnectionCallback(applicationContext, intent, pendingResult);
            MediaBrowserCompat mediaBrowser = new MediaBrowserCompat(applicationContext,
                    mediaBrowserServiceComponentName, connectionCallback, null);
            connectionCallback.setMediaBrowser(mediaBrowser);
            mediaBrowser.connect();
            return;
        }
        throw new IllegalStateException("Could not find any Service that handles "
                + Intent.ACTION_MEDIA_BUTTON + " or implements a media browser service.");
    }

You can see that it actually starts a foreground service, I am still investigating the crash but at least I know that a foreground service is started in the app.

Also, this crash does not only happen in Samsung like I thought, crashlytics reporting grouped Samsung crashed together because its a crash from the android platform, other instances of the same crash happens less frequently so they were way down in the crash list on firebase.

like image 223
oziomajnr Avatar asked Mar 02 '21 08:03

oziomajnr


People also ask

What is startForeground in Android?

A started service can use the startForeground(int, android. app. Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory.

When should the startForeground function be called?

From Google's docs on Android 8.0 behavior changes: The system allows apps to call Context. startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method within five seconds after the service is created.

What is the difference between startService and startForegroundService?

NO MORE STARTSERVICE - The new context. startForegroundService() method starts a foreground service but with a implicit contract that service will call start foreground within 5 second of its creation. You can also call startService and startForegroundService for different OS version.

How do I start a foreground service?

context. startForegroundService(intent); Inside the service, usually in onStartCommand() , you can request that your service run in the foreground. To do so, call startForeground() .


1 Answers

Update

Possible reason

After you provided more code, it's still unclear if you call startForeground() in your PlayerService or not. It looks like you don't, and that's why you're seeing the crash. On the other hand, if this crash is only happening on Android 11, then you may also have a different problem.

Sample code

I just made a simple app to reproduce a crash for PlayerService if startForeground() is not called. Maybe it will be helpful to you.

app/build.gradle

Add a dependency to use the androidx MediaButtonReceiver

...
implementation "androidx.media:media:1.2.1" 
...

AndroidManifest.xml

...
    <service
        android:name=".PlayerService"
        android:exported="false"
        android:foregroundServiceType="mediaPlayback"
        android:stopWithTask="false">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </service>

    <receiver android:name="androidx.media.session.MediaButtonReceiver">
        <intent-filter>
            <action android:name="android.intent.action.MEDIA_BUTTON" />
        </intent-filter>
    </receiver>   
...

MainActivity.java

Emulate the media button event as below

...
@Override
protected void onResume() {
    super.onResume();

    Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
    intent.setClass(this, MediaButtonReceiver.class);
    intent.putExtra(Intent.EXTRA_KEY_EVENT, "test");
    sendBroadcast(intent);
}
... 

PlayerService.java

public class PlayerService extends Service {

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        MediaButtonReceiver.handleIntent(null, intent);

        return super.onStartCommand(intent, flags, startId);
    }

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

Fix

By adding startForeground() to onCreate() the problem gets fixed. Please note that the way I prepare parameters and use the method is a draft only. For detailed information, please check the official documentation here.

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

        Notification testNotification = new Notification();
        int testId = 1;
        startForeground(testId, testNotification);
    }
...
like image 66
Anatolii Avatar answered Oct 21 '22 18:10

Anatolii