Skip to content

Commit

Permalink
Skip known-unreasonable network location updates
Browse files Browse the repository at this point in the history
If we have a GPS location of a given accuracy, and our proposed network
location is more than twice as far away as the GPS accuracy, this is
considered unreasonable. Stop sending that network location.

As part of this, also keep track of lower-accuracy GPS locations.

Change-Id: I1893c63c2d716a5e41011cbf39f069491e6b5da6
  • Loading branch information
t-m-w committed May 9, 2024
1 parent 3fcc78a commit a079c9a
Showing 1 changed file with 35 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta
@GuardedBy("gpsLocationBuffer")
private val gpsLocationBuffer = LinkedList<Location>()

@GuardedBy("lowAccuracyGpsLocationBuffer")
private val lowAccuracyGpsLocationBuffer = LinkedList<Location>()

private var currentLocalMovingWifi: WifiDetails? = null
private var lastLocalMovingWifiLocationCandidate: Location? = null

Expand Down Expand Up @@ -391,7 +394,22 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta
elapsedRealtimeNanos = (old.elapsedRealtimeNanos.toDouble() * (1.0-pct) + new.elapsedRealtimeNanos.toDouble() * pct).toLong()
}
}
fun Location.isUnreasonableComparedTo(other: Location): Boolean {
val distance = distanceTo(other)
if (distance > 2 * other.accuracy) {
Log.d(TAG, "Unreasonable location ${this.provider} vs ${other.provider} (accuracy ${other.accuracy}): distance $distance")
return true
}
return false
}
fun Location.reasonableOrNull(): Location? {
val gpsLocation = getGpsLocation(elapsedMillis, includeLowAccuracy = true) ?: return this
if (isUnreasonableComparedTo(gpsLocation)) return null
return this
}
val location = synchronized(locationLock) {
lastCellLocation = lastCellLocation?.reasonableOrNull()
lastWifiLocation = lastWifiLocation?.reasonableOrNull()
if (lastCellLocation == null && lastWifiLocation == null) return
when {
// Only non-null
Expand All @@ -401,7 +419,7 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta
lastCellLocation!!.elapsedMillis > lastWifiLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_END_MS -> lastCellLocation
lastWifiLocation!!.elapsedMillis > lastCellLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_START_MS -> lastWifiLocation
// Wifi out of cell range with higher precision
lastCellLocation!!.precision > lastWifiLocation!!.precision && lastWifiLocation!!.distanceTo(lastCellLocation!!) > 2 * lastCellLocation!!.accuracy -> lastCellLocation
lastCellLocation!!.precision > lastWifiLocation!!.precision && lastWifiLocation!!.isUnreasonableComparedTo(lastCellLocation!!) -> lastCellLocation
// Consider cliff start
lastCellLocation!!.elapsedMillis > lastWifiLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_START_MS -> cliffLocations(lastWifiLocation, lastCellLocation)
else -> lastWifiLocation
Expand Down Expand Up @@ -429,7 +447,9 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta
}

private fun onNewGpsLocation(location: Location) {
if (location.accuracy > GPS_PASSIVE_MIN_ACCURACY) return
if (location.accuracy > GPS_PASSIVE_MIN_ACCURACY_LOW) return
// gpsLocationBuffer field is shadowed by the low accuracy buffer if inaccurate enough.
val gpsLocationBuffer = if (location.accuracy > GPS_PASSIVE_MIN_ACCURACY) lowAccuracyGpsLocationBuffer else this.gpsLocationBuffer
synchronized(gpsLocationBuffer) {
if (gpsLocationBuffer.isNotEmpty() && gpsLocationBuffer.last.elapsedMillis < SystemClock.elapsedRealtime() - GPS_BUFFER_SIZE * GPS_PASSIVE_INTERVAL) {
gpsLocationBuffer.clear()
Expand All @@ -440,16 +460,20 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta
}
}

private fun getGpsLocation(elapsedMillis: Long): Location? {
private fun getGpsLocation(elapsedMillis: Long, includeLowAccuracy: Boolean = false): Location? {
if (elapsedMillis + GPS_BUFFER_SIZE * GPS_PASSIVE_INTERVAL < SystemClock.elapsedRealtime()) return null
synchronized(gpsLocationBuffer) {
if (gpsLocationBuffer.isEmpty()) return null
for (location in gpsLocationBuffer.descendingIterator()) {
if (location.elapsedMillis in (elapsedMillis - GPS_PASSIVE_INTERVAL)..(elapsedMillis + GPS_PASSIVE_INTERVAL)) return location
if (location.elapsedMillis < elapsedMillis) return null
fun getGpsLocationFromBuffer(gpsLocationBuffer: LinkedList<Location>): Location? {
// gpsLocationBuffer field is shadowed by the argument provided.
synchronized(gpsLocationBuffer) {
if (gpsLocationBuffer.isEmpty()) return null
for (location in gpsLocationBuffer.descendingIterator()) {
if (location.elapsedMillis in (elapsedMillis - GPS_PASSIVE_INTERVAL)..(elapsedMillis + GPS_PASSIVE_INTERVAL)) return location
if (location.elapsedMillis < elapsedMillis) return null
}
}
return null
}
return null
return getGpsLocationFromBuffer(gpsLocationBuffer) ?: if (includeLowAccuracy) getGpsLocationFromBuffer(lowAccuracyGpsLocationBuffer) else null
}

override fun dump(fd: FileDescriptor?, writer: PrintWriter, args: Array<out String>?) {
Expand All @@ -463,6 +487,7 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta
writer.println("Ichnaea settings: endpoint=${settings.ichneaeEndpoint} contribute=${settings.ichnaeaContribute}")
writer.println("Wifi scan cache size=${wifiScanCache.size()} hits=${wifiScanCache.hitCount()} miss=${wifiScanCache.missCount()} puts=${wifiScanCache.putCount()} evicts=${wifiScanCache.evictionCount()}")
writer.println("GPS location buffer size=${gpsLocationBuffer.size} first=${gpsLocationBuffer.firstOrNull()?.elapsedMillis?.formatRealtime()} last=${gpsLocationBuffer.lastOrNull()?.elapsedMillis?.formatRealtime()}")
writer.println("Low-accuracy GPS location buffer size=${lowAccuracyGpsLocationBuffer.size} first=${lowAccuracyGpsLocationBuffer.firstOrNull()?.elapsedMillis?.formatRealtime()} last=${lowAccuracyGpsLocationBuffer.lastOrNull()?.elapsedMillis?.formatRealtime()}")
cache.dump(writer)
synchronized(activeRequests) {
if (activeRequests.isNotEmpty()) {
Expand All @@ -478,6 +503,7 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta
const val GPS_BUFFER_SIZE = 60
const val GPS_PASSIVE_INTERVAL = 1000L
const val GPS_PASSIVE_MIN_ACCURACY = 25f
const val GPS_PASSIVE_MIN_ACCURACY_LOW = 10000f // 10 kilometers
const val LOCATION_TIME_CLIFF_START_MS = 30000L
const val LOCATION_TIME_CLIFF_END_MS = 60000L
const val DEBOUNCE_DELAY_MS = 5000L
Expand Down

0 comments on commit a079c9a

Please sign in to comment.