Loading play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/NetworkLocationService.kt +35 −12 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import java.io.PrintWriter import java.lang.Math.pow import java.nio.ByteBuffer import java.util.LinkedList import kotlin.math.max import kotlin.math.min class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDetailsCallback { Loading Loading @@ -271,10 +272,6 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta val scanResultTimestamp = min(wifis.maxOf { it.timestamp ?: Long.MAX_VALUE }, System.currentTimeMillis()) val scanResultRealtimeMillis = if (SDK_INT >= 17) SystemClock.elapsedRealtime() - (System.currentTimeMillis() - scanResultTimestamp) else scanResultTimestamp if (scanResultRealtimeMillis < lastWifiDetailsRealtimeMillis + interval / 2 && lastWifiDetailsRealtimeMillis != 0L) { Log.d(TAG, "Ignoring wifi details, similar age as last ($scanResultRealtimeMillis < $lastWifiDetailsRealtimeMillis + $interval / 2)") return } @Suppress("DEPRECATION") currentLocalMovingWifi = getSystemService<WifiManager>()?.connectionInfo ?.let { wifiInfo -> wifis.filter { it.macAddress == wifiInfo.bssid && it.isMoving } } Loading @@ -294,6 +291,10 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta } private fun updateWifiLocation(requestableWifis: List<WifiDetails>, scanResultRealtimeMillis: Long = 0, scanResultTimestamp: Long = 0) { if (scanResultRealtimeMillis < lastWifiDetailsRealtimeMillis + interval / 2 && lastWifiDetailsRealtimeMillis != 0L) { Log.d(TAG, "Ignoring wifi details, similar age as last ($scanResultRealtimeMillis < $lastWifiDetailsRealtimeMillis + $interval / 2)") return } val previousLastRealtimeMillis = lastWifiDetailsRealtimeMillis if (scanResultRealtimeMillis != 0L) lastWifiDetailsRealtimeMillis = scanResultRealtimeMillis lifecycleScope.launch { Loading @@ -302,9 +303,9 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta lastWifiDetailsRealtimeMillis = previousLastRealtimeMillis return@launch } if (scanResultTimestamp != 0L && location.time == 0L) location.time = scanResultTimestamp if (SDK_INT >= 17 && scanResultRealtimeMillis != 0L && location.elapsedRealtimeNanos == 0L) location.elapsedRealtimeNanos = scanResultRealtimeMillis * 1_000_000L if (scanResultTimestamp != 0L) location.time = max(scanResultTimestamp, location.time) if (SDK_INT >= 17 && scanResultRealtimeMillis != 0L) location.elapsedRealtimeNanos = max(location.elapsedRealtimeNanos, scanResultRealtimeMillis * 1_000_000L) synchronized(locationLock) { lastWifiLocation = location } Loading Loading @@ -372,22 +373,43 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta } private fun sendLocationUpdate(now: Boolean = false) { fun cliffLocations(old: Location?, new: Location?): Location? { // We move from wifi towards cell with accuracy if (old == null) return new if (new == null) return old val diff = new.elapsedMillis - old.elapsedMillis if (diff < LOCATION_TIME_CLIFF_START_MS) return old if (diff > LOCATION_TIME_CLIFF_END_MS) return new val pct = (diff - LOCATION_TIME_CLIFF_START_MS).toDouble() / (LOCATION_TIME_CLIFF_END_MS - LOCATION_TIME_CLIFF_START_MS).toDouble() return Location(old).apply { provider = "cliff" latitude = old.latitude * (1.0-pct) + new.latitude * pct longitude = old.longitude * (1.0-pct) + new.longitude * pct accuracy = (old.accuracy * (1.0-pct) + new.accuracy * pct).toFloat() altitude = old.altitude * (1.0-pct) + new.altitude * pct time = (old.time.toDouble() * (1.0-pct) + new.time.toDouble() * pct).toLong() elapsedRealtimeNanos = (old.elapsedRealtimeNanos.toDouble() * (1.0-pct) + new.elapsedRealtimeNanos.toDouble() * pct).toLong() } } val location = synchronized(locationLock) { if (lastCellLocation == null && lastWifiLocation == null) return when { // Only non-null lastCellLocation == null -> lastWifiLocation lastWifiLocation == null -> lastCellLocation // Consider cliff lastCellLocation!!.elapsedMillis > lastWifiLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_MS -> lastCellLocation lastWifiLocation!!.elapsedMillis > lastCellLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_MS -> lastWifiLocation // Consider cliff end 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 // Consider cliff start lastCellLocation!!.elapsedMillis > lastWifiLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_START_MS -> cliffLocations(lastWifiLocation, lastCellLocation) else -> lastWifiLocation } } ?: return if (location == lastLocation) return if (lastLocation == lastWifiLocation && location == lastCellLocation && !now) { if (lastLocation == lastWifiLocation && lastLocation.let { it != null && location.accuracy > it.accuracy } && !now) { Log.d(TAG, "Debounce inaccurate location update") handler.postDelayed({ sendLocationUpdate(true) }, DEBOUNCE_DELAY_MS) Loading Loading @@ -456,7 +478,8 @@ 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 LOCATION_TIME_CLIFF_MS = 30000L const val LOCATION_TIME_CLIFF_START_MS = 30000L const val LOCATION_TIME_CLIFF_END_MS = 60000L const val DEBOUNCE_DELAY_MS = 5000L const val MAX_WIFI_SCAN_CACHE_AGE = 1000L * 60 * 60 * 24 // 1 day const val MAX_LOCAL_WIFI_AGE_NS = 60_000_000_000L // 1 minute Loading play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/ichnaea/GeolocateResponse.kt +4 −4 Original line number Diff line number Diff line Loading @@ -6,8 +6,8 @@ package org.microg.gms.location.network.ichnaea data class GeolocateResponse( val location: ResponseLocation?, val accuracy: Double?, val fallback: String?, val error: ResponseError? val location: ResponseLocation? = null, val accuracy: Double? = null, val fallback: String? = null, val error: ResponseError? = null ) No newline at end of file play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/ichnaea/IchnaeaServiceClient.kt +24 −1 Original line number Diff line number Diff line Loading @@ -11,8 +11,10 @@ import android.net.Uri import android.os.Bundle import android.util.Log import com.android.volley.Request.Method import com.android.volley.VolleyError import com.android.volley.toolbox.JsonObjectRequest import com.android.volley.toolbox.Volley import org.json.JSONObject import org.microg.gms.location.LocationSettings import org.microg.gms.location.network.cell.CellDetails import org.microg.gms.location.network.precision Loading @@ -20,6 +22,7 @@ import org.microg.gms.location.network.wifi.WifiDetails import org.microg.gms.location.network.wifi.isMoving import org.microg.gms.location.provider.BuildConfig import org.microg.gms.utils.singleInstanceOf import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine Loading Loading @@ -75,12 +78,32 @@ class IchnaeaServiceClient(private val context: Context) { } } private fun continueError(continuation: Continuation<GeolocateResponse>, error: VolleyError) { try { val response = JSONObject(error.networkResponse.data.decodeToString()).toGeolocateResponse() if (response.error != null) { continuation.resume(response) return } else if (response.location?.lat != null){ Log.w(TAG, "Received location in response with error code") } else { Log.w(TAG, "Received valid json without error in response with error code") } } catch (_: Exception) { } if (error.networkResponse != null) { continuation.resume(GeolocateResponse(error = ResponseError(error.networkResponse.statusCode, error.message))) return } continuation.resumeWithException(error) } private suspend fun rawGeoLocate(request: GeolocateRequest): GeolocateResponse = suspendCoroutine { continuation -> val url = Uri.parse(settings.ichneaeEndpoint).buildUpon().appendPath("v1").appendPath("geolocate").build().toString() queue.add(object : JsonObjectRequest(Method.POST, url, request.toJson(), { continuation.resume(it.toGeolocateResponse()) }, { continuation.resumeWithException(it) continueError(continuation, it) }) { override fun getHeaders(): Map<String, String> = getRequestHeaders() }) Loading play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/ichnaea/ResponseError.kt +2 −2 Original line number Diff line number Diff line Loading @@ -6,6 +6,6 @@ package org.microg.gms.location.network.ichnaea data class ResponseError( val code: Int, val message: String val code: Int? = null, val message: String? = null ) No newline at end of file play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/wifi/MovingWifiHelper.kt +3 −6 Original line number Diff line number Diff line Loading @@ -299,13 +299,9 @@ class MovingWifiHelper(private val context: Context) { "CDWiFi", "MAVSTART-WIFI" -> parsePassengera(location, data) "AegeanWiFi" -> parseDisplayUgo(location, data) "Cathay Pacific", "Telekom_FlyNet", "KrisWorld", "SWISS Connect", "Edelweiss Entertainment" -> parsePanasonic(location, data) "FlyNet" -> parseBoardConnect(location, data) "FlyNet", "Austrian FlyNet" -> parseBoardConnect(location, data) "ACWiFi" -> parseAirCanada(location, data) "OUIFI" -> parseSncf(location, data) "_SNCF_WIFI_INOUI" -> parseSncf(location, data) "_SNCF_WIFI_INTERCITES" -> parseSncf(location, data) "_WIFI_LYRIA" -> parseSncf(location, data) "NormandieTrainConnecte" -> parseSncf(location, data) "OUIFI", "_SNCF_WIFI_INOUI", "_SNCF_WIFI_INTERCITES", "_WIFI_LYRIA", "NormandieTrainConnecte" -> parseSncf(location, data) "agilis-Wifi" -> parseHotsplots(location, data) else -> throw UnsupportedOperationException() } Loading Loading @@ -337,6 +333,7 @@ class MovingWifiHelper(private val context: Context) { "_WIFI_LYRIA" to "https://wifi.tgv-lyria.com/router/api/train/gps", "NormandieTrainConnecte" to "https://wifi.normandie.fr/router/api/train/gps", "agilis-Wifi" to "http://hsp.hotsplots.net/status.json", "Austrian FlyNet" to "https://www.austrian-flynet.com/map/api/flightData", ) } } Loading Loading
play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/NetworkLocationService.kt +35 −12 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import java.io.PrintWriter import java.lang.Math.pow import java.nio.ByteBuffer import java.util.LinkedList import kotlin.math.max import kotlin.math.min class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDetailsCallback { Loading Loading @@ -271,10 +272,6 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta val scanResultTimestamp = min(wifis.maxOf { it.timestamp ?: Long.MAX_VALUE }, System.currentTimeMillis()) val scanResultRealtimeMillis = if (SDK_INT >= 17) SystemClock.elapsedRealtime() - (System.currentTimeMillis() - scanResultTimestamp) else scanResultTimestamp if (scanResultRealtimeMillis < lastWifiDetailsRealtimeMillis + interval / 2 && lastWifiDetailsRealtimeMillis != 0L) { Log.d(TAG, "Ignoring wifi details, similar age as last ($scanResultRealtimeMillis < $lastWifiDetailsRealtimeMillis + $interval / 2)") return } @Suppress("DEPRECATION") currentLocalMovingWifi = getSystemService<WifiManager>()?.connectionInfo ?.let { wifiInfo -> wifis.filter { it.macAddress == wifiInfo.bssid && it.isMoving } } Loading @@ -294,6 +291,10 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta } private fun updateWifiLocation(requestableWifis: List<WifiDetails>, scanResultRealtimeMillis: Long = 0, scanResultTimestamp: Long = 0) { if (scanResultRealtimeMillis < lastWifiDetailsRealtimeMillis + interval / 2 && lastWifiDetailsRealtimeMillis != 0L) { Log.d(TAG, "Ignoring wifi details, similar age as last ($scanResultRealtimeMillis < $lastWifiDetailsRealtimeMillis + $interval / 2)") return } val previousLastRealtimeMillis = lastWifiDetailsRealtimeMillis if (scanResultRealtimeMillis != 0L) lastWifiDetailsRealtimeMillis = scanResultRealtimeMillis lifecycleScope.launch { Loading @@ -302,9 +303,9 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta lastWifiDetailsRealtimeMillis = previousLastRealtimeMillis return@launch } if (scanResultTimestamp != 0L && location.time == 0L) location.time = scanResultTimestamp if (SDK_INT >= 17 && scanResultRealtimeMillis != 0L && location.elapsedRealtimeNanos == 0L) location.elapsedRealtimeNanos = scanResultRealtimeMillis * 1_000_000L if (scanResultTimestamp != 0L) location.time = max(scanResultTimestamp, location.time) if (SDK_INT >= 17 && scanResultRealtimeMillis != 0L) location.elapsedRealtimeNanos = max(location.elapsedRealtimeNanos, scanResultRealtimeMillis * 1_000_000L) synchronized(locationLock) { lastWifiLocation = location } Loading Loading @@ -372,22 +373,43 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta } private fun sendLocationUpdate(now: Boolean = false) { fun cliffLocations(old: Location?, new: Location?): Location? { // We move from wifi towards cell with accuracy if (old == null) return new if (new == null) return old val diff = new.elapsedMillis - old.elapsedMillis if (diff < LOCATION_TIME_CLIFF_START_MS) return old if (diff > LOCATION_TIME_CLIFF_END_MS) return new val pct = (diff - LOCATION_TIME_CLIFF_START_MS).toDouble() / (LOCATION_TIME_CLIFF_END_MS - LOCATION_TIME_CLIFF_START_MS).toDouble() return Location(old).apply { provider = "cliff" latitude = old.latitude * (1.0-pct) + new.latitude * pct longitude = old.longitude * (1.0-pct) + new.longitude * pct accuracy = (old.accuracy * (1.0-pct) + new.accuracy * pct).toFloat() altitude = old.altitude * (1.0-pct) + new.altitude * pct time = (old.time.toDouble() * (1.0-pct) + new.time.toDouble() * pct).toLong() elapsedRealtimeNanos = (old.elapsedRealtimeNanos.toDouble() * (1.0-pct) + new.elapsedRealtimeNanos.toDouble() * pct).toLong() } } val location = synchronized(locationLock) { if (lastCellLocation == null && lastWifiLocation == null) return when { // Only non-null lastCellLocation == null -> lastWifiLocation lastWifiLocation == null -> lastCellLocation // Consider cliff lastCellLocation!!.elapsedMillis > lastWifiLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_MS -> lastCellLocation lastWifiLocation!!.elapsedMillis > lastCellLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_MS -> lastWifiLocation // Consider cliff end 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 // Consider cliff start lastCellLocation!!.elapsedMillis > lastWifiLocation!!.elapsedMillis + LOCATION_TIME_CLIFF_START_MS -> cliffLocations(lastWifiLocation, lastCellLocation) else -> lastWifiLocation } } ?: return if (location == lastLocation) return if (lastLocation == lastWifiLocation && location == lastCellLocation && !now) { if (lastLocation == lastWifiLocation && lastLocation.let { it != null && location.accuracy > it.accuracy } && !now) { Log.d(TAG, "Debounce inaccurate location update") handler.postDelayed({ sendLocationUpdate(true) }, DEBOUNCE_DELAY_MS) Loading Loading @@ -456,7 +478,8 @@ 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 LOCATION_TIME_CLIFF_MS = 30000L const val LOCATION_TIME_CLIFF_START_MS = 30000L const val LOCATION_TIME_CLIFF_END_MS = 60000L const val DEBOUNCE_DELAY_MS = 5000L const val MAX_WIFI_SCAN_CACHE_AGE = 1000L * 60 * 60 * 24 // 1 day const val MAX_LOCAL_WIFI_AGE_NS = 60_000_000_000L // 1 minute Loading
play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/ichnaea/GeolocateResponse.kt +4 −4 Original line number Diff line number Diff line Loading @@ -6,8 +6,8 @@ package org.microg.gms.location.network.ichnaea data class GeolocateResponse( val location: ResponseLocation?, val accuracy: Double?, val fallback: String?, val error: ResponseError? val location: ResponseLocation? = null, val accuracy: Double? = null, val fallback: String? = null, val error: ResponseError? = null ) No newline at end of file
play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/ichnaea/IchnaeaServiceClient.kt +24 −1 Original line number Diff line number Diff line Loading @@ -11,8 +11,10 @@ import android.net.Uri import android.os.Bundle import android.util.Log import com.android.volley.Request.Method import com.android.volley.VolleyError import com.android.volley.toolbox.JsonObjectRequest import com.android.volley.toolbox.Volley import org.json.JSONObject import org.microg.gms.location.LocationSettings import org.microg.gms.location.network.cell.CellDetails import org.microg.gms.location.network.precision Loading @@ -20,6 +22,7 @@ import org.microg.gms.location.network.wifi.WifiDetails import org.microg.gms.location.network.wifi.isMoving import org.microg.gms.location.provider.BuildConfig import org.microg.gms.utils.singleInstanceOf import kotlin.coroutines.Continuation import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine Loading Loading @@ -75,12 +78,32 @@ class IchnaeaServiceClient(private val context: Context) { } } private fun continueError(continuation: Continuation<GeolocateResponse>, error: VolleyError) { try { val response = JSONObject(error.networkResponse.data.decodeToString()).toGeolocateResponse() if (response.error != null) { continuation.resume(response) return } else if (response.location?.lat != null){ Log.w(TAG, "Received location in response with error code") } else { Log.w(TAG, "Received valid json without error in response with error code") } } catch (_: Exception) { } if (error.networkResponse != null) { continuation.resume(GeolocateResponse(error = ResponseError(error.networkResponse.statusCode, error.message))) return } continuation.resumeWithException(error) } private suspend fun rawGeoLocate(request: GeolocateRequest): GeolocateResponse = suspendCoroutine { continuation -> val url = Uri.parse(settings.ichneaeEndpoint).buildUpon().appendPath("v1").appendPath("geolocate").build().toString() queue.add(object : JsonObjectRequest(Method.POST, url, request.toJson(), { continuation.resume(it.toGeolocateResponse()) }, { continuation.resumeWithException(it) continueError(continuation, it) }) { override fun getHeaders(): Map<String, String> = getRequestHeaders() }) Loading
play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/ichnaea/ResponseError.kt +2 −2 Original line number Diff line number Diff line Loading @@ -6,6 +6,6 @@ package org.microg.gms.location.network.ichnaea data class ResponseError( val code: Int, val message: String val code: Int? = null, val message: String? = null ) No newline at end of file
play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/wifi/MovingWifiHelper.kt +3 −6 Original line number Diff line number Diff line Loading @@ -299,13 +299,9 @@ class MovingWifiHelper(private val context: Context) { "CDWiFi", "MAVSTART-WIFI" -> parsePassengera(location, data) "AegeanWiFi" -> parseDisplayUgo(location, data) "Cathay Pacific", "Telekom_FlyNet", "KrisWorld", "SWISS Connect", "Edelweiss Entertainment" -> parsePanasonic(location, data) "FlyNet" -> parseBoardConnect(location, data) "FlyNet", "Austrian FlyNet" -> parseBoardConnect(location, data) "ACWiFi" -> parseAirCanada(location, data) "OUIFI" -> parseSncf(location, data) "_SNCF_WIFI_INOUI" -> parseSncf(location, data) "_SNCF_WIFI_INTERCITES" -> parseSncf(location, data) "_WIFI_LYRIA" -> parseSncf(location, data) "NormandieTrainConnecte" -> parseSncf(location, data) "OUIFI", "_SNCF_WIFI_INOUI", "_SNCF_WIFI_INTERCITES", "_WIFI_LYRIA", "NormandieTrainConnecte" -> parseSncf(location, data) "agilis-Wifi" -> parseHotsplots(location, data) else -> throw UnsupportedOperationException() } Loading Loading @@ -337,6 +333,7 @@ class MovingWifiHelper(private val context: Context) { "_WIFI_LYRIA" to "https://wifi.tgv-lyria.com/router/api/train/gps", "NormandieTrainConnecte" to "https://wifi.normandie.fr/router/api/train/gps", "agilis-Wifi" to "http://hsp.hotsplots.net/status.json", "Austrian FlyNet" to "https://www.austrian-flynet.com/map/api/flightData", ) } } Loading