Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2eeeec24 authored by Nick Pelly's avatar Nick Pelly
Browse files

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
parent b8acd060
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -10504,6 +10504,7 @@ package android.location {
    method public float getAccuracy();
    method public double getAltitude();
    method public float getBearing();
    method public long getElapsedRealtimeNano();
    method public android.os.Bundle getExtras();
    method public double getLatitude();
    method public double getLongitude();
@@ -10523,6 +10524,7 @@ package android.location {
    method public void setAccuracy(float);
    method public void setAltitude(double);
    method public void setBearing(float);
    method public void setElapsedRealtimeNano(long);
    method public void setExtras(android.os.Bundle);
    method public void setLatitude(double);
    method public void setLongitude(double);
+99 −8
Original line number Diff line number Diff line
@@ -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;
@@ -652,6 +709,37 @@ public class Location implements Parcelable {
        mHasAccuracy = false;
    }

    /**
     * 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
@@ -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);
+19 −2
Original line number Diff line number Diff line
@@ -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) {
+7 −1
Original line number Diff line number Diff line
@@ -1532,6 +1532,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
        }

        if (!location.isComplete()) {
            Log.w(TAG, "Dropping incomplete location: " + location);
            return;
        }

        mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
        Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
        m.arg1 = (passive ? 1 : 0);
@@ -1588,7 +1593,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run

        // Check whether sufficient time has passed
        long minTime = record.mMinTime;
        if (loc.getTime() - lastLoc.getTime() < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
        long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
        if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
            return false;
        }

+4 −2
Original line number Diff line number Diff line
@@ -308,7 +308,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
            /* if new location is older than the current one, the devices hasn't
             * moved.
             */
            if (location.getTime() < mLocation.getTime()) {
            if (location.getElapsedRealtimeNano() < mLocation.getElapsedRealtimeNano()) {
                return false;
            }

@@ -764,7 +764,8 @@ class UiModeManagerService extends IUiModeManager.Stub {
                        mLocationManager.getLastKnownLocation(providers.next());
                // pick the most recent location
                if (location == null || (lastKnownLocation != null &&
                        location.getTime() < lastKnownLocation.getTime())) {
                        location.getElapsedRealtimeNano() <
                        lastKnownLocation.getElapsedRealtimeNano())) {
                    location = lastKnownLocation;
                }
            }
@@ -781,6 +782,7 @@ class UiModeManagerService extends IUiModeManager.Stub {
                location.setLatitude(0);
                location.setAccuracy(417000.0f);
                location.setTime(System.currentTimeMillis());
                location.setElapsedRealtimeNano(SystemClock.elapsedRealtimeNano());
            }
            synchronized (mLock) {
                mLocation = location;
Loading