Skip to content

Commit 2eeeec2

Browse files
author
Nick Pelly
committed
Improve Location object.
Add getElapsedRealtimeNano(): Currently Location just has getTime() and setTime() based on UTC time. This is entirely unreliable since it is not guaranteed monotonic. There is a lot of code that compares fix age based on deltas - and it is all broken in the case of a system clock change. System clock can change when switching cellular networks (and in some cases when switching towers). Document the meaning of getAccuracy(): It is the horizontal, 95% confidence radius. Make some fields mandatory if they are reported by a LocationProvider: All Locations returned by a LocationProvider must include at the minimum a lat, long, timestamps, and accuracy. This is necessary to perform fused location. There are no public API's for applications to feed locations into a location provider so this should not cause any breakage. If a LocationProvider does not fill in enough fields on a Location object then it is dropped, and logged. Bug: 4305998 Change-Id: I7df77125d8a64e174d7bc8c2708661b4f33461ea
1 parent b8acd06 commit 2eeeec2

File tree

7 files changed

+137
-14
lines changed

7 files changed

+137
-14
lines changed

api/current.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10504,6 +10504,7 @@ package android.location {
1050410504
method public float getAccuracy();
1050510505
method public double getAltitude();
1050610506
method public float getBearing();
10507+
method public long getElapsedRealtimeNano();
1050710508
method public android.os.Bundle getExtras();
1050810509
method public double getLatitude();
1050910510
method public double getLongitude();
@@ -10523,6 +10524,7 @@ package android.location {
1052310524
method public void setAccuracy(float);
1052410525
method public void setAltitude(double);
1052510526
method public void setBearing(float);
10527+
method public void setElapsedRealtimeNano(long);
1052610528
method public void setExtras(android.os.Bundle);
1052710529
method public void setLatitude(double);
1052810530
method public void setLongitude(double);

location/java/android/location/Location.java

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.os.Bundle;
2020
import android.os.Parcel;
2121
import android.os.Parcelable;
22+
import android.os.SystemClock;
2223
import android.util.Printer;
2324

2425
import java.text.DecimalFormat;
@@ -59,6 +60,7 @@ public class Location implements Parcelable {
5960

6061
private String mProvider;
6162
private long mTime = 0;
63+
private long mElapsedRealtimeNano = 0;
6264
private double mLatitude = 0.0;
6365
private double mLongitude = 0.0;
6466
private boolean mHasAltitude = false;
@@ -84,6 +86,7 @@ public class Location implements Parcelable {
8486

8587
public void dump(Printer pw, String prefix) {
8688
pw.println(prefix + "mProvider=" + mProvider + " mTime=" + mTime);
89+
pw.println(prefix + "mElapsedRealtimeNano=" + mElapsedRealtimeNano);
8790
pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
8891
pw.println(prefix + "mHasAltitude=" + mHasAltitude + " mAltitude=" + mAltitude);
8992
pw.println(prefix + "mHasSpeed=" + mHasSpeed + " mSpeed=" + mSpeed);
@@ -118,6 +121,7 @@ public Location(Location l) {
118121
public void set(Location l) {
119122
mProvider = l.mProvider;
120123
mTime = l.mTime;
124+
mElapsedRealtimeNano = l.mElapsedRealtimeNano;
121125
mLatitude = l.mLatitude;
122126
mLongitude = l.mLongitude;
123127
mHasAltitude = l.mHasAltitude;
@@ -137,6 +141,7 @@ public void set(Location l) {
137141
public void reset() {
138142
mProvider = null;
139143
mTime = 0;
144+
mElapsedRealtimeNano = 0;
140145
mLatitude = 0;
141146
mLongitude = 0;
142147
mHasAltitude = false;
@@ -467,23 +472,62 @@ public void setProvider(String provider) {
467472
}
468473

469474
/**
470-
* Returns the UTC time of this fix, in milliseconds since January 1,
475+
* Return the UTC time of this fix, in milliseconds since January 1,
471476
* 1970.
477+
* <p>Note that the UTC time on a device is not monotonic: it
478+
* can jump forwards or backwards unpredictably. So always use
479+
* {@link #getElapsedRealtimeNano()} when calculating time deltas.
480+
* <p>On the other hand, {@link #getTime()} is useful for presenting
481+
* a human readable time to the user, or for carefully comparing
482+
* location fixes across reboot or across devices.
483+
* <p>This method will always return a valid timestamp on
484+
* Locations generated by a {@link LocationProvider}.
485+
*
486+
* @return time of fix, in milliseconds since January 1, 1970.
472487
*/
473488
public long getTime() {
474489
return mTime;
475490
}
476491

477492
/**
478-
* Sets the UTC time of this fix, in milliseconds since January 1,
493+
* Set the UTC time of this fix, in milliseconds since January 1,
479494
* 1970.
495+
*
496+
* @param time UTC time of this fix, in milliseconds since January 1, 1970
480497
*/
481498
public void setTime(long time) {
482499
mTime = time;
483500
}
484501

485502
/**
486-
* Returns the latitude of this fix.
503+
* Return the time of this fix, in elapsed real-time since system boot.
504+
* <p>This value can be reliably compared to
505+
* {@link android.os.SystemClock#elapsedRealtimeNano()},
506+
* to calculate the age of a fix, and to compare Location fixes, since
507+
* elapsed real-time is guaranteed monotonic for each system boot, and
508+
* continues to increment even when the system is in deep sleep.
509+
* <p>This method will always return a valid timestamp on
510+
* Locations generated by a {@link LocationProvider}.
511+
*
512+
* @return elapsed real-time of fix, in nanoseconds since system boot.
513+
*/
514+
public long getElapsedRealtimeNano() {
515+
return mElapsedRealtimeNano;
516+
}
517+
518+
/**
519+
* Set the time of this fix, in elapsed real-time since system boot.
520+
*
521+
* @param time elapsed real-time of fix, in nanoseconds since system boot.
522+
*/
523+
public void setElapsedRealtimeNano(long time) {
524+
mElapsedRealtimeNano = time;
525+
}
526+
527+
/**
528+
* Return the latitude of this fix.
529+
* <p>This method will always return a valid latitude on
530+
* Locations generated by a {@link LocationProvider}.
487531
*/
488532
public double getLatitude() {
489533
return mLatitude;
@@ -497,7 +541,9 @@ public void setLatitude(double latitude) {
497541
}
498542

499543
/**
500-
* Returns the longitude of this fix.
544+
* Return the longitude of this fix.
545+
* <p>This method will always return a valid longitude on
546+
* Locations generated by a {@link LocationProvider}.
501547
*/
502548
public double getLongitude() {
503549
return mLongitude;
@@ -619,16 +665,27 @@ public void removeBearing() {
619665
}
620666

621667
/**
622-
* Returns true if the provider is able to report accuracy information,
623-
* false otherwise. The default implementation returns false.
668+
* Return true if this Location has an associated accuracy.
669+
* <p>All Location objects generated by a {@link LocationProvider}
670+
* will have an accuracy.
624671
*/
625672
public boolean hasAccuracy() {
626673
return mHasAccuracy;
627674
}
628675

629676
/**
630-
* Returns the accuracy of the fix in meters. If hasAccuracy() is false,
631-
* 0.0 is returned.
677+
* Return the accuracy of this Location fix.
678+
* <p>Accuracy is measured in meters, and indicates the
679+
* radius of 95% confidence.
680+
* In other words, there is a 95% probability that the
681+
* true location is within a circle centered at the reported
682+
* location, with radius of the reported accuracy.
683+
* <p>This is only a measure of horizontal accuracy, and does
684+
* not indicate the accuracy of bearing, velocity or altitude
685+
* if those are included in this Location.
686+
* <p>If {@link #hasAccuracy} is false, 0.0 is returned.
687+
* <p>All Location object generated by a {@link LocationProvider}
688+
* will have a valid accuracy.
632689
*/
633690
public float getAccuracy() {
634691
return mAccuracy;
@@ -652,6 +709,37 @@ public void removeAccuracy() {
652709
mHasAccuracy = false;
653710
}
654711

712+
/**
713+
* Return true if this Location object has enough data set to
714+
* be considered a valid fix from a {@link LocationProvider}.
715+
* @see #makeComplete
716+
* @hide
717+
*/
718+
public boolean isComplete() {
719+
if (mProvider == null) return false;
720+
if (!mHasAccuracy) return false;
721+
if (mTime == 0) return false;
722+
if (mElapsedRealtimeNano == 0) return false;
723+
return true;
724+
}
725+
726+
/**
727+
* Helper to fill in incomplete fields.
728+
* Only use this to assist in backwards compatibility
729+
* with Location objects received from applications.
730+
* @see #isComplete
731+
* @hide
732+
*/
733+
public void makeComplete() {
734+
if (mProvider == null) mProvider = "?";
735+
if (!mHasAccuracy) {
736+
mHasAccuracy = true;
737+
mAccuracy = 100.0f;
738+
}
739+
if (mTime == 0) mTime = System.currentTimeMillis();
740+
if (mElapsedRealtimeNano == 0) mElapsedRealtimeNano = SystemClock.elapsedRealtimeNano();
741+
}
742+
655743
/**
656744
* Returns additional provider-specific information about the
657745
* location fix as a Bundle. The keys and values are determined
@@ -681,6 +769,7 @@ public void setExtras(Bundle extras) {
681769
@Override public String toString() {
682770
return "Location[mProvider=" + mProvider +
683771
",mTime=" + mTime +
772+
",mElapsedRealtimeNano=" + mElapsedRealtimeNano +
684773
",mLatitude=" + mLatitude +
685774
",mLongitude=" + mLongitude +
686775
",mHasAltitude=" + mHasAltitude +
@@ -700,6 +789,7 @@ public Location createFromParcel(Parcel in) {
700789
String provider = in.readString();
701790
Location l = new Location(provider);
702791
l.mTime = in.readLong();
792+
l.mElapsedRealtimeNano = in.readLong();
703793
l.mLatitude = in.readDouble();
704794
l.mLongitude = in.readDouble();
705795
l.mHasAltitude = in.readInt() != 0;
@@ -726,6 +816,7 @@ public int describeContents() {
726816
public void writeToParcel(Parcel parcel, int flags) {
727817
parcel.writeString(mProvider);
728818
parcel.writeLong(mTime);
819+
parcel.writeLong(mElapsedRealtimeNano);
729820
parcel.writeDouble(mLatitude);
730821
parcel.writeDouble(mLongitude);
731822
parcel.writeInt(mHasAltitude ? 1 : 0);

location/java/android/location/LocationManager.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@
1919
import android.app.PendingIntent;
2020
import android.content.Context;
2121
import android.content.Intent;
22+
import android.os.Build;
2223
import android.os.Bundle;
2324
import android.os.Looper;
2425
import android.os.RemoteException;
2526
import android.os.Handler;
2627
import android.os.Message;
28+
import android.os.SystemClock;
2729
import android.util.Log;
2830

2931
import com.android.internal.location.DummyLocationProvider;
@@ -1220,8 +1222,11 @@ public void removeTestProvider(String provider) {
12201222
}
12211223

12221224
/**
1223-
* Sets a mock location for the given provider. This location will be used in place
1224-
* of any actual location from the provider.
1225+
* Sets a mock location for the given provider.
1226+
* <p>This location will be used in place of any actual location from the provider.
1227+
* The location object must have a minimum number of fields set to be
1228+
* considered a valid LocationProvider Location, as per documentation
1229+
* on {@link Location} class.
12251230
*
12261231
* @param provider the provider name
12271232
* @param loc the mock location
@@ -1230,8 +1235,20 @@ public void removeTestProvider(String provider) {
12301235
* or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
12311236
* Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
12321237
* @throws IllegalArgumentException if no provider with the given name exists
1238+
* @throws IllegalArgumentException if the location is incomplete
12331239
*/
12341240
public void setTestProviderLocation(String provider, Location loc) {
1241+
if (!loc.isComplete()) {
1242+
if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
1243+
// for backwards compatibility, allow mock locations that are incomplete
1244+
Log.w(TAG, "Incomplete Location object", new Throwable());
1245+
loc.makeComplete();
1246+
} else {
1247+
throw new IllegalArgumentException(
1248+
"Location object not complete. Missing timestamps or accuracy?");
1249+
}
1250+
}
1251+
12351252
try {
12361253
mService.setTestProviderLocation(provider, loc);
12371254
} catch (RemoteException ex) {

services/java/com/android/server/LocationManagerService.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1532,6 +1532,11 @@ public void reportLocation(Location location, boolean passive) {
15321532
throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
15331533
}
15341534

1535+
if (!location.isComplete()) {
1536+
Log.w(TAG, "Dropping incomplete location: " + location);
1537+
return;
1538+
}
1539+
15351540
mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
15361541
Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
15371542
m.arg1 = (passive ? 1 : 0);
@@ -1588,7 +1593,8 @@ private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, Updat
15881593

15891594
// Check whether sufficient time has passed
15901595
long minTime = record.mMinTime;
1591-
if (loc.getTime() - lastLoc.getTime() < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
1596+
long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
1597+
if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
15921598
return false;
15931599
}
15941600

services/java/com/android/server/UiModeManagerService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ private boolean hasMoved(Location location) {
308308
/* if new location is older than the current one, the devices hasn't
309309
* moved.
310310
*/
311-
if (location.getTime() < mLocation.getTime()) {
311+
if (location.getElapsedRealtimeNano() < mLocation.getElapsedRealtimeNano()) {
312312
return false;
313313
}
314314

@@ -764,7 +764,8 @@ private void retrieveLocation() {
764764
mLocationManager.getLastKnownLocation(providers.next());
765765
// pick the most recent location
766766
if (location == null || (lastKnownLocation != null &&
767-
location.getTime() < lastKnownLocation.getTime())) {
767+
location.getElapsedRealtimeNano() <
768+
lastKnownLocation.getElapsedRealtimeNano())) {
768769
location = lastKnownLocation;
769770
}
770771
}
@@ -781,6 +782,7 @@ private void retrieveLocation() {
781782
location.setLatitude(0);
782783
location.setAccuracy(417000.0f);
783784
location.setTime(System.currentTimeMillis());
785+
location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
784786
}
785787
synchronized (mLock) {
786788
mLocation = location;

services/java/com/android/server/location/GpsLocationProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,6 +1078,9 @@ private void reportLocation(int flags, double latitude, double longitude, double
10781078
mLocation.setLatitude(latitude);
10791079
mLocation.setLongitude(longitude);
10801080
mLocation.setTime(timestamp);
1081+
// It would be nice to push the elapsed real-time timestamp
1082+
// further down the stack, but this is still useful
1083+
mLocation.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
10811084
}
10821085
if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
10831086
mLocation.setAltitude(altitude);

services/java/com/android/server/location/LocationBasedCountryDetector.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ protected Location getLastKnownLocation() {
114114
for (String provider : providers) {
115115
Location lastKnownLocation = mLocationManager.getLastKnownLocation(provider);
116116
if (lastKnownLocation != null) {
117-
if (bestLocation == null || bestLocation.getTime() < lastKnownLocation.getTime()) {
117+
if (bestLocation == null ||
118+
bestLocation.getElapsedRealtimeNano() <
119+
lastKnownLocation.getElapsedRealtimeNano()) {
118120
bestLocation = lastKnownLocation;
119121
}
120122
}

0 commit comments

Comments
 (0)