Location service got stopped by Doze mode

1.4k views Asked by At

I'm developing sport tracking app that uses location manager and gps provider for getting location updates every second even if the screen is off and the phone is in the pocket.

When user pressed start button in my activity I start service in foreground, display notification and register location listener.

Service starts receiving location updates and writes them into my track file.

Suddenly I get log message 'Power manager idle mode: true', the phone goes into Doze mode and my sevice stops getting any location update until the phone wakes up.

I read docs about Doze mode and see that it shouldn't affect location services, but it does in my case.

May be I'm doing something wrong. Here is the code of my service, any help is really appreciated.

public class LocService
    extends Service
implements LocationListener, GpsStatus.Listener
{

private String mName;
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private LocationManager locationManager;

public LocService(String name)
{
    super();
    mName = name;
}

private final class ServiceHandler extends Handler
{
    public ServiceHandler(Looper looper)
    {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg)
    {
        if (msg != null && msg.obj != null)
        {
            onHandleIntent((Intent)msg.obj);
        }
        else
        {
            logMessage("msg for intent is not good");
        }
    }
}

@Override
public void onCreate()
{
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    {
        logMessage("Enabling Doze mode listener");
        IntentFilter filter = new IntentFilter();
        filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
        registerReceiver(new BroadcastReceiver()
        {
            @Override
            public void onReceive(Context context, Intent intent)
            {
                onDeviceIdleChanged();
            }
        }, filter);
    }

    locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
}

@TargetApi(Build.VERSION_CODES.M)
private void onDeviceIdleChanged()
{
    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    if(powerManager != null)
    {
        logMessage("Power manager idle mode: " + powerManager.isDeviceIdleMode());
    } else
    {
        logMessage("Power manager idle changed to ?");
    }
}

protected void onHandleIntent(Intent intent)
{
    //call start/stop location logging on proper intent
    if(intent.getIntExtra("cmd", -1) == 1)
    {
        startLogging();
    } else
    {
        stopLogging();
    }
}

private void startLogging()
{
    logMessage("LocationService.startLogging");
    try
    {
        locationManager.addGpsStatusListener(this);
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 0, this);
        logMessage("requesting gps updates");

        startForeground(ONGOING_NOTIFICATION, getNotification(-1, -1, true, false));
        logMessage("Sending foreground service notification");

    }
    catch (SecurityException ex)
    {
        logMessage(" SecurityException while requesting location info: " + ex);
    }
}

private void stopLogging()
{
    try
    {
        locationManager.removeUpdates(this);

        stopForeground(true);
        notificationManager.cancel(ONGOING_NOTIFICATION);
    }
    catch (SecurityException ex)
    {
        logMessage(" SecurityException on stopLogging with location manager: " + ex);
    }

}


@Override
public void onLocationChanged(Location location)
{
    //save location lat, lon directly to track file
    //flush file
}

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

@Override
public void onProviderEnabled(String provider)
{
    logMessage("Location provider enabled " + provider);
}

@Override
public void onProviderDisabled(String provider)
{
    logMessage("Location provider disabled " + provider);
}

@Override
public void onGpsStatusChanged(int event)
{
    try
    {
        logMessage(" *** onGpsStatusChanged with " + event);
        GpsStatus status = locationManager.getGpsStatus(null);
        int inFix = 0;
        int total = 0;
        for (GpsSatellite satellite : status.getSatellites())
        {
            if (satellite.usedInFix()) inFix++;
            total++;
        }
        logMessage(" Sats: " + total + " in fix " + inFix);
    }
    catch (SecurityException sex)
    {
    }
    catch (Exception ex)
    {
    }
}

@Override
public void onStart(Intent intent, int startId)
{
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
    onStart(intent, startId);
    return START_STICKY;
}

@Override
public void onDestroy()
{
    mServiceLooper.quit();
    try
    {
        locationManager.removeUpdates(this);
    }
    catch (SecurityException ex)
    {
        logMessage(" SecurityException on Destroy service with location manager: " + ex);
    }
}

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

private void logMessage(String msg)
{
    Log.i("LocServ", msg);
}
}
1

There are 1 answers

1
phnmnn On

It is not a given that when ACTION_DEVICE_IDLE_MODE_CHANGED is fired, doze was either turned on or off. There are more factors that can affect idle mode.

Try to create and acquire WakeLock.

    PowerManager.WakeLock getLock(Context context, String lockName) {
        if (wakeLock == null) {
            PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName);
        }
        return wakeLock;
    }

    //on start service
    getLock(ctxt.getApplicationContext(), "lockName").acquire();


    //on destroy service
    @Override
    public void onDestroy() {
        PowerManager.WakeLock lock = getLock(this.getApplicationContext(), "lockName");
        if (lock.isHeld()) {
            lock.release();
        }
        super.onDestroy();
    }