diff options
author | Nick Pelly <npelly@google.com> | 2012-07-18 13:13:37 -0700 |
---|---|---|
committer | Nick Pelly <npelly@google.com> | 2012-07-20 10:07:07 -0700 |
commit | 2eeeec248a38ff33999c83f4b8d5bab7d50e79d2 (patch) | |
tree | 2886aaefdd09bbafa7853dbbbb29aa866846045f /location | |
parent | b8acd060d409f0e81ab3510b429cb86d3f34adb8 (diff) | |
download | frameworks_base-2eeeec248a38ff33999c83f4b8d5bab7d50e79d2.zip frameworks_base-2eeeec248a38ff33999c83f4b8d5bab7d50e79d2.tar.gz frameworks_base-2eeeec248a38ff33999c83f4b8d5bab7d50e79d2.tar.bz2 |
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
Diffstat (limited to 'location')
-rw-r--r-- | location/java/android/location/Location.java | 107 | ||||
-rw-r--r-- | location/java/android/location/LocationManager.java | 21 |
2 files changed, 118 insertions, 10 deletions
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index aacf857..5ad60ab 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -19,6 +19,7 @@ package android.location; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Printer; import java.text.DecimalFormat; @@ -59,6 +60,7 @@ public class Location implements Parcelable { private String mProvider; private long mTime = 0; + private long mElapsedRealtimeNano = 0; private double mLatitude = 0.0; private double mLongitude = 0.0; private boolean mHasAltitude = false; @@ -84,6 +86,7 @@ public class Location implements Parcelable { public void dump(Printer pw, String prefix) { pw.println(prefix + "mProvider=" + mProvider + " mTime=" + mTime); + pw.println(prefix + "mElapsedRealtimeNano=" + mElapsedRealtimeNano); pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude); pw.println(prefix + "mHasAltitude=" + mHasAltitude + " mAltitude=" + mAltitude); pw.println(prefix + "mHasSpeed=" + mHasSpeed + " mSpeed=" + mSpeed); @@ -118,6 +121,7 @@ public class Location implements Parcelable { public void set(Location l) { mProvider = l.mProvider; mTime = l.mTime; + mElapsedRealtimeNano = l.mElapsedRealtimeNano; mLatitude = l.mLatitude; mLongitude = l.mLongitude; mHasAltitude = l.mHasAltitude; @@ -137,6 +141,7 @@ public class Location implements Parcelable { public void reset() { mProvider = null; mTime = 0; + mElapsedRealtimeNano = 0; mLatitude = 0; mLongitude = 0; mHasAltitude = false; @@ -467,23 +472,62 @@ public class Location implements Parcelable { } /** - * Returns the UTC time of this fix, in milliseconds since January 1, + * Return the UTC time of this fix, in milliseconds since January 1, * 1970. + * <p>Note that the UTC time on a device is not monotonic: it + * can jump forwards or backwards unpredictably. So always use + * {@link #getElapsedRealtimeNano()} when calculating time deltas. + * <p>On the other hand, {@link #getTime()} is useful for presenting + * a human readable time to the user, or for carefully comparing + * location fixes across reboot or across devices. + * <p>This method will always return a valid timestamp on + * Locations generated by a {@link LocationProvider}. + * + * @return time of fix, in milliseconds since January 1, 1970. */ public long getTime() { return mTime; } /** - * Sets the UTC time of this fix, in milliseconds since January 1, + * Set the UTC time of this fix, in milliseconds since January 1, * 1970. + * + * @param time UTC time of this fix, in milliseconds since January 1, 1970 */ public void setTime(long time) { mTime = time; } /** - * Returns the latitude of this fix. + * Return the time of this fix, in elapsed real-time since system boot. + * <p>This value can be reliably compared to + * {@link android.os.SystemClock#elapsedRealtimeNano()}, + * to calculate the age of a fix, and to compare Location fixes, since + * elapsed real-time is guaranteed monotonic for each system boot, and + * continues to increment even when the system is in deep sleep. + * <p>This method will always return a valid timestamp on + * Locations generated by a {@link LocationProvider}. + * + * @return elapsed real-time of fix, in nanoseconds since system boot. + */ + public long getElapsedRealtimeNano() { + return mElapsedRealtimeNano; + } + + /** + * Set the time of this fix, in elapsed real-time since system boot. + * + * @param time elapsed real-time of fix, in nanoseconds since system boot. + */ + public void setElapsedRealtimeNano(long time) { + mElapsedRealtimeNano = time; + } + + /** + * Return the latitude of this fix. + * <p>This method will always return a valid latitude on + * Locations generated by a {@link LocationProvider}. */ public double getLatitude() { return mLatitude; @@ -497,7 +541,9 @@ public class Location implements Parcelable { } /** - * Returns the longitude of this fix. + * Return the longitude of this fix. + * <p>This method will always return a valid longitude on + * Locations generated by a {@link LocationProvider}. */ public double getLongitude() { return mLongitude; @@ -619,16 +665,27 @@ public class Location implements Parcelable { } /** - * Returns true if the provider is able to report accuracy information, - * false otherwise. The default implementation returns false. + * Return true if this Location has an associated accuracy. + * <p>All Location objects generated by a {@link LocationProvider} + * will have an accuracy. */ public boolean hasAccuracy() { return mHasAccuracy; } /** - * Returns the accuracy of the fix in meters. If hasAccuracy() is false, - * 0.0 is returned. + * Return the accuracy of this Location fix. + * <p>Accuracy is measured in meters, and indicates the + * radius of 95% confidence. + * In other words, there is a 95% probability that the + * true location is within a circle centered at the reported + * location, with radius of the reported accuracy. + * <p>This is only a measure of horizontal accuracy, and does + * not indicate the accuracy of bearing, velocity or altitude + * if those are included in this Location. + * <p>If {@link #hasAccuracy} is false, 0.0 is returned. + * <p>All Location object generated by a {@link LocationProvider} + * will have a valid accuracy. */ public float getAccuracy() { return mAccuracy; @@ -653,6 +710,37 @@ public class Location implements Parcelable { } /** + * Return true if this Location object has enough data set to + * be considered a valid fix from a {@link LocationProvider}. + * @see #makeComplete + * @hide + */ + public boolean isComplete() { + if (mProvider == null) return false; + if (!mHasAccuracy) return false; + if (mTime == 0) return false; + if (mElapsedRealtimeNano == 0) return false; + return true; + } + + /** + * Helper to fill in incomplete fields. + * Only use this to assist in backwards compatibility + * with Location objects received from applications. + * @see #isComplete + * @hide + */ + public void makeComplete() { + if (mProvider == null) mProvider = "?"; + if (!mHasAccuracy) { + mHasAccuracy = true; + mAccuracy = 100.0f; + } + if (mTime == 0) mTime = System.currentTimeMillis(); + if (mElapsedRealtimeNano == 0) mElapsedRealtimeNano = SystemClock.elapsedRealtimeNano(); + } + + /** * Returns additional provider-specific information about the * location fix as a Bundle. The keys and values are determined * by the provider. If no additional information is available, @@ -681,6 +769,7 @@ public class Location implements Parcelable { @Override public String toString() { return "Location[mProvider=" + mProvider + ",mTime=" + mTime + + ",mElapsedRealtimeNano=" + mElapsedRealtimeNano + ",mLatitude=" + mLatitude + ",mLongitude=" + mLongitude + ",mHasAltitude=" + mHasAltitude + @@ -700,6 +789,7 @@ public class Location implements Parcelable { String provider = in.readString(); Location l = new Location(provider); l.mTime = in.readLong(); + l.mElapsedRealtimeNano = in.readLong(); l.mLatitude = in.readDouble(); l.mLongitude = in.readDouble(); l.mHasAltitude = in.readInt() != 0; @@ -726,6 +816,7 @@ public class Location implements Parcelable { public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mProvider); parcel.writeLong(mTime); + parcel.writeLong(mElapsedRealtimeNano); parcel.writeDouble(mLatitude); parcel.writeDouble(mLongitude); parcel.writeInt(mHasAltitude ? 1 : 0); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index ff74f41..15a2928 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -19,11 +19,13 @@ package android.location; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.os.Looper; import android.os.RemoteException; import android.os.Handler; import android.os.Message; +import android.os.SystemClock; import android.util.Log; import com.android.internal.location.DummyLocationProvider; @@ -1220,8 +1222,11 @@ public class LocationManager { } /** - * Sets a mock location for the given provider. This location will be used in place - * of any actual location from the provider. + * Sets a mock location for the given provider. + * <p>This location will be used in place of any actual location from the provider. + * The location object must have a minimum number of fields set to be + * considered a valid LocationProvider Location, as per documentation + * on {@link Location} class. * * @param provider the provider name * @param loc the mock location @@ -1230,8 +1235,20 @@ public class LocationManager { * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled * @throws IllegalArgumentException if no provider with the given name exists + * @throws IllegalArgumentException if the location is incomplete */ public void setTestProviderLocation(String provider, Location loc) { + if (!loc.isComplete()) { + if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) { + // for backwards compatibility, allow mock locations that are incomplete + Log.w(TAG, "Incomplete Location object", new Throwable()); + loc.makeComplete(); + } else { + throw new IllegalArgumentException( + "Location object not complete. Missing timestamps or accuracy?"); + } + } + try { mService.setTestProviderLocation(provider, loc); } catch (RemoteException ex) { |