title: Android onLocationChanged not obeying update minimum duration and update older than previous one
body: <p>I am experiencing some strange behaviour from monitoring the times of updated locations from the GPS LocationListener.</p>
  
  <p>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 <strong>earlier</strong> than the previous location?</p>
  
  <p>Example of both seen as fraction of a second before previous location (logcat output, edited for clarity):</p>
  
  <pre><code>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
  </code></pre>
  
  <p>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)</p>
  
  <p>My entire tracker class (minus the dozens of debugging logs):</p>
  
  <pre><code>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
                  &amp;&amp; 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() &gt; previousLocation.getTime();
      }
  
  
      //-return true if location update is more accurate than previous location
      public boolean checkUpdateAccuracy(Location location) {
          return location.getAccuracy() &lt; 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;
      }
  }
  </code></pre>
  
  <p>What is the reason for this behaviour? </p>
  
  <h2>Edit</h2>
  
  <p>Here is a better example of new location update being timestamped prior to previous:</p>
  
  <pre><code>updated location:
      Time: 20/02/22 06:44:57.346
  previous location:
      Time: 20/02/22 06:45:21.370
  </code></pre>