Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android background location update is not coming when the screen is off

We are working on mobile tracking app. For location updates, Fused location API is used with High Accuracy as priority.

Location update is required even when the screen is off. So, we are using background Service. Background Service is also acquiring partial WakeLock so that the device does not goes to sleep. In the background Service we request location updates with the pending update of the Service.

The problem is that we are only receiving location updates when the screen is on. As soon as the screen is off location updates stops coming. Also there is a Thread run by the Service which is not getting kill at any point.

Creating the location request again when the screen is off by BroadcastReceiver does not work either.

Here is the background Service class (RouteExecution):

private static final String TAG = "RouteExecution";

private static RouteExecution routeExecution = null;

private static GoogleApiClient mGoogleApiClient;
private static PendingIntent pendingIntent = null;
private PowerManager.WakeLock waitLock;
/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 */
public RouteExecution() {
    super(TAG);

    RouteExecution.routeExecution = this;
}

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

    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();

    mGoogleApiClient.connect();

    IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    BroadcastReceiver mReceiver = new PowerButtonReceiver();
    registerReceiver(mReceiver, filter);

    PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    waitLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
    waitLock.acquire();
}

@Override
protected void onHandleIntent(Intent intent) {

    if (LocationResult.hasResult(intent)) {

        LocationResult locationResult = LocationResult.extractResult(intent);
        Location location = locationResult.getLastLocation();

        GPSLocation gpsLocation = new GPSLocation(location);

        Log.d(TAG, "Location Accuracy: " + location.getAccuracy() + " "
                + " has: " + location.hasAccuracy() + " Provider: " + location.getProvider()
                + " long: " + location.getLongitude() + " lat: " + location.getLatitude());
    }
}



public boolean checkLocationPermission() {

    if (ActivityCompat.checkSelfPermission(routeExecution, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(routeExecution, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        //    ActivityCompat#requestPermissions
        // here to request the missing permissions, and then overriding
        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
        //                                          int[] grantResults)
        // to handle the case where the user grants the permission. See the documentation
        // for ActivityCompat#requestPermissions for more details.
        return false;
    }

    return true;
}

public void createLocationListener() {

    if (!this.checkLocationPermission()) {
        return;
    }

    LocationRequest mLocationRequest = LocationRequest.create();
    mLocationRequest.setInterval(5000);
    mLocationRequest.setFastestInterval(5000);
    mLocationRequest.setSmallestDisplacement(0);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    PendingResult<Status> statusPendingResult = LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
            mLocationRequest, pendingIntent);
}

@Override
public void onConnected(@Nullable Bundle bundle) {

    Log.d(TAG, mGoogleApiClient.isConnected() + "   On Connected");
    synchronized (this) {

            createLocationListener();
    }
}


public static GoogleApiClient getmGoogleApiClient() {
    return mGoogleApiClient;
}

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


    waitLock.release();
    mGoogleApiClient.disconnect();
}

public static RouteExecution getRouteExecution() {
    return routeExecution;
}

public static void setPendingIntent(PendingIntent pendingIntent) {
    RouteExecution.pendingIntent = pendingIntent;
}

Service is started using AlarmManager. Here is the extraction:

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent updateServiceIntent = new Intent(context, RouteExecution.class);
PendingIntent pendingUpdateIntent = PendingIntent.getService(context, 0, updateServiceIntent, 0);
RouteExecution.setPendingIntent(pendingUpdateIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP, 50000, pendingUpdateIntent);

BroadcastReceiver:

public class PowerButtonReceiver extends BroadcastReceiver {
    private static final String TAG = "PowerButton";

    @Override
    public void onReceive(Context context, Intent intent) {

        Log.d(TAG, "Power Button");
        if (RouteExecution.getRouteExecution() != null) {
            RouteExecution.getRouteExecution().createLocationListener();
        }
    }
}

How to keep getting location updates even when the screen is off.

Thanks for the help.

like image 599
pratick Avatar asked Nov 08 '22 09:11

pratick


1 Answers

First of all, aquiring a WakeLock in the Service is not reliable at all. The device can go back to sleep before that code gets executed.

I would call PendingIntent.getBroadcast() instead of PendingIntent.getService(), because a WakeLock is technically guaranteed during onReceive(), and start the Service from there.

Either aquire a WakeLock in onReceive(), start your Service and release the WakeLock from the Service, when appropriate

or

use WakefulBroadcastReceiver, which basically does the same, without having to implement most of that logic yourself.

Another possible problem is using AlarmManager.set() on Marshmallow devices.

Alarms set by this method will not get fired on these devices if Doze is active.

Use setAndAllowWhileIdle() instead of set() on API level 23+.

like image 116
earthw0rmjim Avatar answered Nov 15 '22 05:11

earthw0rmjim