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

Commit cbadbf45 authored by Yu-Han Yang's avatar Yu-Han Yang
Browse files

Drop invalid locations

Bug: 314328533
Test: atest LocationProviderManagerTest
Change-Id: I907cdfc39bdfc53356d07c68f512b3511bc709be
parent 6da4412d
Loading
Loading
Loading
Loading
+70 −8
Original line number Diff line number Diff line
@@ -19,8 +19,11 @@ package android.location;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.location.flags.Flags;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.Log;

import com.android.internal.util.Preconditions;

@@ -37,6 +40,23 @@ import java.util.function.Predicate;
 * @hide
 */
public final class LocationResult implements Parcelable {
    private static final String TAG = "LocationResult";

    // maximum reasonable accuracy, somewhat arbitrarily chosen. this is a very high upper limit, it
    // could likely be lower, but we only want to throw out really absurd values.
    private static final float MAX_ACCURACY_M = 1000000;

    // maximum reasonable speed we expect a device to travel at is currently mach 1 (top speed of
    // current fastest private jet). Higher speed than the value is considered as a malfunction
    // than a correct reading.
    private static final float MAX_SPEED_MPS = 343;

    /** Exception representing an invalid location within a {@link LocationResult}. */
    public static class BadLocationException extends Exception {
        public BadLocationException(String message) {
            super(message);
        }
    }

    /**
     * Creates a new LocationResult from the given locations, making a copy of each location.
@@ -101,11 +121,52 @@ public final class LocationResult implements Parcelable {
     *
     * @hide
     */
    public @NonNull LocationResult validate() {
    public @NonNull LocationResult validate() throws BadLocationException {
        long prevElapsedRealtimeNs = 0;
        final int size = mLocations.size();
        for (int i = 0; i < size; ++i) {
            Location location = mLocations.get(i);
            if (Flags.locationValidation()) {
                if (location.getLatitude() < -90.0
                        || location.getLatitude() > 90.0
                        || location.getLongitude() < -180.0
                        || location.getLongitude() > 180.0
                        || Double.isNaN(location.getLatitude())
                        || Double.isNaN(location.getLongitude())) {
                    throw new BadLocationException("location must have valid lat/lng");
                }
                if (!location.hasAccuracy()) {
                    throw new BadLocationException("location must have accuracy");
                }
                if (location.getAccuracy() < 0 || location.getAccuracy() > MAX_ACCURACY_M) {
                    throw new BadLocationException("location must have reasonable accuracy");
                }
                if (location.getTime() < 0) {
                    throw new BadLocationException("location must have valid time");
                }
                if (prevElapsedRealtimeNs > location.getElapsedRealtimeNanos()) {
                    throw new BadLocationException(
                            "location must have valid monotonically increasing realtime");
                }
                if (location.getElapsedRealtimeNanos()
                        > SystemClock.elapsedRealtimeNanos()) {
                    throw new BadLocationException("location must not have realtime in the future");
                }
                if (!location.isMock()) {
                    if (location.getProvider() == null) {
                        throw new BadLocationException("location must have valid provider");
                    }
                    if (location.getLatitude() == 0 && location.getLongitude() == 0) {
                        throw new BadLocationException("location must not be at 0,0");
                    }
                }

                if (location.hasSpeed() && (location.getSpeed() < 0
                        || location.getSpeed() > MAX_SPEED_MPS)) {
                    Log.w(TAG, "removed bad location speed: " + location.getSpeed());
                    location.removeSpeed();
                }
            } else {
                if (!location.isComplete()) {
                    throw new IllegalArgumentException(
                            "incomplete location at index " + i + ": " + mLocations);
@@ -114,6 +175,7 @@ public final class LocationResult implements Parcelable {
                    throw new IllegalArgumentException(
                            "incorrectly ordered location at index " + i + ": " + mLocations);
                }
            }
            prevElapsedRealtimeNs = location.getElapsedRealtimeNanos();
        }

+7 −0
Original line number Diff line number Diff line
@@ -27,3 +27,10 @@ flag {
    description: "Flag for releasing SUPL connection on timeout"
    bug: "315024652"
}

flag {
    name: "location_validation"
    namespace: "location"
    description: "Flag for location validation"
    bug: "314328533"
}
+11 −2
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.LocationResult.BadLocationException;
import android.location.flags.Flags;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
@@ -1380,7 +1381,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements

        location.setExtras(mLocationExtras.getBundle());

        try {
            reportLocation(LocationResult.wrap(location).validate());
        } catch (BadLocationException e) {
            throw new IllegalArgumentException(e);
        }

        if (mStarted) {
            mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1751,7 +1756,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
                }
            }

            try {
                reportLocation(LocationResult.wrap(locations).validate());
            } catch (BadLocationException e) {
                throw new IllegalArgumentException(e);
            }
        }

        Runnable[] listeners;
+9 −19
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.location.LocationManagerInternal;
import android.location.LocationManagerInternal.ProviderEnabledListener;
import android.location.LocationRequest;
import android.location.LocationResult;
import android.location.LocationResult.BadLocationException;
import android.location.altitude.AltitudeConverter;
import android.location.provider.IProviderRequestListener;
import android.location.provider.ProviderProperties;
@@ -910,7 +911,8 @@ public class LocationProviderManager extends
                                        < getRequest().getMinUpdateIntervalMillis() - maxJitterMs) {
                                    if (D) {
                                        Log.v(TAG, mName + " provider registration " + getIdentity()
                                                + " dropped delivery - too fast");
                                                + " dropped delivery - too fast (deltaMs="
                                                + deltaMs + ").");
                                    }
                                    return false;
                                }
@@ -2574,29 +2576,17 @@ public class LocationProviderManager extends
    @GuardedBy("mMultiplexerLock")
    @Nullable
    private LocationResult processReportedLocation(LocationResult locationResult) {
        LocationResult processed = locationResult.filter(location -> {
            if (!location.isMock()) {
                if (location.getLatitude() == 0 && location.getLongitude() == 0) {
                    Log.e(TAG, "blocking 0,0 location from " + mName + " provider");
                    return false;
                }
            }

            if (!location.isComplete()) {
                Log.e(TAG, "blocking incomplete location from " + mName + " provider");
                return false;
            }

            return true;
        });
        if (processed == null) {
        try {
            locationResult.validate();
        } catch (BadLocationException e) {
            Log.e(TAG, "Dropping invalid locations: " + e);
            return null;
        }

        // Attempt to add a missing MSL altitude on behalf of the provider.
        if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_LOCATION,
                "enable_location_provider_manager_msl", true)) {
            return processed.map(location -> {
            return locationResult.map(location -> {
                if (!location.hasMslAltitude() && location.hasAltitude()) {
                    try {
                        Location locationCopy = new Location(location);
@@ -2626,7 +2616,7 @@ public class LocationProviderManager extends
                return location;
            });
        }
        return processed;
        return locationResult;
    }

    @GuardedBy("mMultiplexerLock")
+6 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.annotation.Nullable;
import android.location.Location;
import android.location.LocationResult;
import android.location.LocationResult.BadLocationException;
import android.location.provider.ProviderProperties;
import android.location.provider.ProviderRequest;
import android.location.util.identity.CallerIdentity;
@@ -55,7 +56,11 @@ public class MockLocationProvider extends AbstractLocationProvider {
        Location location = new Location(l);
        location.setIsFromMockProvider(true);
        mLocation = location;
        try {
            reportLocation(LocationResult.wrap(location).validate());
        } catch (BadLocationException e) {
            throw new IllegalArgumentException(e);
        }
    }

    @Override
Loading