We recently implemented foreground service and on Android 9 (Samsung devices only) some users experience some recurring crashes and we don’t know what’s causing this.
Fatal Exception: android.app.RemoteServiceException Context.startForegroundService() did not then call Service.startForeground()
android.app.ActivityThread$H.handleMessage (ActivityThread.java:1874)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loop (Looper.java:214)
android.app.ActivityThread.main (ActivityThread.java:7073)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:964)
AndroidManigest.xml
<service
android:name="com.sotg.base.util.GeoNotification"
android:exported="true"
android:foregroundServiceType="location" />
GeoNotificationManager.kt
override fun startService() {
val geoService = Intent(context, GeoNotification::class.java)
geoService.action = GeoNotification.START_SERVICE_ACTION
// for android Oreo and higher start as foreground
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(geoService)
} else {
context.startService(geoService)
}
}
override fun stopService() {
// Shouldn't initialize the service to stop it when the service is not running.
if (!GeoNotification.isServiceRunning()) return;
val geoService = Intent(context, GeoNotification::class.java)
geoService.action = GeoNotification.STOP_TRACKING_ACTION
runCatching { context.startService(geoService) }
.onFailure(analytics::logException)
}
GoeNotification.java
private static boolean isServiceRunning;
public static boolean isServiceRunning() {
return isServiceRunning;
}
@Override
public void onCreate() {
super.onCreate();
isServiceRunning = false;
}
@Override
public void onDestroy() {
stopTracking();
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (START_SERVICE_ACTION.equals(intent.getAction())) {
startTracking();
} else if (STOP_TRACKING_ACTION.equals(intent.getAction())) {
stopTracking();
} else {
Log.w(TAG, "Unknown action for location service");
}
// Service restarts if it gets terminated
// the original Intent is re-delivered to the onStartCommand
// method when using START_REDELIVER_INTENT.
return START_REDELIVER_INTENT;
}
private void startTracking() {
if (isServiceRunning) return;
// create notification and config as foreground only for android Oreo and higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(Constants.NOTIFY_SURVEY_GCM, createForegroundNotification(this));
}
isServiceRunning = true;
...
}
private void stopTracking() {
if (!isServiceRunning) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
}
stopSelf();
return;
}
// stop location updates on partner SDKs
....
stopSelf();
isServiceRunning = false;
}
public static Notification createForegroundNotification(Context context) {
NotificationChannelUtil.INSTANCE.getPreparedManager(context);
String channel = NotificationChannelUtil.INSTANCE.getSYSTEM_CHANNEL_ID();
// The PendingIntent to launch activity.
PendingIntent activityPendingIntent = PendingIntent.getActivity(context, 0,
new Intent(context, MainTabActivity.class), 0);
NotificationCompat.Action settingsAction = NotificationUtil.createSettingAction(context);
String title = context.getResources().getString(R.string.foreground_service_notification_title);
String desc = context.getResources().getString(R.string.foreground_service_notification_description);
String expandedText = context.getResources().getString(R.string.foreground_service_notification_description_expanded);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channel)
.setContentIntent(activityPendingIntent)
.setSmallIcon(R.drawable.noti)
.setContentTitle(title)
.setContentText(desc)
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(expandedText))
.addAction(settingsAction)
.setColorized(false)
.setOngoing(true);
return builder.build();
}