Search code examples
androidlocationmanagerandroid-gpsandroid-doze

Getting location updates when in doze mode


I'm trying to get location updates in my android app when in doze mode, this used to work up to android 5.x. With android 6 and the advent of doze, no matter what I do, the updates stop at some point. After reading a few articles and stackoverflow answers on the topic I made the following changes:

  • made my service a foreground service
  • made the service hold a partial wake lock
  • I've given my app the WAKE_LOCK permission
  • made the service run in a separate process (for a workaround to some android bug)
  • I've disabled battery optimizations for my app

But still, I'm not getting location updates when doze kicks in. I've verified that my service thread keeps running when doze starts (by periodically logging a message), but somehow the location manager stops sending updates. The documentation on doze and the LocationManager is pretty scant in this regard, so I was wondering if somebody knows of a way to keep the location manager alive in doze? Is there some method on LocationManager that if called periodically will keep the LocationManager active? Note that I'm interested in GPS updates only, with a high update frequency, once every second.


Solution

  • In the end I found a workaround for the issue, after reading through the source code of com.android.internal.location.GpsLocationProvider I noticed that sending it an com.android.internal.location.ALARM_WAKEUP Intent prevented the location provider from dozing. So to keep the GPS from dozing I broadcast the Intent every 10 seconds, I added the following in my service class:

    [...]
    private Handler handler;
    private PowerManager powerManager;
    
    private PendingIntent wakeupIntent;
    private final Runnable heartbeat = new Runnable() {
        public void run() {
            try {
                if (isRecording && powerManager != null && powerManager.isDeviceIdleMode()) {
                    LOG.trace("Poking location service");
                    try {
                        wakeupIntent.send();
                    } catch (SecurityException | PendingIntent.CanceledException e) {
                        LOG.info("Heartbeat location manager keep-alive failed", e);
                    }
                }
            } finally {
                if (handler != null) {
                    handler.postDelayed(this, 10000);
                }
            }
        }
    };
    
    @Override
    public void onCreate() {
        handler = new Handler();
        wakeupIntent = PendingIntent.getBroadcast(getBaseContext(), 0,
            new Intent("com.android.internal.location.ALARM_WAKEUP"), 0);
        locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
        powerManager = (PowerManager) getSystemService(POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TrackService");
        [...]
    }