1

I have tried every suggestion I could find on the Internet to use Foreground Service to keep my app running beyond sleep and deep sleep modes but nothing has been successful so far.

I am working on a taxi booking application. I designed it to start emitting driver's location to server whenever the driver turns himself Online and stop emitting when Offline.

The following is the Foreground Service code that is started whenever driver turns himself Online and stopped when he presses online button which changes Common.CustomSocketOn to 0.

It work fine during screen wake and also works when the screen is off before the app gets killed.

However, even with WAKE_LOCK acquired, it still can't stay more than few minutes in sleep mode before getting killed by Android 7.

This failure to keep running in sleep mode breaks down many other features of the app because when the app gets killed silently, it does not get the chance to turn the driver Offline nor sign him out. As a result, the driver gets booking requests when his app is not running and therefore, cannot attend to it, and that keeps the booking from going to the next available driver. In fact, this causes so many other anomalies.

Please, can somebody tell me any other thing I need to do to keep Android from killing the Foreground Service.

public class OnlineForeGroundService extends Service {

private static final String TAG_FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE";
public static final String ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE";

private static LocationListener locationListener;
private static LocationManager locationManager;

private PowerManager.WakeLock wakeLock;

public OnlineForeGroundService() {
}

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    //throw new UnsupportedOperationException("Not yet implemented");
    return null;
}

@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG_FOREGROUND_SERVICE, "My foreground service.");

    final PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    try {
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "KEEP_AWAKE");
    }
    catch (Exception e){
        e.printStackTrace();
    }
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if(intent != null)
    {
        String action = intent.getAction();

        if(action != null) {
            switch (action) {
                case ACTION_START_FOREGROUND_SERVICE:
                    startForegroundService();
                    Toast.makeText(getApplicationContext(), getText(R.string.going_online), Toast.LENGTH_SHORT).show();
                    wakeLock.acquire();
                    break;
                case ACTION_STOP_FOREGROUND_SERVICE:
                    stopForegroundService();
                    //Toast.makeText(getApplicationContext(), getText(R.string.going_offline), Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
    return super.onStartCommand(intent, flags, startId);
    //return START_STICKY;
}

/* Used to build and start foreground service. */
@SuppressLint("MissingPermission")
private void startForegroundService()
{
    if(OnlineForeGroundService.locationManager == null) {
        OnlineForeGroundService.locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
    }


    if(OnlineForeGroundService.locationListener == null) {
        OnlineForeGroundService.locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {

                if (Common.CustomSocketOn == 1) {
                    SharedPreferences userPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

                    if (Common.OldLatitude != 0 && Common.OldLongitude != 0) {

                        float distance[] = new float[1];
                        Location.distanceBetween(Common.OldLatitude, Common.OldLongitude, location.getLatitude(), location.getLongitude(), distance);

                        //Distance - 100
                        if (distance.length > 0 && distance[0] > 30) {
                            try {
                                JSONArray locAry = new JSONArray();
                                locAry.put(location.getLatitude());
                                locAry.put(location.getLongitude());
                                JSONObject emitobj = new JSONObject();
                                emitobj.put("coords", locAry);
                                emitobj.put("driver_name", userPref.getString("user_name", ""));
                                emitobj.put("driver_id", userPref.getString("id", ""));
                                emitobj.put("driver_status", "1"); //change by sir
                                emitobj.put("car_type", userPref.getString("car_type", ""));
                                emitobj.put("isdevice", "1");
                                emitobj.put("booking_status", userPref.getString("booking_status", ""));
                                emitobj.put("isLocationChange", 1);
                                if (location.getLatitude() != 0.0 && location.getLongitude() != 0.0 && Common.socket != null && Common.socket.connected()) {
                                    Common.socket.emit("Create Driver Data", emitobj);
                                } else if (location.getLatitude() != 0.0 && location.getLongitude() != 0.0 && Common.socket == null) {
                                    Common.socket = null;
                                    SocketSingleObject.instance = null;
                                    Common.socket = SocketSingleObject.get(getApplicationContext()).getSocket();
                                    Common.socket.connect();
                                    Common.socket.emit("Create Driver Data", emitobj);
                                } else if (location.getLatitude() != 0.0 && location.getLongitude() != 0.0 && Common.socket != null && !Common.socket.connected()) {
                                    Common.socket.connect();
                                    Common.socket.emit("Create Driver Data", emitobj);
                                }
                            } catch (JSONException e) {
                                e.printStackTrace();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }

                            Common.OldLatitude = location.getLatitude();
                            Common.OldLongitude = location.getLongitude();
                        }
                    }

                    if (Common.OldLatitude == 0 && Common.OldLongitude == 0) {
                        Common.OldLatitude = location.getLatitude();
                        Common.OldLongitude = location.getLongitude();
                    }

                }
                else{
                    stopForegroundService();
                }
            }

            @Override
            public void onProviderDisabled(String provider) {
                Log.d("Latitude", "disable");
            }

            @Override
            public void onProviderEnabled(String provider) {
                Log.d("Latitude", "enable");
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {
            }
        };
    }


    if(Common.isPermission){
        if(Common.CustomSocketOn == 1){
            try {
                OnlineForeGroundService.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, Common.DriverDistanceTime, Common.DriverDistance, OnlineForeGroundService.locationListener);
                OnlineForeGroundService.locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, Common.DriverDistanceTime, Common.DriverDistance, OnlineForeGroundService.locationListener);
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }



    Log.d(TAG_FOREGROUND_SERVICE, "Starting foreground service.");
    String onlineSticker = getText(R.string.app_name)+" - Online";

    Intent notificationIntent = new Intent(this, HomeActivity.class);
    notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);
    PendingIntent pendingIntent =
            PendingIntent.getActivity(this, 0, notificationIntent, 0);

    Notification notification;
    /*if(android.os.Build.VERSION.SDK_INT >= 26) {
        notification = new Notification.Builder(HomeActivity.class, NotificationManager.IMPORTANCE_HIGH)
                .setContentTitle(getText(R.string.app_name))
                .setContentText(getText(R.string.you_are_online))
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .setTicker(onlineSticker)
                .build();

        //startForeground(ONGOING_NOTIFICATION_ID, notification);

        // Start foreground service.
    }
    else{*/
        notification = new Notification.Builder(this)
                .setContentTitle(getText(R.string.app_name))
                .setContentText(getText(R.string.you_are_online))
                .setPriority(Notification.PRIORITY_HIGH)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .setTicker(onlineSticker)
                .build();
    //}
    startForeground(397, notification);
}

private void stopForegroundService()
{
    Toast.makeText(getApplicationContext(), getText(R.string.going_offline), Toast.LENGTH_SHORT).show();

    if(Common.isPermission){
        if(Common.CustomSocketOn == 0){
            try {
                OnlineForeGroundService.locationManager.removeUpdates(OnlineForeGroundService.locationListener);
                //OnlineForeGroundService.locationListener = null;
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
    }


    Log.d(TAG_FOREGROUND_SERVICE, "Stop foreground service.");

    if (null != wakeLock && wakeLock.isHeld()) {
        wakeLock.release();
    }

    // Stop foreground service and remove the notification.
    stopForeground(true);

    // Stop the foreground service.
    stopSelf();
}

}

Here is the androidManifest entry for the service and WAKE_LOCK permission:

<uses-permission android:name="android.permission.WAKE_LOCK" />
<service
        android:name=".driver.service.OnlineForeGroundService"
        android:process=".process"
        android:enabled="true"
        android:exported="true" ></service>
jamejava
  • 11
  • 2
  • 2
    Foreground services don't mean you can't be killed, it just reduces your priority on the kill list. And they don't turn off doze. You need this app to be witelisted from power management by the user if you expect this to work with the screen off. – Gabe Sechan Sep 20 '18 at 03:43
  • 2
    Several phone manufacturers may also kill apps when the screen is off regardless of wake locks. They also provides a special whiltelist settings. – Dewey Reed Sep 20 '18 at 04:13
  • @Gabe Sechan that is not true, processes with foreground service are exempt from doze and app standby. Quote from Diane Hackborn "Apps that have been running foreground services (with the associated notification) are not restricted by doze." (from a comment on [this google plus post](https://plus.google.com/+AndroidDevelopers/posts/94jCkmG4jff). There is a [bug](https://code.google.com/p/android/issues/detail?id=193802) related to this on marshmallow that was fixed in nougat. It you are testing on marshmallow that might be a good place to look. – MidasLefko Sep 20 '18 at 05:44
  • @MidasLefko Unfortunately, I am testing on nougat and have implemented everything suggested change on my code with no success. I will now test whitelisting as suggested by Gabe and Dewey and report back. But even if that is the case, do I now have to include the instruction to whitelist the app and expect every driver to know how to do it? I can't remember ever manually whitelisting facebook app on the same nougat but Facebook service and others keep running during screen off. – jamejava Sep 20 '18 at 07:44
  • 1
    After 2 days, I am still stuck here. No new solution works, not even whitelisting the app in Battery Optimization. The only solution that I have tried that works consistently is leaving the app in the foreground when screen is going to sleep or lock. If another activity has the foreground when the the phone sleeps, my foreground service is killed in less than 5 minutes. But if my app has the foreground when the phone sleeps, everything works perfectly well no matter how long the sleep lasts. Can somebody help me fix this – jamejava Sep 22 '18 at 12:41
  • @jamejava: did you ever found a solution? – testing Mar 19 '20 at 13:41

2 Answers2

2

Please see this excellent site which provides a vast amount of information covering various different handset vendors and Android API versions.

This has helped me massively understand the very common Android developer problem of dealing with reports and feedback of foreground services being killed despite following the documented steps to ensure your service is setup correctly to avoid being shutdown.

https://dontkillmyapp.com/stock_android

The site includes mention of the Dianne Hackborn comments which are now no longer accessible on the Google plus (due to it's end of life).

Ultimately it seems that the solution to prevent your Foreground Service from being shutdown may vary across Android OS version and device manufacturer, but this site provides a good summary of the steps users can be directed to follow and also developers can implement (where possible) to try and mitigate this issue.

Jadent
  • 974
  • 8
  • 14
0

Check how you are starting your OnlineForeGroundService.

On Android Oreo and above it needs to be started with startForegroundService(Intent intent), not startService(Intent intent)

For example something like:

final Intent serviceIntent = new Intent(context, OnlineForeGroundService.class);
serviceIntent.setAction(OnlineForeGroundService.ACTION_START_FOREGROUND_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}
Sound Conception
  • 5,263
  • 5
  • 30
  • 47
  • Concept, I have corrected as you suggested to cover Oreo. However, my start code is right but does not work on nougat. – jamejava Sep 20 '18 at 07:55