Search code examples
androidgoogle-mapslocationandroid-geofence

Geofence pending intent firing too late when device is in deep sleep (doze) mode


I need to notify user when he is near by given place. I use Geofencing API for this. When i test app on Android emulator with mock location everything works fine. Same for real device with Mock Location. But when I walk and my phone is in deep sleep mode Geofence fires after 5 - 10 min. If i am inside geofences radius and I unlock my phone, open any app which use location my geofence triggers immediately. (Android 5.1, Motorolla moto G 1-st generation)

Below is the way, how I registered my geofence:

  public void registerLocation(RegisterAlarmRequestModel data) {
    if (isLocationDetectionAllowed() && isConnected) {
        GeofencingRequest geofencingRequest = prepareGeofencingRequest(prepareGeofence(data));
        PendingIntent pendingIntent = prepareIntent(data.getId());
        PendingResult<Status> result = GeofencingApi.addGeofences(
                googleApiClient, geofencingRequest, pendingIntent);
        Status status = result.await();
        if (status.isSuccess())
            Log.d("Location", "Geofence " + data.getId() + " has been registered");
    }
}

//preparing Geofence Pending Intent which will be triggered 
private PendingIntent prepareIntent(int alarmId) {
    Intent intent = new Intent(context, LocationRingingService.class);
    intent.putExtra(LocationRingingService.KEY_ALARM_ID, alarmId);
    return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

private GeofencingRequest prepareGeofencingRequest(Geofence geofence) {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder()
            .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            .addGeofence(geofence);
    return builder.build();
}

private Geofence prepareGeofence(RegisterAlarmRequestModel data) {
    Geofence geofence = new Geofence.Builder()
            .setRequestId(String.valueOf(data.getId()))
            .setCircularRegion(data.getLatitude(), data.getLongitude(), data.getRadius())
            .setLoiteringDelay(100)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
            .build();
    return geofence;
}

For receiving intent I am using IntentService:

@Override
protected void onHandleIntent(Intent intent) {
    Log.d("Location", "accepted intent: " + intent.toString());
    //database request
}

This is how i have registered my service in manifest:

<service
        android:name=".plugin.delivery.ringing.location.service.LocationRingingService"
        android:enabled="true"
        android:exported="true" />

Update: I need catch the moment when user just entered into geofence as accurate as possible. I have one idea: register geofence with radius greater than need (for example if need 100m radius, register geofence with 200-300m radius). And when user enters into Geophence with larger radius - start service with location udating to improve geofencing precision. And when user just entered - disable location service.


Solution

  • To improve it, Let's perform some checks

    1) Use broadcast receiver to get it triggered easily instead of service. And set priority with intent-filter.

    For e.g

        <receiver
            android:name=".youpackage.GeoReceiver"
            android:exported="true">
            <intent-filter android:priority="999">
                <action android:name="yourpackage.ACTION_RECEIVE_GEOFENCE" />
            </intent-filter>
        </receiver>
    

    And your pending intent will be :

    Intent intent = new Intent("yourpackage.ACTION_RECEIVE_GEOFENCE");
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    youractivity,
                    0,
                    intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
    

    2) As your GPS goes in sleep mode, we need to wake it up while creating Geofence. Once you create your geofence, you can start pinging your GPS until you will get ENTER transition. This would must help to get triggering it.

    public class GPSService extends Service implements GoogleApiClient.ConnectionCallbacks,    GoogleApiClient.OnConnectionFailedListener, LocationListener {
    
        GoogleApiClient mGoogleApiClient;
        LocationRequest locationRequest;
    
        public GPSService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
         // TODO: Return the communication channel to the service.
         throw new UnsupportedOperationException("Not yet implemented");
        }
    
        @Override
        public void onCreate() {
          super.onCreate();
    
    
         mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();
    
         mGoogleApiClient.connect();
      }
    
       @Override
       public void onLocationChanged(Location location) {
    
         Utility.ReadAndWriteData(this, Utility.readFileName(this), "Still Geofence is not triggered!!!");
    
        }
    
        @Override
        public void onConnected(Bundle bundle) {
    
            locationRequest = LocationRequest.create();
          locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            locationRequest.setFastestInterval(1000);
            locationRequest.setInterval(2000);
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,locationRequest,this);
    
        }
    
       @Override
       public void onConnectionFailed(ConnectionResult connectionResult)   {
    
      }
    
         @Override
         public void onConnectionSuspended(int i) {
    
         }
    
         @Override
         public void onDestroy() {
           super.onDestroy();
    
            if(mGoogleApiClient.isConnected()) {
    
         LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
    
           }
     } 
    

    And don't forgot to stop this service immediately when you get ENTER transition or It cause drain battery. This service is only to wake GPS up from sleep mode.