From 10a5259d9dc813f6722dd1de4b93f69982ebaa11 Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sat, 16 Nov 2024 15:56:42 -0600 Subject: [PATCH] Location: Remove passive location listener Fetching the last location once we get a request is already a good start and won't trigger any location indicators --- .../microg/gms/settings/SettingsContract.kt | 2 + .../microg/gms/settings/SettingsProvider.kt | 7 +++- .../microg/gms/location/LocationSettings.kt | 5 ++- play-services-location/core/build.gradle | 3 -- .../network/NetworkLocationService.kt | 39 ++++++++++++++----- .../provider/FusedLocationProviderService.kt | 2 +- .../manager/DeviceOrientationManager.kt | 1 + .../gms/location/manager/LocationManager.kt | 26 ++++++++----- .../manager/LocationRequestManager.kt | 2 +- .../ui/LocationPreferencesFragment.kt | 3 +- 10 files changed, 61 insertions(+), 29 deletions(-) diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt index 6c9355986d..5a0ee849ba 100644 --- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt +++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt @@ -173,6 +173,7 @@ object SettingsContract { const val GEOCODER_NOMINATIM = "location_geocoder_nominatim" const val ICHNAEA_ENDPOINT = "location_ichnaea_endpoint" const val ONLINE_SOURCE = "location_online_source" + const val ICHNAEA_CONTRIBUTE = "location_ichnaea_contribute" val PROJECTION = arrayOf( WIFI_ICHNAEA, @@ -185,6 +186,7 @@ object SettingsContract { GEOCODER_NOMINATIM, ICHNAEA_ENDPOINT, ONLINE_SOURCE, + ICHNAEA_CONTRIBUTE, ) } diff --git a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt index 1b98a6d4a0..124932d270 100644 --- a/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt +++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt @@ -10,12 +10,12 @@ import android.content.ContentValues import android.content.Context import android.content.Context.MODE_PRIVATE import android.content.SharedPreferences +import android.content.pm.ApplicationInfo import android.database.Cursor import android.database.MatrixCursor import android.net.Uri import android.os.Build.VERSION.SDK_INT import android.preference.PreferenceManager -import org.microg.gms.base.core.BuildConfig import org.microg.gms.common.PackageUtils.warnIfNotMainProcess import org.microg.gms.settings.SettingsContract.Auth import org.microg.gms.settings.SettingsContract.CheckIn @@ -23,12 +23,13 @@ import org.microg.gms.settings.SettingsContract.DroidGuard import org.microg.gms.settings.SettingsContract.Exposure import org.microg.gms.settings.SettingsContract.Gcm import org.microg.gms.settings.SettingsContract.Location -import org.microg.gms.settings.SettingsContract.Vending import org.microg.gms.settings.SettingsContract.Profile import org.microg.gms.settings.SettingsContract.SafetyNet +import org.microg.gms.settings.SettingsContract.Vending import org.microg.gms.settings.SettingsContract.getAuthority import java.io.File + private const val SETTINGS_PREFIX = "org.microg.gms.settings." /** @@ -321,6 +322,7 @@ class SettingsProvider : ContentProvider() { Location.GEOCODER_NOMINATIM -> getSettingsBoolean(key, hasUnifiedNlpGeocoderBackend("org.microg.nlp.backend.nominatim") ) Location.ICHNAEA_ENDPOINT -> getSettingsString(key, null) Location.ONLINE_SOURCE -> getSettingsString(key, null) + Location.ICHNAEA_CONTRIBUTE -> getSettingsBoolean(key, false) else -> throw IllegalArgumentException("Unknown key: $key") } } @@ -341,6 +343,7 @@ class SettingsProvider : ContentProvider() { Location.GEOCODER_NOMINATIM -> editor.putBoolean(key, value as Boolean) Location.ICHNAEA_ENDPOINT -> (value as String).let { if (it.isBlank()) editor.remove(key) else editor.putString(key, it) } Location.ONLINE_SOURCE -> (value as? String?).let { if (it.isNullOrBlank()) editor.remove(key) else editor.putString(key, it) } + Location.ICHNAEA_CONTRIBUTE -> editor.putBoolean(key, value as Boolean) else -> throw IllegalArgumentException("Unknown key: $key") } } diff --git a/play-services-location/core/base/src/main/kotlin/org/microg/gms/location/LocationSettings.kt b/play-services-location/core/base/src/main/kotlin/org/microg/gms/location/LocationSettings.kt index fec222c200..e8c29cfe33 100644 --- a/play-services-location/core/base/src/main/kotlin/org/microg/gms/location/LocationSettings.kt +++ b/play-services-location/core/base/src/main/kotlin/org/microg/gms/location/LocationSettings.kt @@ -8,6 +8,7 @@ package org.microg.gms.location import android.content.ContentValues import android.content.Context import android.database.Cursor +import org.microg.gms.location.base.BuildConfig import org.microg.gms.settings.SettingsContract private const val PATH_GEOLOCATE = "/v1/geolocate" @@ -96,6 +97,6 @@ class LocationSettings(private val context: Context) { set(value) = setSettings { put(SettingsContract.Location.ONLINE_SOURCE, value) } var ichnaeaContribute: Boolean - get() = false - set(value) = Unit + get() = getSettings(SettingsContract.Location.ICHNAEA_CONTRIBUTE) { c -> c.getInt(0) != 0 } + set(value) = setSettings { put(SettingsContract.Location.ICHNAEA_CONTRIBUTE, value) } } \ No newline at end of file diff --git a/play-services-location/core/build.gradle b/play-services-location/core/build.gradle index bdcd04fe99..c86d0d6a5d 100644 --- a/play-services-location/core/build.gradle +++ b/play-services-location/core/build.gradle @@ -43,7 +43,6 @@ android { targetSdkVersion androidTargetSdk buildConfigField "String", "FORCE_SHOW_BACKGROUND_PERMISSION", "\"\"" buildConfigField "boolean", "SHOW_NOTIFICATION_WHEN_NOT_PERMITTED", "false" - buildConfigField "boolean", "ALWAYS_LISTEN_GPS_PASSIVE", "true" } lintOptions { @@ -59,13 +58,11 @@ android { dimension 'target' buildConfigField "String", "FORCE_SHOW_BACKGROUND_PERMISSION", "\"com.huawei.permission.sec.MDM.v2\"" buildConfigField "boolean", "SHOW_NOTIFICATION_WHEN_NOT_PERMITTED", "true" - buildConfigField "boolean", "ALWAYS_LISTEN_GPS_PASSIVE", "false" } "huaweilh" { dimension 'target' buildConfigField "String", "FORCE_SHOW_BACKGROUND_PERMISSION", "\"com.huawei.permission.sec.MDM.v2\"" buildConfigField "boolean", "SHOW_NOTIFICATION_WHEN_NOT_PERMITTED", "true" - buildConfigField "boolean", "ALWAYS_LISTEN_GPS_PASSIVE", "false" } } diff --git a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/NetworkLocationService.kt b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/NetworkLocationService.kt index 60082080ee..8a1036943c 100644 --- a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/NetworkLocationService.kt +++ b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/network/NetworkLocationService.kt @@ -64,10 +64,11 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta private var lastCellLocation: Location? = null private var lastLocation: Location? = null - private val gpsLocationListener by lazy { LocationListenerCompat { onNewGpsLocation(it) } } + private val passiveLocationListener by lazy { LocationListenerCompat { onNewPassiveLocation(it) } } @GuardedBy("gpsLocationBuffer") private val gpsLocationBuffer = LinkedList() + private var passiveListenerActive = false private var currentLocalMovingWifi: WifiDetails? = null private var lastLocalMovingWifiLocationCandidate: Location? = null @@ -88,15 +89,31 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta putExtra(EXTRA_CONFIGURATION, CONFIGURATION_FIELD_ONLINE_SOURCE) }) } + } + + private fun updatePassiveGpsListenerRegistration() { try { getSystemService()?.let { locationManager -> - LocationManagerCompat.requestLocationUpdates( - locationManager, - LocationManager.GPS_PROVIDER, - LocationRequestCompat.Builder(LocationRequestCompat.PASSIVE_INTERVAL).setMinUpdateIntervalMillis(GPS_PASSIVE_INTERVAL).build(), - gpsLocationListener, - handlerThread.looper - ) + if ((settings.cellLearning || settings.wifiLearning) && (highPowerIntervalMillis != Long.MAX_VALUE)) { + if (!passiveListenerActive) { + LocationManagerCompat.requestLocationUpdates( + locationManager, + LocationManager.PASSIVE_PROVIDER, + LocationRequestCompat.Builder(LocationRequestCompat.PASSIVE_INTERVAL) + .setQuality(LocationRequestCompat.QUALITY_LOW_POWER) + .setMinUpdateIntervalMillis(GPS_PASSIVE_INTERVAL) + .build(), + passiveLocationListener, + handlerThread.looper + ) + passiveListenerActive = true + } + } else { + if (passiveListenerActive) { + LocationManagerCompat.removeUpdates(locationManager, passiveLocationListener) + passiveListenerActive = false + } + } } } catch (e: SecurityException) { Log.d(TAG, "GPS location retriever not initialized due to lack of permission") @@ -181,6 +198,8 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta handler.postDelayed(highPowerScanRunnable, nextHighPowerRequestIn) } } + + updatePassiveGpsListenerRegistration() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -539,8 +558,8 @@ class NetworkLocationService : LifecycleService(), WifiDetailsCallback, CellDeta } } - private fun onNewGpsLocation(location: Location) { - if (location.accuracy > GPS_PASSIVE_MIN_ACCURACY) return + private fun onNewPassiveLocation(location: Location) { + if (location.provider != LocationManager.GPS_PROVIDER || location.accuracy > GPS_PASSIVE_MIN_ACCURACY) return synchronized(gpsLocationBuffer) { if (gpsLocationBuffer.isNotEmpty() && gpsLocationBuffer.last.elapsedMillis < SystemClock.elapsedRealtime() - GPS_BUFFER_SIZE * GPS_PASSIVE_INTERVAL) { gpsLocationBuffer.clear() diff --git a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/FusedLocationProviderService.kt b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/FusedLocationProviderService.kt index 188b596c3a..c9b4a10999 100644 --- a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/FusedLocationProviderService.kt +++ b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/FusedLocationProviderService.kt @@ -27,7 +27,7 @@ class FusedLocationProviderService : IntentLocationProviderService() { @SuppressLint("MissingPermission") override fun requestIntentUpdated(currentRequest: ProviderRequestUnbundled?, pendingIntent: PendingIntent) { - val intervalMillis = currentRequest?.interval ?: Long.MAX_VALUE + val intervalMillis = max(currentRequest?.interval ?: Long.MAX_VALUE, minIntervalMillis) val request = LocationRequest.Builder(intervalMillis) if (SDK_INT >= 31 && currentRequest != null) { request.setPriority(when { diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/DeviceOrientationManager.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/DeviceOrientationManager.kt index eb98ca0f49..1cb966b42a 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/DeviceOrientationManager.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/DeviceOrientationManager.kt @@ -118,6 +118,7 @@ class DeviceOrientationManager(private val context: Context, override val lifecy synchronized(appOpsLock) { val newAppOps = mutableSetOf() for (request in requests.values) { + if (request.clientIdentity.isSelfUser()) continue newAppOps.add(request.clientIdentity) } Log.d(TAG, "Updating app ops for device orientation, change attribution to: ${newAppOps.map { it.packageName }.joinToString().takeIf { it.isNotEmpty() } ?: "none"}") diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt index cbce54d560..7b671113f5 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationManager.kt @@ -13,8 +13,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.location.Location -import android.location.LocationManager.GPS_PROVIDER -import android.location.LocationManager.NETWORK_PROVIDER +import android.location.LocationManager.* import android.os.* import android.os.Build.VERSION.SDK_INT import android.util.Log @@ -53,10 +52,12 @@ class LocationManager(private val context: Context, override val lifecycle: Life private val requestManager by lazy { LocationRequestManager(context, lifecycle, postProcessor, database) { updateLocationRequests() } } private val gpsLocationListener by lazy { LocationListenerCompat { updateGpsLocation(it) } } private val networkLocationListener by lazy { LocationListenerCompat { updateNetworkLocation(it) } } + private val settings by lazy { LocationSettings(context) } private var boundToSystemNetworkLocation: Boolean = false private val activePermissionRequestLock = Mutex() private var activePermissionRequest: Deferred? = null private var lastGpsLocation: Location? = null + private var lastNetworkLocation: Location? = null private var currentGpsInterval: Long = -1 private var currentNetworkInterval: Long = -1 @@ -194,7 +195,7 @@ class LocationManager(private val context: Context, override val lifecycle: Life private fun updateLocationRequests() { val gpsInterval = when { - !BuildConfig.ALWAYS_LISTEN_GPS_PASSIVE && deviceOrientationManager.isActive -> min(requestManager.intervalMillis, DEVICE_ORIENTATION_INTERVAL) + deviceOrientationManager.isActive -> min(requestManager.intervalMillis, DEVICE_ORIENTATION_INTERVAL) requestManager.priority == PRIORITY_HIGH_ACCURACY && requestManager.granularity == GRANULARITY_FINE -> requestManager.intervalMillis else -> Long.MAX_VALUE } @@ -207,7 +208,7 @@ class LocationManager(private val context: Context, override val lifecycle: Life deviceOrientationManager.isActive -> DEVICE_ORIENTATION_INTERVAL else -> Long.MAX_VALUE } - val lowPower = requestManager.granularity <= GRANULARITY_COARSE || requestManager.priority >= Priority.PRIORITY_LOW_POWER + val lowPower = requestManager.granularity <= GRANULARITY_COARSE || requestManager.priority >= Priority.PRIORITY_LOW_POWER || (requestManager.priority >= Priority.PRIORITY_BALANCED_POWER_ACCURACY && requestManager.intervalMillis >= BALANCE_LOW_POWER_INTERVAL) if (context.hasNetworkLocationServiceBuiltIn() && currentNetworkInterval != networkInterval) { val intent = Intent(ACTION_NETWORK_LOCATION_SERVICE) @@ -243,6 +244,14 @@ class LocationManager(private val context: Context, override val lifecycle: Life } if (!context.hasNetworkLocationServiceBuiltIn() && LocationManagerCompat.hasProvider(locationManager, NETWORK_PROVIDER) && currentNetworkInterval != networkInterval) { boundToSystemNetworkLocation = true + if (networkInterval == Long.MAX_VALUE) { + // Fetch last location from GPS, just to make sure we already considered it + try { + locationManager.getLastKnownLocation(NETWORK_PROVIDER)?.let { updateNetworkLocation(it) } + } catch (e: SecurityException) { + // Ignore + } + } try { val quality = if (lowPower) QUALITY_LOW_POWER else QUALITY_BALANCED_POWER_ACCURACY locationManager.requestSystemProviderUpdates(NETWORK_PROVIDER, networkInterval, quality, networkLocationListener) @@ -258,9 +267,6 @@ class LocationManager(private val context: Context, override val lifecycle: Life if (interval != Long.MAX_VALUE) { Log.d(TAG, "Request updates for $provider at interval ${interval}ms") LocationManagerCompat.requestLocationUpdates(this, provider, Builder(interval).setQuality(quality).build(), listener, context.mainLooper) - } else if (BuildConfig.ALWAYS_LISTEN_GPS_PASSIVE && provider == GPS_PROVIDER) { - Log.d(TAG, "Request updates for $provider passively") - LocationManagerCompat.requestLocationUpdates(this, provider, Builder(PASSIVE_INTERVAL).setQuality(QUALITY_LOW_POWER).setMinUpdateIntervalMillis(MAX_FINE_UPDATE_INTERVAL).build(), listener, context.mainLooper) } else { Log.d(TAG, "Remove updates for $provider") LocationManagerCompat.removeUpdates(this, listener) @@ -288,14 +294,15 @@ class LocationManager(private val context: Context, override val lifecycle: Life } } - fun updateGpsLocation(location: Location) { + private fun updateGpsLocation(location: Location) { + if (location.provider != GPS_PROVIDER) return lastGpsLocation = location lastLocationCapsule.updateFineLocation(location) sendNewLocation() updateLocationRequests() } - fun sendNewLocation() { + private fun sendNewLocation() { lifecycleScope.launchWhenStarted { requestManager.processNewLocation(lastLocationCapsule) } @@ -382,5 +389,6 @@ class LocationManager(private val context: Context, override val lifecycle: Life const val DEVICE_ORIENTATION_INTERVAL = 10_000L const val NETWORK_OFF_GPS_AGE = 5000L const val NETWORK_OFF_GPS_ACCURACY = 10f + const val BALANCE_LOW_POWER_INTERVAL = 30_000L } } \ No newline at end of file diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt index 2d22bd381f..ca770689d1 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/manager/LocationRequestManager.kt @@ -214,7 +214,7 @@ class LocationRequestManager(private val context: Context, override val lifecycl val newAppOps = mutableMapOf() val merged = binderRequests.values + pendingIntentRequests.values for (request in merged) { - if (request.effectivePriority >= PRIORITY_PASSIVE) continue + if (request.effectivePriority >= PRIORITY_PASSIVE || request.clientIdentity.isSelfUser()) continue if (!newAppOps.containsKey(request.clientIdentity)) { newAppOps[request.clientIdentity] = request.effectiveHighPower } else if (request.effectiveHighPower) { diff --git a/play-services-location/core/src/main/kotlin/org/microg/gms/location/ui/LocationPreferencesFragment.kt b/play-services-location/core/src/main/kotlin/org/microg/gms/location/ui/LocationPreferencesFragment.kt index b0fd7305e5..41eb4c269a 100644 --- a/play-services-location/core/src/main/kotlin/org/microg/gms/location/ui/LocationPreferencesFragment.kt +++ b/play-services-location/core/src/main/kotlin/org/microg/gms/location/ui/LocationPreferencesFragment.kt @@ -36,6 +36,7 @@ import org.microg.gms.location.core.R import org.microg.gms.location.manager.LocationAppsDatabase import org.microg.gms.location.network.OnlineSource import org.microg.gms.location.network.effectiveEndpoint +import org.microg.gms.location.network.onlineSource import org.microg.gms.ui.AppIconPreference import org.microg.gms.ui.buildAlertDialog import org.microg.gms.ui.getApplicationInfoIfExists @@ -189,7 +190,7 @@ class LocationPreferencesFragment : PreferenceFragmentCompat() { view.setPadding(0, 16.dp, 0, 0) view.orientation = LinearLayout.VERTICAL val settings = LocationSettings(requireContext()) - val currentSourceId = settings.onlineSourceId + val currentSourceId = settings.onlineSource?.id val unselectHandlerMap = mutableMapOf Unit>() var selectedSourceId = currentSourceId val customView = layoutInflater.inflate(R.layout.preference_location_custom_url, null)