UnifiedNlp/service/src/main/kotlin/org/microg/nlp/service/LocationBackendHelper.kt

135 lines
4.8 KiB
Kotlin

/*
* SPDX-FileCopyrightText: 2014, microG Project Team
* SPDX-License-Identifier: Apache-2.0
*/
package org.microg.nlp.service
import android.annotation.TargetApi
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.location.Location
import android.os.*
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.microg.nlp.api.Constants.LOCATION_EXTRA_BACKEND_COMPONENT
import org.microg.nlp.api.Constants.LOCATION_EXTRA_BACKEND_PROVIDER
import org.microg.nlp.api.LocationCallback
class LocationBackendHelper(context: Context, private val locationFuser: LocationFuser, coroutineScope: CoroutineScope, serviceIntent: Intent, signatureDigest: String?) : AbstractBackendHelper(TAG, context, coroutineScope, serviceIntent, signatureDigest) {
private val callback = Callback()
private var backend: AsyncLocationBackend? = null
private var updateWaiting: Boolean = false
var lastLocation: Location? = null
private set(location) {
if (location == null || !location.hasAccuracy()) {
return
}
if (location.extras == null) {
location.extras = Bundle()
}
location.extras.putString(LOCATION_EXTRA_BACKEND_PROVIDER, location.provider)
location.extras.putString(LOCATION_EXTRA_BACKEND_COMPONENT,
serviceIntent.component!!.flattenToShortString())
location.provider = "network"
if (!location.hasAccuracy()) {
location.accuracy = 50000f
}
if (location.time <= 0) {
location.time = System.currentTimeMillis()
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
updateElapsedRealtimeNanos(location)
}
field = location
}
/**
* Requests a location update from the backend.
*
* @return The location reported by the backend. This may be null if a backend cannot determine its
* location, or if it is going to return a location asynchronously.
*/
suspend fun update(): Location? {
var result: Location? = null
if (backend == null) {
Log.d(TAG, "Not (yet) bound.")
updateWaiting = true
} else {
updateWaiting = false
try {
result = backend?.update()
if (result == null) {
Log.d(TAG, "Received no location from ${serviceIntent.component!!.flattenToShortString()}")
} else {
Log.d(TAG, "Received location from ${serviceIntent.component!!.flattenToShortString()} with time ${result.time} (last was ${lastLocation?.time ?: 0})")
if (this.lastLocation == null || result.time > this.lastLocation!!.time) {
lastLocation = result
locationFuser.reportLocation()
}
}
} catch (e: Exception) {
Log.w(TAG, e)
unbind()
}
}
return result
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private fun updateElapsedRealtimeNanos(location: Location) {
if (location.elapsedRealtimeNanos <= 0) {
location.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos()
}
}
@Throws(RemoteException::class)
public override suspend fun close() {
Log.d(TAG, "Calling close")
backend!!.close()
}
public override fun hasBackend(): Boolean {
return backend != null
}
override fun onServiceConnected(name: ComponentName, service: IBinder) {
super.onServiceConnected(name, service)
backend = AsyncLocationBackend(service, name.toShortString() + "-location-backend")
coroutineScope.launch {
try {
Log.d(TAG, "Calling open")
backend!!.open(callback)
if (updateWaiting) {
update()
}
} catch (e: Exception) {
Log.w(TAG, e)
unbind()
}
}
}
override fun onServiceDisconnected(name: ComponentName) {
super.onServiceDisconnected(name)
backend = null
}
private inner class Callback : LocationCallback.Stub() {
override fun report(location: Location?) {
val lastLocation = lastLocation
if (location == null || lastLocation != null && location.time > 0 && location.time <= lastLocation.getTime())
return
this@LocationBackendHelper.lastLocation = location
locationFuser.reportLocation()
}
}
companion object {
private val TAG = "UnifiedLocation"
}
}