1

I am experiencing some strange behaviour from monitoring the times of updated locations from the GPS LocationListener.

Sometimes (though intermittently) I will see the updated location have a timestamp a fraction of a second off (despite the minimum update being set to 5000milliseconds in requestUpdates) or sometimes even earlier than the previous location?

Example of both seen as fraction of a second before previous location (logcat output, edited for clarity):

GpsTracker_v4: onLocationChanged: Warning: location update is older than previous location
D/GpsTracker_v4: 
    previous location:
        provider: network
        time:20/02/22 06:07:17.468
    ---
    new location:
        provider: network
        time: 20/02/22 06:07:17.467

I have logged enough by now to (fairly) confidently claim this is not due to a gps/network provider conflict, or due to it only being milliseconds apart (example of longer durations up to seconds long included at bottom of post)

My entire tracker class (minus the dozens of debugging logs):

public class GpsTracker_v4 {
    /*--------------------------------------
        CONSTANTS
    --------------------------------------*/
    private static final String TAG = GpsTracker_v4.class.getSimpleName();

    // location updates interval - 5sec
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 5000;


    /*--------------------------------------
        MEMBER VARIABLES
    --------------------------------------*/
    //---VARIABLES---
    Context context;
    LocationManager locationManager;
    LocationListener locationListener;

    Location previousLocation = null;


    /*--------------------------------------
        CONSTRUCTOR
    --------------------------------------*/
    //-create manager and initialise location listener
    public GpsTracker_v4(Context c) {
        this.context = c;

        locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);

        locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {

                if (location != null) {
                    if (previousLocation != null) {

                        //check location update is more recent than previous
                        if (checkUpdateTime(location)) {
                            Log.d(TAG, "onLocationChanged: new location more recent");
                        } else {
                            Log.w(TAG, "onLocationChanged: Warning: location update is " +
                                    "older than previous location");
                        }

                        Log.d(TAG, "previous location:\n" +
                                "provider: " + previousLocation.getProvider() + "\n" +
                                "time:" + getDateString(previousLocation.getTime()) + "\n" +
                                "---\n" +
                                "new location:\n" +
                                "provider: " + location.getProvider() + "\n" +
                                "time: " + getDateString(location.getTime()));

                        //check location is more accurate than previous location
                        if (checkUpdateAccuracy(location)) {
                            Log.d(TAG, "onLocationChanged: location update is more accurate");
                        } else {
                            Log.w(TAG, "onLocationChanged: Warning: location update is " +
                                    "less accurate or equal to previous");
                        }

                        Log.d(TAG, "Prev location:\n" +
                                "provider: " + previousLocation.getProvider() + "\n" +
                                "accuracy: " + previousLocation.getAccuracy() + "\n" +
                                "---\n" +
                                "New location:\n" +
                                "provider: " + location.getProvider() + "\n" +
                                "accuracy: " + location.getAccuracy());
                    } else {
                        //no previous location yet
                        Log.d(TAG, "onLocationChanged: previousLocation is null: " +
                                "using first location update:");
                    }

                    //replace previous location with updated
                    previousLocation = location;
                    //debugging attempt: remove all content of location 
                    location = null;
                } else {
                    //null location passed
                    Log.e(TAG, "onLocationChanged: Error: passed location is NULL");
                    //todo: handle
                }
            }

            @Override
            public void onStatusChanged(String s, int i, Bundle bundle) {
                //todo:
            }

            @Override
            public void onProviderEnabled(String s) {
                //todo:
            }

            @Override
            public void onProviderDisabled(String s) {
                //todo:
            }
        };

        beginRequestUpdates();
    }


    /*--------------------------------------
        METHODS
    --------------------------------------*/
    //-begin requesting updates from both gps and network providers
    public void beginRequestUpdates() {
        //permission check (IDE insists its done here, not earlier or in another method)
        if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(
                context, Manifest.permission.ACCESS_COARSE_LOCATION)
                != PackageManager.PERMISSION_GRANTED) {
            Log.e(TAG, "checkPermission: Error: Permission not been granted.);
            return;
        }

        //begin GPs updates
        locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                UPDATE_INTERVAL_IN_MILLISECONDS,
                0,
                locationListener,
                Looper.getMainLooper());
        Log.d(TAG, "beginRequestUpdates: GPS updates requested");

        //begin network updates
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                UPDATE_INTERVAL_IN_MILLISECONDS,
                0,
                locationListener,
                Looper.getMainLooper());
        Log.d(TAG, "beginRequestUpdates: Network updates requested");
    }


    /*--------------------------------------
        HELPER METHODS
    --------------------------------------*/
    //-return true if time of location update is more recent than previous update time
    public boolean checkUpdateTime(Location location) {
        return location.getTime() > previousLocation.getTime();
    }


    //-return true if location update is more accurate than previous location
    public boolean checkUpdateAccuracy(Location location) {
        return location.getAccuracy() < previousLocation.getAccuracy();
    }


    //-get user-readable date/time from milli (in year-month-day format)
    private String getDateString(long milliSeconds) {
        String dateFormat = "yy/MM/dd hh:mm:ss.SSS";
        SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(milliSeconds);
        return formatter.format(calendar.getTime());
    }


    public void printLocation(Location location) {
        System.out.println("" +
                "Provider: " + location.getProvider() + "\n" +
                "Latitude: " + location.getLatitude() + "\n" +
                "Longitude: " + location.getLongitude() + "\n" +
                "Accuracy: " + location.getAccuracy() + "\n" +
                "Time: " + getDateString(location.getTime()) + "\n");
    }


    /*--------------------------------------
        MUTATORS
    --------------------------------------*/
    //-set listener from outside class
    public void setListener(LocationListener listener) {
        this.locationListener = listener;
    }
}

What is the reason for this behaviour?

Edit

Here is a better example of new location update being timestamped prior to previous:

updated location:
    Time: 20/02/22 06:44:57.346
previous location:
    Time: 20/02/22 06:45:21.370
halfer
  • 19,824
  • 17
  • 99
  • 186
SSQ
  • 83
  • 8
  • 1
    My first thought was, "well, if this was the GPS provider, then I'd be concerned", but then the `getTime()` docs go out of their way to mention that `the UTC time on a device is not monotonic...`, which raises the possibility that those time hacks are coming directly from the system clock, and your system clock could be changing. – greeble31 Feb 22 '20 at 23:43
  • thanks for the heads up, i'll write an activity to display these values and run some experiments outside via GPS and see if it does the same. Do you know where the time value comes from using network provider? (mobile device? wifi router? the laptop running androidstudios? etc? (in any case, what would cause this erratic system clock? ive had a look online since reading your reply but no clues as of yet. – SSQ Feb 23 '20 at 11:20
  • 1
    Those timestamps most likely come from your system clock. The discontinuities are unexpectedly large, but maybe you have a defective device, or live in an area where the cell network clocks aren't synced, or where your LAN NTP provider is different than your mobile NTP provider. I'm just guessing. But, you can take time hacks yourself (using `System.currentTimeMillis()`) and see how orderly they are, compared to `getTime()`. – greeble31 Feb 23 '20 at 16:31
  • thanks for the idea, ive started printing the sys and location millis together and there is inconsistencies (expected as its a phone connected via adb) as i have no idea what to do with them and im wasting even more time on this, im going to just throw away any "wrong" locations to save time. (i keep getting identical ones as well: EXACT same millisecond timestamp on them, definitely not a code problem as i have put all the values back to null at the start of onLocationChanged, its got to be something in how that listener is (mal)functioning – SSQ Feb 23 '20 at 17:46
  • i have found evidence that something is happening other than expected due to the frequency of updates being received: "elapsed time between location updates will never be less than minTime, but it can be greater" from: https://blog.codecentric.de/en/2014/05/android-gps-positioning-location-strategies/ Im guessing that it does not wait for the minTime to expire before retreiving the next location, but already has it stored somewhere, waiting to deliver it? if so, this is where the problem will lie? as i assume there to be a queue somewhere that i cannot find? – SSQ Feb 23 '20 at 17:50
  • I don't know that any contracts are being violated, here. For all I know, the `NETWORK_PROVIDER` simply delivers the same fix, over and over again, until it gets a new one. It's not like a GPS. And, maybe the out-of-order fix times are just caused by a wonky system clock. And, the 5 second update interval is only being violated if you can prove the OS _queued_ the fixes at a quicker interval, using a coherent, monotonic clock (such as `System.elapsedRealtimeNanos()`). – greeble31 Feb 23 '20 at 18:13
  • 1
    I may have overlooked the obvious here: i have both gps and network updates requested, if one gets a new location, it would be possible the other is the one being passed to the listener instead at that moment. i appriciate the help – SSQ Feb 23 '20 at 18:39

0 Answers0