Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Verified Commit 16d8fd50 authored by Marvin W.'s avatar Marvin W. 🐿️
Browse files

Small fixes everywhere

parent 484d01fb
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -5,12 +5,13 @@

buildscript {
    ext.kotlinVersion = '1.3.72'
    ext.coroutineVersion = '1.3.3'
    ext.coroutineVersion = '1.3.7'

    ext.appcompatVersion = '1.1.0'
    ext.fragmentVersion = '1.2.4'
    ext.recyclerviewVersion = '1.1.0'
    ext.fragmentVersion = '1.2.5'
    ext.lifecycleVersion = '2.2.0'
    ext.navigationVersion = '2.3.0'
    ext.recyclerviewVersion = '1.1.0'

    ext.androidBuildGradleVersion = '3.6.3'

+138 −79
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.AtomicInteger

import android.content.pm.PackageManager.SIGNATURE_MATCH
import kotlinx.coroutines.*
import java.lang.RuntimeException
import java.util.concurrent.*
import kotlin.coroutines.*
import kotlin.math.min
@@ -47,6 +48,7 @@ class UnifiedLocationClient private constructor(context: Context) {
    private var timer: Timer? = null
    private var reconnectCount = 0
    private val requests = CopyOnWriteArraySet<LocationRequest>()
    private val coroutineScope = CoroutineScope(Dispatchers.IO + Job())
    private val connection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName, service: IBinder) {
            this@UnifiedLocationClient.onServiceConnected(name, service)
@@ -70,6 +72,10 @@ class UnifiedLocationClient private constructor(context: Context) {
        get() = options.getBoolean(KEY_FORCE_NEXT_UPDATE, false)
        set(value) = options.putBoolean(KEY_FORCE_NEXT_UPDATE, value)

    var opPackageName: String?
        get() = options.getString(KEY_OP_PACKAGE_NAME)
        set(value) = options.putString(KEY_OP_PACKAGE_NAME, value)

    val isAvailable: Boolean
        get() = bound || resolve() != null

@@ -83,44 +89,40 @@ class UnifiedLocationClient private constructor(context: Context) {
        val pm = context.packageManager
        var resolveInfos = pm.queryIntentServices(intent, 0)
        if (resolveInfos.size > 1) {
            // Pick own package if possible
            for (info in resolveInfos) {
                if (info.serviceInfo.packageName == context.packageName) {
                    intent.setPackage(info.serviceInfo.packageName)
                    Log.d(TAG, "Found more than one active unified service, picked own package " + intent.getPackage()!!)
                    return intent
                }
            }

            // Pick package with matching signature if possible
            for (info in resolveInfos) {
                if (pm.checkSignatures(context.packageName, info.serviceInfo.packageName) == SIGNATURE_MATCH) {
                    intent.setPackage(info.serviceInfo.packageName)
                    Log.d(TAG, "Found more than one active unified service, picked related package " + intent.getPackage()!!)
                    return intent
            // Restrict to self if possible
            val isSelf: (it: ResolveInfo) -> Boolean = {
                it.serviceInfo.packageName == context.packageName
            }
            if (resolveInfos.size > 1 && resolveInfos.any(isSelf)) {
                Log.d(TAG, "Found more than one active unified service, restricted to own package " + context.packageName)
                resolveInfos = resolveInfos.filter(isSelf)
            }

            // Restrict to system if single package is system
            if (resolveInfos.any {
                        (it.serviceInfo?.applicationInfo?.flags
                                ?: 0) and ApplicationInfo.FLAG_SYSTEM > 0
                    }) {
                Log.d(TAG, "Found more than one active unified service, restricted to system packages")
                resolveInfos = resolveInfos.filter {
                    (it.serviceInfo?.applicationInfo?.flags
                            ?: 0) and ApplicationInfo.FLAG_SYSTEM > 0
            // Restrict to package with matching signature if possible
            val isSelfSig: (it: ResolveInfo) -> Boolean = {
                it.serviceInfo.packageName == context.packageName
            }
            if (resolveInfos.size > 1 && resolveInfos.any(isSelfSig)) {
                Log.d(TAG, "Found more than one active unified service, restricted to related packages")
                resolveInfos = resolveInfos.filter(isSelfSig)
            }

            var highestPriority: ResolveInfo? = null
            for (info in resolveInfos) {
                if (highestPriority == null || highestPriority.priority < info.priority) {
                    highestPriority = info
            // Restrict to system if any package is system
            val isSystem: (it: ResolveInfo) -> Boolean = {
                (it.serviceInfo?.applicationInfo?.flags ?: 0) and ApplicationInfo.FLAG_SYSTEM > 0
            }
            if (resolveInfos.size > 1 && resolveInfos.any(isSystem)) {
                Log.d(TAG, "Found more than one active unified service, restricted to system packages")
                resolveInfos = resolveInfos.filter(isSystem)
            }

            val highestPriority: ResolveInfo? = resolveInfos.maxBy { it.priority }
            intent.setPackage(highestPriority!!.serviceInfo.packageName)
            Log.d(TAG, "Found more than one active unified service, picked highest priority " + intent.getPackage()!!)
            intent.setClassName(highestPriority.serviceInfo.packageName, highestPriority.serviceInfo.name)
            if (resolveInfos.size > 1) {
                Log.d(TAG, "Found more than one active unified service, picked highest priority " + intent.component)
            }
            return intent
        } else if (!resolveInfos.isEmpty()) {
            intent.setPackage(resolveInfos[0].serviceInfo.packageName)
@@ -132,17 +134,21 @@ class UnifiedLocationClient private constructor(context: Context) {
    }

    @Synchronized
    private fun updateBinding() {
    private fun updateBinding(): Boolean {
        Log.d(TAG, "updateBinding - current: $bound, refs: ${serviceReferenceCount.get()}, reqs: ${requests.size}, avail: $isAvailable")
        if (!bound && (serviceReferenceCount.get() > 0 || !requests.isEmpty()) && isAvailable) {
            timer = Timer("unified-client")
            bound = true
            bind()
            return true
        } else if (bound && serviceReferenceCount.get() == 0 && requests.isEmpty()) {
            timer!!.cancel()
            timer = null
            bound = false
            unbind()
            return false
        }
        return bound
    }

    @Synchronized
@@ -173,27 +179,30 @@ class UnifiedLocationClient private constructor(context: Context) {
        val intent = resolve() ?: return
        unbind()
        reconnectCount++
        Log.d(TAG, "Binding to $intent")
        bound = context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    @Synchronized
    private fun unbind() {
        try {
            this.context.get()?.let {
                it.unbindService(connection)
            }
            this.context.get()?.unbindService(connection)
        } catch (ignored: Exception) {
        }

        this.service = null
    }

    @Synchronized
    fun ref() {
        Log.d(TAG, "ref: ${Exception().stackTrace[1]}")
        serviceReferenceCount.incrementAndGet()
        updateBinding()
    }

    @Synchronized
    fun unref() {
        Log.d(TAG, "unref: ${Exception().stackTrace[1]}")
        serviceReferenceCount.decrementAndGet()
        updateBinding()
    }
@@ -210,24 +219,34 @@ class UnifiedLocationClient private constructor(context: Context) {
        requestLocationUpdates(listener, interval, Integer.MAX_VALUE)
    }

    @Synchronized
    fun requestLocationUpdates(listener: LocationListener, interval: Long, count: Int) {
        requests.removeAll(requests.filter { it.listener === listener })
        requests.add(LocationRequest(listener, interval, count))
        coroutineScope.launch {
            updateServiceInterval()
            updateBinding()
        }
    }

    @Synchronized
    fun removeLocationUpdates(listener: LocationListener) {
        coroutineScope.launch {
            removeRequests(requests.filter { it.listener === listener })
        }
    }

    private suspend fun refAndGetService(): UnifiedLocationService = suspendCoroutine { continuation -> refAndGetServiceContinued(continuation) }

    @Synchronized
    private fun refAndGetServiceContinued(continuation: Continuation<UnifiedLocationService>) {
        Log.d(TAG, "ref+get: ${Exception().stackTrace[2]}")
        serviceReferenceCount.incrementAndGet()
        waitForServiceContinued(continuation)
    }

    private suspend fun waitForService(): UnifiedLocationService = suspendCoroutine { continuation -> waitForServiceContinued(continuation) }

    @Synchronized
    private fun waitForServiceContinued(continuation: Continuation<UnifiedLocationService>) {
        val service = service
        if (service != null) {
            continuation.resume(service)
@@ -235,7 +254,15 @@ class UnifiedLocationClient private constructor(context: Context) {
            synchronized(waitingForService) {
                waitingForService.add(continuation)
            }
            timer?.schedule(object : TimerTask() {
            updateBinding()
            val timer = timer
            if (timer == null) {
                synchronized(waitingForService) {
                    waitingForService.remove(continuation)
                }
                continuation.resumeWithException(RuntimeException("No timer, called waitForService when not connected"))
            } else {
                timer.schedule(object : TimerTask() {
                    override fun run() {
                        try {
                            continuation.resumeWithException(TimeoutException())
@@ -244,13 +271,13 @@ class UnifiedLocationClient private constructor(context: Context) {
                        }
                    }
                }, CALL_TIMEOUT)
            updateBinding()
            }
        }
    }

    fun <T> configureContinuationTimeout(continuation: Continuation<T>, timeout: Long) {
    private fun <T> configureContinuationTimeout(continuation: Continuation<T>, timeout: Long) {
        if (timeout <= 0 || timeout == Long.MAX_VALUE) return
        timer?.schedule(object : TimerTask() {
        timer!!.schedule(object : TimerTask() {
            override fun run() {
                try {
                    Log.w(TAG, "Timeout reached")
@@ -262,17 +289,24 @@ class UnifiedLocationClient private constructor(context: Context) {
        }, timeout)
    }

    fun <T> executeSyncWithTimeout(timeout: Long = CALL_TIMEOUT, action: suspend () -> T): T {
    private fun <T> executeSyncWithTimeout(timeout: Long = CALL_TIMEOUT, action: suspend () -> T): T {
        var result: T? = null
        val latch = CountDownLatch(1)
        var err: Exception? = null
        syncThreads.execute {
            try {
                runBlocking {
                    result = withTimeout(timeout) { action() }
                }
            } catch (e: Exception) {
                err = e
            } finally {
                latch.countDown()
            }
        }
        if (!latch.await(timeout, TimeUnit.MILLISECONDS))
            throw TimeoutException()
        err?.let { throw it }
        return result ?: throw NullPointerException()
    }

@@ -302,7 +336,7 @@ class UnifiedLocationClient private constructor(context: Context) {
                service.getFromLocationNameWithOptions(locationName, maxResults, lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude, locale, options, AddressContinuation(continuation))
                configureContinuationTimeout(continuation, timeout)
            }
        } catch (e: RemoteException) {
        } catch (e: Exception) {
            Log.w(TAG, "Failed to request geocode", e)
            emptyList()
        } finally {
@@ -327,7 +361,7 @@ class UnifiedLocationClient private constructor(context: Context) {
    suspend fun getLocationBackends(): Array<String> {
        try {
            return refAndGetService().locationBackends
        } catch (e: RemoteException) {
        } catch (e: Exception) {
            Log.w(TAG, "Failed to handle request", e)
            return emptyArray()
        } finally {
@@ -338,7 +372,7 @@ class UnifiedLocationClient private constructor(context: Context) {
    suspend fun setLocationBackends(backends: Array<String>) {
        try {
            refAndGetService().locationBackends = backends
        } catch (e: RemoteException) {
        } catch (e: Exception) {
            Log.w(TAG, "Failed to handle request", e)
        } finally {
            unref()
@@ -366,6 +400,10 @@ class UnifiedLocationClient private constructor(context: Context) {
        }
    }

    fun getLastLocationSync(timeout: Long = CALL_TIMEOUT): Location? = executeSyncWithTimeout(timeout) {
        getLastLocation()
    }

    suspend fun getLastLocation(): Location? {
        return try {
            refAndGetService().lastLocation
@@ -388,12 +426,11 @@ class UnifiedLocationClient private constructor(context: Context) {
        }
    }

    @Synchronized
    private fun removeRequestPendingRemoval() {
    private suspend fun removeRequestPendingRemoval() {
        removeRequests(requests.filter { it.needsRemoval })
    }

    private fun removeRequests(removalNeeded: List<LocationRequest>) {
    private suspend fun removeRequests(removalNeeded: List<LocationRequest>) {
        if (removalNeeded.isNotEmpty()) {
            requests.removeAll(removalNeeded)
            updateServiceInterval()
@@ -401,9 +438,7 @@ class UnifiedLocationClient private constructor(context: Context) {
        }
    }

    @Synchronized
    private fun updateServiceInterval() {
        if (service == null) return
    private suspend fun updateServiceInterval() {
        var interval: Long = Long.MAX_VALUE
        var requestSingle = false
        for (request in requests) {
@@ -419,11 +454,18 @@ class UnifiedLocationClient private constructor(context: Context) {
        } else {
            Log.d(TAG, "Set update interval to $interval")
        }
        val service: UnifiedLocationService
        try {
            service!!.setUpdateInterval(interval, options)
            service = waitForService()
        } catch (e: Exception) {
            Log.w(TAG, e)
            return
        }
        try {
            service.setUpdateInterval(interval, options)
            if (requestSingle) {
                Log.d(TAG, "Request single update (force update: $forceNextUpdate)")
                service!!.requestSingleUpdate(options)
                service.requestSingleUpdate(options)
                forceNextUpdate = false
            }
        } catch (e: DeadObjectException) {
@@ -434,7 +476,6 @@ class UnifiedLocationClient private constructor(context: Context) {
        }
    }


    @Synchronized
    private fun onServiceConnected(name: ComponentName, binder: IBinder) {
        Log.d(TAG, "Connected to $name")
@@ -446,24 +487,40 @@ class UnifiedLocationClient private constructor(context: Context) {
            continuations.addAll(waitingForService)
            waitingForService.clear()
        }
        for (continuation in continuations) {
            continuation.resume(service)
        }
        coroutineScope.launch {
            try {
                Log.d(TAG, "Registering location callback")
                service.registerLocationCallback(object : LocationCallback.Stub() {
                    override fun onLocationUpdate(location: Location) {
                    for (request in requests) {
                        request.handleLocation(location)
                        coroutineScope.launch {
                            this@UnifiedLocationClient.onLocationUpdate(location)
                        }
                    removeRequestPendingRemoval()
                    }
                }, options)
            updateServiceInterval()
                Log.d(TAG, "Registered location callback")
            } catch (e: Exception) {
                Log.w(TAG, "Failed to register location callback", e)
            }
            updateServiceInterval()
            if (continuations.size > 0) {
                Log.d(TAG, "Resuming ${continuations.size} continuations")
            }
            for (continuation in continuations) {
                try {
                    continuation.resume(service)
                } catch (e: Exception) {
                    Log.w(TAG, e)
                }
            }
        }
    }

    private suspend fun onLocationUpdate(location: Location) {
        for (request in requests) {
            request.handleLocation(location)
        }
        removeRequestPendingRemoval()
    }

    @Synchronized
    private fun onServiceDisconnected(name: ComponentName) {
@@ -531,7 +588,9 @@ class UnifiedLocationClient private constructor(context: Context) {

    companion object {
        const val ACTION_UNIFIED_LOCATION_SERVICE = "org.microg.nlp.service.UnifiedLocationService"
        const val KEY_FORCE_NEXT_UPDATE = "org.microg.nlp.client.FORCE_NEXT_UPDATE"
        const val PERMISSION_SERVICE_ADMIN = "org.microg.nlp.SERVICE_ADMIN"
        const val KEY_FORCE_NEXT_UPDATE = "org.microg.nlp.FORCE_NEXT_UPDATE"
        const val KEY_OP_PACKAGE_NAME = "org.microg.nlp.OP_PACKAGE_NAME"
        private val TAG = "ULocClient"
        private var client: UnifiedLocationClient? = null

+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
    <uses-permission android:name="android.permission.ACCESS_COARSE_UPDATES" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="org.microg.nlp.SERVICE_ADMIN" />

    <application>
        <uses-library android:name="com.android.location.provider" />
+21 −0
Original line number Diff line number Diff line
@@ -18,9 +18,14 @@ import com.android.location.provider.ProviderRequestUnbundled;

import org.microg.nlp.client.UnifiedLocationClient;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;

import static android.location.LocationProvider.AVAILABLE;

public class LocationProvider extends LocationProviderBase implements UnifiedLocationClient.LocationListener {
    private static final List<String> EXCLUDED_PACKAGES = Arrays.asList("android", "com.android.location.fused", "com.google.android.gms");
    private static final long FASTEST_REFRESH_INTERVAL = 30000;
    private static final String TAG = "ULocation";
    private Context context;
@@ -43,6 +48,21 @@ public class LocationProvider extends LocationProviderBase implements UnifiedLoc
    @Override
    public void onSetRequest(ProviderRequestUnbundled requests, WorkSource source) {
        Log.v(TAG, "onSetRequest: " + requests + " by " + source);
        String opPackageName = null;
        try {
            Field namesField = WorkSource.class.getDeclaredField("mNames");
            namesField.setAccessible(true);
            String[] names = (String[]) namesField.get(source);
            if (names != null) {
                for (String name : names) {
                    if (!EXCLUDED_PACKAGES.contains(name)) {
                        opPackageName = name;
                        break;
                    }
                }
            }
        } catch (Exception ignored) {
        }

        long autoTime = Math.max(requests.getInterval(), FASTEST_REFRESH_INTERVAL);
        boolean autoUpdate = requests.getReportLocation();
@@ -50,6 +70,7 @@ public class LocationProvider extends LocationProviderBase implements UnifiedLoc
        Log.v(TAG, "using autoUpdate=" + autoUpdate + " autoTime=" + autoTime);

        if (autoUpdate) {
            UnifiedLocationClient.get(context).setOpPackageName(opPackageName);
            UnifiedLocationClient.get(context).requestLocationUpdates(this, autoTime);
        } else {
            UnifiedLocationClient.get(context).removeLocationUpdates(this);
+3 −0
Original line number Diff line number Diff line
@@ -34,6 +34,9 @@ dependencies {
    implementation project(':api')
    implementation project(':client')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutineVersion"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutineVersion"
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
}

afterEvaluate {
Loading