From 511afe821b20a5545f2ecc3f8db92a3ecb24d87f Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 29 Sep 2024 15:40:38 +0200 Subject: [PATCH] Location: Provide "fused" system location provider --- .../provider/src/main/AndroidManifest.xml | 11 ++ .../provider/FusedLocationProviderService.kt | 66 +++++++++++ ...t => IntentLocationProviderPreTiramisu.kt} | 71 +++--------- .../provider/IntentLocationProviderService.kt | 91 +++++++++++++++ .../NetworkLocationProviderService.kt | 105 +++++++++--------- .../gms/location/provider/extensions.kt | 2 + 6 files changed, 238 insertions(+), 108 deletions(-) create mode 100644 play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/FusedLocationProviderService.kt rename play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/{NetworkLocationProviderPreTiramisu.kt => IntentLocationProviderPreTiramisu.kt} (52%) create mode 100644 play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/IntentLocationProviderService.kt diff --git a/play-services-location/core/provider/src/main/AndroidManifest.xml b/play-services-location/core/provider/src/main/AndroidManifest.xml index 52ec99d3b2..ff8cc6dd32 100644 --- a/play-services-location/core/provider/src/main/AndroidManifest.xml +++ b/play-services-location/core/provider/src/main/AndroidManifest.xml @@ -61,6 +61,17 @@ android:name="serviceVersion" android:value="2" /> + + + + + + = 31 && currentRequest != null) { + request.setPriority(when(currentRequest.quality) { + LocationRequestCompat.QUALITY_LOW_POWER -> Priority.PRIORITY_LOW_POWER + LocationRequestCompat.QUALITY_HIGH_ACCURACY -> Priority.PRIORITY_HIGH_ACCURACY + else -> Priority.PRIORITY_BALANCED_POWER_ACCURACY + }) + request.setMaxUpdateDelayMillis(currentRequest.maxUpdateDelayMillis) + } + try { + LocationServices.getFusedLocationProviderClient(this).requestLocationUpdates(request.build(), pendingIntent) + } catch (e: SecurityException) { + Log.d(TAG, "Failed requesting location updated", e) + } + } + + override fun stopIntentUpdated(pendingIntent: PendingIntent?) { + LocationServices.getFusedLocationProviderClient(this).removeLocationUpdates(pendingIntent) + } + + override val minIntervalMillis: Long + get() = MIN_INTERVAL_MILLIS + override val minReportMillis: Long + get() = MIN_REPORT_MILLIS + override val properties: ProviderPropertiesUnbundled + get() = PROPERTIES + override val providerName: String + get() = "fused" + + companion object { + private const val MIN_INTERVAL_MILLIS = 20000L + private const val MIN_REPORT_MILLIS = 1000L + private val PROPERTIES = ProviderPropertiesUnbundled.create(false, false, false, false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_COARSE) + } +} \ No newline at end of file diff --git a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/NetworkLocationProviderPreTiramisu.kt b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/IntentLocationProviderPreTiramisu.kt similarity index 52% rename from play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/NetworkLocationProviderPreTiramisu.kt rename to play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/IntentLocationProviderPreTiramisu.kt index eeec457afd..f5bb9c67e0 100644 --- a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/NetworkLocationProviderPreTiramisu.kt +++ b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/IntentLocationProviderPreTiramisu.kt @@ -7,9 +7,7 @@ package org.microg.gms.location.provider import android.app.PendingIntent import android.app.PendingIntent.FLAG_UPDATE_CURRENT -import android.content.Context import android.content.Intent -import android.location.Criteria import android.location.Location import android.location.LocationManager import android.os.Build.VERSION.SDK_INT @@ -17,31 +15,28 @@ import android.os.Handler import android.os.Looper import android.os.SystemClock import android.os.WorkSource -import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.PendingIntentCompat import androidx.core.content.getSystemService import com.android.location.provider.ProviderPropertiesUnbundled import com.android.location.provider.ProviderRequestUnbundled -import org.microg.gms.location.* -import org.microg.gms.location.network.LOCATION_EXTRA_PRECISION -import org.microg.gms.location.network.NetworkLocationService -import org.microg.gms.location.provider.NetworkLocationProviderService.Companion.ACTION_REPORT_LOCATION +import org.microg.gms.location.elapsedMillis +import org.microg.gms.location.formatRealtime import java.io.PrintWriter import kotlin.math.max -class NetworkLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { +class IntentLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { @Deprecated("Use only with SDK < 31") - constructor(context: Context, legacy: Unit) : super(properties) { - this.context = context + constructor(service: IntentLocationProviderService, properties: ProviderPropertiesUnbundled, legacy: Unit) : super(properties) { + this.service = service } @RequiresApi(31) - constructor(context: Context) : super(context, properties) { - this.context = context + constructor(service: IntentLocationProviderService, properties: ProviderPropertiesUnbundled) : super(service, properties) { + this.service = service } - private val context: Context + private val service: IntentLocationProviderService private var enabled = false private var currentRequest: ProviderRequestUnbundled? = null private var pendingIntent: PendingIntent? = null @@ -52,29 +47,7 @@ class NetworkLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { private fun updateRequest() { if (enabled) { - val forceNow: Boolean - val intervalMillis: Long - if (currentRequest?.reportLocation == true) { - forceNow = true - intervalMillis = max(currentRequest?.interval ?: Long.MAX_VALUE, MIN_INTERVAL_MILLIS) - } else { - forceNow = false - intervalMillis = Long.MAX_VALUE - } - val intent = Intent(ACTION_NETWORK_LOCATION_SERVICE) - intent.`package` = context.packageName - intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent) - intent.putExtra(EXTRA_ENABLE, true) - intent.putExtra(EXTRA_INTERVAL_MILLIS, intervalMillis) - intent.putExtra(EXTRA_FORCE_NOW, forceNow) - if (SDK_INT >= 31) { - intent.putExtra(EXTRA_LOW_POWER, currentRequest?.isLowPower ?: false) - intent.putExtra(EXTRA_WORK_SOURCE, currentRequest?.workSource) - } - if (SDK_INT >= 29) { - intent.putExtra(EXTRA_BYPASS, currentRequest?.isLocationSettingsIgnored ?: false) - } - context.startService(intent) + service.requestIntentUpdated(currentRequest, pendingIntent) reportAgain() } } @@ -96,9 +69,9 @@ class NetworkLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { override fun enable() { synchronized(this) { if (enabled) throw IllegalStateException() - val intent = Intent(context, NetworkLocationProviderService::class.java) + val intent = Intent(service, service.javaClass) intent.action = ACTION_REPORT_LOCATION - pendingIntent = PendingIntentCompat.getService(context, 0, intent, FLAG_UPDATE_CURRENT, true) + pendingIntent = PendingIntentCompat.getService(service, 0, intent, FLAG_UPDATE_CURRENT, true) currentRequest = null enabled = true when { @@ -107,7 +80,7 @@ class NetworkLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { } try { if (lastReportedLocation == null) { - lastReportedLocation = context.getSystemService()?.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) + lastReportedLocation = service.getSystemService()?.getLastKnownLocation(service.providerName) } } catch (_: SecurityException) { } catch (_: Exception) { @@ -118,10 +91,7 @@ class NetworkLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { override fun disable() { synchronized(this) { if (!enabled) throw IllegalStateException() - val intent = Intent(context, NetworkLocationService::class.java) - intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent) - intent.putExtra(EXTRA_ENABLE, false) - context.startService(intent) + service.stopIntentUpdated(pendingIntent) pendingIntent?.cancel() pendingIntent = null currentRequest = null @@ -133,7 +103,7 @@ class NetworkLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { private fun reportAgain() { // Report location again if it's recent enough lastReportedLocation?.let { - if (it.elapsedMillis + max(currentRequest?.interval ?: 0, MIN_INTERVAL_MILLIS) > SystemClock.elapsedRealtime()) { + if (it.elapsedMillis + max(currentRequest?.interval ?: 0, service.minIntervalMillis) > SystemClock.elapsedRealtime()) { reportLocationToSystem(it) } } @@ -141,20 +111,13 @@ class NetworkLocationProviderPreTiramisu : AbstractLocationProviderPreTiramisu { override fun reportLocationToSystem(location: Location) { handler.removeCallbacks(reportAgainRunnable) - location.provider = LocationManager.NETWORK_PROVIDER - location.extras?.remove(LOCATION_EXTRA_PRECISION) + location.provider = service.providerName lastReportedLocation = location lastReportTime = SystemClock.elapsedRealtime() super.reportLocation(location) - val repeatInterval = max(MIN_REPORT_MILLIS, currentRequest?.interval ?: Long.MAX_VALUE) - if (repeatInterval < MIN_INTERVAL_MILLIS) { + val repeatInterval = max(service.minReportMillis, currentRequest?.interval ?: Long.MAX_VALUE) + if (repeatInterval < service.minIntervalMillis) { handler.postDelayed(reportAgainRunnable, repeatInterval) } } - - companion object { - private const val MIN_INTERVAL_MILLIS = 20000L - private const val MIN_REPORT_MILLIS = 1000L - private val properties = ProviderPropertiesUnbundled.create(false, false, false, false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_COARSE) - } } \ No newline at end of file diff --git a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/IntentLocationProviderService.kt b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/IntentLocationProviderService.kt new file mode 100644 index 0000000000..2761b9aa40 --- /dev/null +++ b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/IntentLocationProviderService.kt @@ -0,0 +1,91 @@ +/* + * SPDX-FileCopyrightText: 2024 microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.location.provider + +import android.app.PendingIntent +import android.app.Service +import android.content.Intent +import android.location.Location +import android.os.Binder +import android.os.Build.VERSION.SDK_INT +import android.os.Handler +import android.os.HandlerThread +import android.os.IBinder +import android.os.Process +import android.util.Log +import com.android.location.provider.ProviderPropertiesUnbundled +import com.android.location.provider.ProviderRequestUnbundled +import java.io.FileDescriptor +import java.io.PrintWriter + +abstract class IntentLocationProviderService : Service() { + private lateinit var handlerThread: HandlerThread + private lateinit var handler: Handler + private var bound: Boolean = false + private var provider: GenericLocationProvider? = null + + override fun onCreate() { + super.onCreate() + handlerThread = HandlerThread(this.javaClass.simpleName) + handlerThread.start() + handler = Handler(handlerThread.looper) + } + + abstract fun requestIntentUpdated(currentRequest: ProviderRequestUnbundled?, pendingIntent: PendingIntent?) + + abstract fun stopIntentUpdated(pendingIntent: PendingIntent?) + + abstract fun extractLocation(intent: Intent): Location? + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + if (Binder.getCallingUid() == Process.myUid() && intent?.action == ACTION_REPORT_LOCATION) { + handler.post { + val location = extractLocation(intent) + if (location != null) { + provider?.reportLocationToSystem(location) + } + } + } + return START_NOT_STICKY + } + + override fun onBind(intent: Intent?): IBinder? { + bound = true + if (provider == null) { + provider = when { + // TODO: Migrate to Tiramisu provider. Not yet required thanks to backwards compat + // SDK_INT >= 33 -> + SDK_INT >= 31 -> + IntentLocationProviderPreTiramisu(this, properties) + + else -> + @Suppress("DEPRECATION") + (IntentLocationProviderPreTiramisu(this, properties, Unit)) + } + provider?.enable() + } + return provider?.getBinder() + } + + override fun dump(fd: FileDescriptor, writer: PrintWriter, args: Array) { + writer.println("Bound: $bound") + provider?.dump(writer) + } + + override fun onDestroy() { + if (SDK_INT >= 18) handlerThread.looper.quitSafely() + else handlerThread.looper.quit() + provider?.disable() + provider = null + bound = false + super.onDestroy() + } + + abstract val minIntervalMillis: Long + abstract val minReportMillis: Long + abstract val properties: ProviderPropertiesUnbundled + abstract val providerName: String +} \ No newline at end of file diff --git a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/NetworkLocationProviderService.kt b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/NetworkLocationProviderService.kt index fb2cd9e279..2c96eeb79d 100644 --- a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/NetworkLocationProviderService.kt +++ b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/NetworkLocationProviderService.kt @@ -5,73 +5,70 @@ package org.microg.gms.location.provider -import android.app.Service +import android.app.PendingIntent import android.content.Intent +import android.location.Criteria import android.location.Location -import android.os.* +import android.location.LocationManager import android.os.Build.VERSION.SDK_INT -import org.microg.gms.location.EXTRA_LOCATION -import java.io.FileDescriptor -import java.io.PrintWriter +import com.android.location.provider.ProviderPropertiesUnbundled +import com.android.location.provider.ProviderRequestUnbundled +import org.microg.gms.location.* +import org.microg.gms.location.network.LOCATION_EXTRA_PRECISION +import kotlin.math.max -class NetworkLocationProviderService : Service() { - private lateinit var handlerThread: HandlerThread - private lateinit var handler: Handler - private var bound: Boolean = false - private var provider: GenericLocationProvider? = null - - override fun onCreate() { - super.onCreate() - handlerThread = HandlerThread(NetworkLocationProviderService::class.java.simpleName) - handlerThread.start() - handler = Handler(handlerThread.looper) +class NetworkLocationProviderService : IntentLocationProviderService() { + override fun extractLocation(intent: Intent): Location? = intent.getParcelableExtra(EXTRA_LOCATION)?.apply { + extras?.remove(LOCATION_EXTRA_PRECISION) } - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - if (Binder.getCallingUid() == Process.myUid() && intent?.action == ACTION_REPORT_LOCATION) { - handler.post { - val location = intent.getParcelableExtra(EXTRA_LOCATION) - if (location != null) { - provider?.reportLocationToSystem(location) - } - } + override fun requestIntentUpdated(currentRequest: ProviderRequestUnbundled?, pendingIntent: PendingIntent?) { + val forceNow: Boolean + val intervalMillis: Long + if (currentRequest?.reportLocation == true) { + forceNow = true + intervalMillis = max(currentRequest.interval ?: Long.MAX_VALUE, minIntervalMillis) + } else { + forceNow = false + intervalMillis = Long.MAX_VALUE } - return START_NOT_STICKY - } - - override fun onBind(intent: Intent?): IBinder? { - bound = true - if (provider == null) { - provider = when { - // TODO: Migrate to Tiramisu provider. Not yet required thanks to backwards compat - // SDK_INT >= 33 -> - SDK_INT >= 31 -> - NetworkLocationProviderPreTiramisu(this) - - else -> - @Suppress("DEPRECATION") - (NetworkLocationProviderPreTiramisu(this, Unit)) - } - provider?.enable() + val intent = Intent(ACTION_NETWORK_LOCATION_SERVICE) + intent.`package` = packageName + intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent) + intent.putExtra(EXTRA_ENABLE, true) + intent.putExtra(EXTRA_INTERVAL_MILLIS, intervalMillis) + intent.putExtra(EXTRA_FORCE_NOW, forceNow) + if (SDK_INT >= 31) { + intent.putExtra(EXTRA_LOW_POWER, currentRequest?.isLowPower ?: false) + intent.putExtra(EXTRA_WORK_SOURCE, currentRequest?.workSource) } - return provider?.getBinder() + if (SDK_INT >= 29) { + intent.putExtra(EXTRA_BYPASS, currentRequest?.isLocationSettingsIgnored ?: false) + } + startService(intent) } - override fun dump(fd: FileDescriptor, writer: PrintWriter, args: Array) { - writer.println("Bound: $bound") - provider?.dump(writer) + override fun stopIntentUpdated(pendingIntent: PendingIntent?) { + val intent = Intent(ACTION_NETWORK_LOCATION_SERVICE) + intent.`package` = packageName + intent.putExtra(EXTRA_PENDING_INTENT, pendingIntent) + intent.putExtra(EXTRA_ENABLE, false) + startService(intent) } - override fun onDestroy() { - if (SDK_INT >= 18) handlerThread.looper.quitSafely() - else handlerThread.looper.quit() - provider?.disable() - provider = null - bound = false - super.onDestroy() - } + override val minIntervalMillis: Long + get() = MIN_INTERVAL_MILLIS + override val minReportMillis: Long + get() = MIN_REPORT_MILLIS + override val properties: ProviderPropertiesUnbundled + get() = PROPERTIES + override val providerName: String + get() = LocationManager.NETWORK_PROVIDER + companion object { - const val ACTION_REPORT_LOCATION = "org.microg.gms.location.provider.ACTION_REPORT_LOCATION" + private const val MIN_INTERVAL_MILLIS = 20000L + private const val MIN_REPORT_MILLIS = 1000L + private val PROPERTIES = ProviderPropertiesUnbundled.create(false, false, false, false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_COARSE) } } \ No newline at end of file diff --git a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/extensions.kt b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/extensions.kt index a99592a812..bc9c54c1f7 100644 --- a/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/extensions.kt +++ b/play-services-location/core/provider/src/main/kotlin/org/microg/gms/location/provider/extensions.kt @@ -12,6 +12,8 @@ import com.google.android.gms.location.internal.ClientIdentity const val TAG = "LocationProvider" +const val ACTION_REPORT_LOCATION = "org.microg.gms.location.provider.ACTION_REPORT_LOCATION" + val GeocoderParams.clientIdentity: ClientIdentity? get() = clientPackage?.let { ClientIdentity(it).apply {