react-native-background-geolocation icon indicating copy to clipboard operation
react-native-background-geolocation copied to clipboard

`getCurrentPosition` does not honor timeout and gets stuck when no connection is available

Open curtisy1 opened this issue 3 years ago • 1 comments

Your Environment

  • Plugin version: tested on 4.8.0 and 4.8.2
  • Platform: Android
  • OS version: Android 12
  • Device manufacturer / model: Oppo / PDEM30
  • React Native version (react-native -v): tested on both 0.64.2 and 0.68.2
  • Plugin config

For our production app, we use this

{
  desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
  reset: true,
  stopTimeout: 1,
  distanceFilter: 1000,
  heartbeatInterval: 60,
  debug: false,
  logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE,
  stopOnTerminate: true,
  startOnBoot: false,
  url: baseUrl + "/api/v1/position", // baseUrl is our hosted api but it also works when using the demo server
  batchSync: true,
  autoSync: false,
  notification: {
    title: "TrackPilot Go",
    text: translate("LOCATION_SERVICE_ACTIVATED_MESSAGE"),
    priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_MAX,
  },
}

The demo app uses

{
      // Debug
      reset: false,
      debug: true,
      logLevel: BackgroundGeolocation.LOG_LEVEL_VERBOSE,
      transistorAuthorizationToken: token,
      // Geolocation
      desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_NAVIGATION,
      distanceFilter: 10,
      stopTimeout: 5,
      // Permissions
      locationAuthorizationRequest: 'Always',
      backgroundPermissionRationale: {
        title: "Allow {applicationName} to access this device's location even when closed or not in use.",
        message: "This app collects location data to enable recording your trips to work and calculate distance-travelled.",
        positiveAction: 'Change to "{backgroundPermissionOptionLabel}"',
        negativeAction: 'Cancel'
      },
      // HTTP & Persistence
      autoSync: true,
      maxDaysToPersist: 14,
      // Application
      stopOnTerminate: false,
      startOnBoot: true,
      enableHeadless: true
    }

Expected Behavior

The call to getCurrentPosition should throw or enter the error callback after the set timeout

Actual Behavior

It gets stuck forever. In rare cases, when a location can be obtained afterwards or when shaking the device, it continues with the success callback.

Steps to Reproduce

  1. Find a place with very bad GPS and WIFI
  2. In the demo app, trigger a getCurrentPosition call
  3. Now comes a weird thing, I'm not sure if this is necessary but sometimes 2 would still find a location. If it does, quit the app and try again

The code is taken from the sample app, modified to try with callback and promises. Both yield the same results.

BackgroundGeolocation.getCurrentPosition({
      persist: true,
      samples: 1,
      timeout: 30,
      extras: {
        getCurrentPosition: true
      }
    }, (location:Location) => {
      console.log('[getCurrentPosition] success: ', location);
    }, (error:LocationError) => {
      console.warn('[getCurrentPosition] error: ', error);
    });
BackgroundGeolocation.getCurrentPosition({
      persist: true,
      samples: 1,
      timeout: 30,
      extras: {
        getCurrentPosition: true
      }
    }).then((location:Location) => {
      console.log('[getCurrentPosition] success: ', location);
    }).catch((error:LocationError) => {
      console.warn('[getCurrentPosition] error: ', error);
    });

Context

We are experiencing issues with getCurrentPosition when in places where a position cannot be determined, e.g. an insulated basement or a Faraday cage, for testing purposes. This does not happen when location is disabled globally in the device.

Debug logs

Since these are huge (3.5k lines long), I figured a paste would be better than the issue. You can find it here

curtisy1 avatar Sep 20 '22 08:09 curtisy1

modified to try with callback and promises

There is no functional difference between the two forms. If you use the old api and provide callback args, the plugin's Javascript api simply feeds your supplied callbacks into its internal Promise API. Ultimately, it's all Promise.

I will attempt to reproduce this.

christocracy avatar Sep 20 '22 14:09 christocracy

As soon as we upgraded from 4.4.4 to 4.8.2 we started seeing this issue. We have critical part of our app where we block the user from pressing a continue button util we either get a Promise resolve or reject.

@curtisy1 says it does not honour the timeout. I'll expand on that by saying it doesn't FULLY honour the time out. It seems like it stops trying to get a fix after X seconds but doesn't send the reject.

Testing with various timeouts

  • Testing with 3 users in different locations on Android 10 & Android 12.
  • Our devices get config from config server so I can change the timeout on the fly.

RESULTS Low Timeout Config (2 seconds)

  • User 1 - NEVER worked, always hangs.
  • User 2 - Only worked once, most just hangs.
  • User 3 - Sometimes resolved with a GPS location but mostly failed. When it worked it seem to work a few times back to back.

RESULTS High Timeout Config (60 seconds)

  • User 1 - WORKED, resolved after 6-12 seconds.
  • User 2 - WORKED, resolved within 3-6 seconds.
  • User 3 - WORKED, resolved within 5 seconds, sometimes less than 2 seconds.

Library Version 4.4.4 It always either retuned the GPS location within the timeout value or we got a promise reject when the timeout had expired, e.g. after 2 seconds.

Library Version 4.8.2 It either:

  • Returns the GPS location within the timeout OR
  • hangs and never returns a resolve or a reject

WORKAROUND?

Is there any suggested workaround for this that could safely abort the call after X seconds?

Note we did make 1 other change as the upgrade to 4.8.2 wouldn't work without it: implementation ("androidx.appcompat:appcompat:1.3.1") {version {strictly '1.3.1'}}

DanW82 avatar Oct 12 '22 17:10 DanW82

we replicate the error on versions 4.8.1 and 4.8.2: timeout does not work. it is not possible to obtain neither "location" nor "locationerror"

BackgroundGeolocation.getCurrentPosition({
  timeout: timeout,          // n second timeout to fetch location
  maximumAge: 30000,    // Accept the last-known-location if not older than 30 sec.
  desiredAccuracy: 10,  // Try to fetch a location with an accuracy of `10` meters.
  samples: 1,           // How many location samples to attempt.
}, (location) => {
  console.log('***** getCurrentPosition ok:' + JSON.stringify(location));
  if (Url != null) {
    UrlCorrente = Url;
    setCRefresh(String(++count)); // serve a forzare un cambio di stato e di conseguenza un refresh della WebView
    console.log("** count: " + count);
  }
}, (locationerror) => {
  console.log('***** getCurrentPosition error:' + JSON.stringify(locationerror));
  if (Url != null) {
    UrlCorrente = Url;
    setCRefresh(String(++count)); // serve a forzare un cambio di stato e di conseguenza un refresh della WebView
    console.log("** count: " + count);
  }
});

nicolaturcoep avatar Oct 21 '22 13:10 nicolaturcoep

Fixed in 4.9.3

It was a simple logic bug with the timeout param.

christocracy avatar Oct 26 '22 15:10 christocracy

Thanks! I can confirm release 4.9.3 fixes this bug (though I wonder how you reproduced it in the end :smile: )

curtisy1 avatar Oct 27 '22 06:10 curtisy1

wonder how you reproduced

By setting a low timeout, eg 0 or 1

christocracy avatar Oct 27 '22 09:10 christocracy