Loading play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt +16 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,22 @@ object SettingsContract { ) } object Location { private const val id = "location" fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" const val WIFI_MLS = "location_wifi_mls" const val WIFI_MOVING = "location_wifi_moving" const val CELL_MLS = "location_cell_mls" val PROJECTION = arrayOf( WIFI_MLS, WIFI_MOVING, CELL_MLS ) } private fun <T> withoutCallingIdentity(f: () -> T): T { val identity = Binder.clearCallingIdentity() try { Loading play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt +26 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import org.microg.gms.settings.SettingsContract.CheckIn 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.Profile import org.microg.gms.settings.SettingsContract.SafetyNet import org.microg.gms.settings.SettingsContract.getAuthority Loading Loading @@ -67,6 +68,7 @@ class SettingsProvider : ContentProvider() { SafetyNet.getContentUri(context!!) -> querySafetyNet(projection ?: SafetyNet.PROJECTION) DroidGuard.getContentUri(context!!) -> queryDroidGuard(projection ?: DroidGuard.PROJECTION) Profile.getContentUri(context!!) -> queryProfile(projection ?: Profile.PROJECTION) Location.getContentUri(context!!) -> queryLocation(projection ?: Location.PROJECTION) else -> null } Loading @@ -86,6 +88,7 @@ class SettingsProvider : ContentProvider() { SafetyNet.getContentUri(context!!) -> updateSafetyNet(values) DroidGuard.getContentUri(context!!) -> updateDroidGuard(values) Profile.getContentUri(context!!) -> updateProfile(values) Location.getContentUri(context!!) -> updateLocation(values) else -> return 0 } return 1 Loading Loading @@ -288,6 +291,29 @@ class SettingsProvider : ContentProvider() { editor.apply() } private fun queryLocation(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key -> when (key) { Location.WIFI_MLS -> getSettingsBoolean(key, true) Location.WIFI_MOVING -> getSettingsBoolean(key, true) Location.CELL_MLS -> getSettingsBoolean(key, true) else -> throw IllegalArgumentException("Unknown key: $key") } } private fun updateLocation(values: ContentValues) { if (values.size() == 0) return val editor = preferences.edit() values.valueSet().forEach { (key, value) -> when (key) { Location.WIFI_MLS -> editor.putBoolean(key, value as Boolean) Location.WIFI_MOVING -> editor.putBoolean(key, value as Boolean) Location.CELL_MLS -> editor.putBoolean(key, value as Boolean) else -> throw IllegalArgumentException("Unknown key: $key") } } editor.apply() } private fun MatrixCursor.addRow( p: Array<out String>, valueGetter: (String) -> Any? Loading play-services-base/core/src/main/kotlin/org/microg/gms/utils/IntentCacheManager.kt 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2023 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ package org.microg.gms.utils import android.app.AlarmManager import android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP import android.app.PendingIntent import android.app.PendingIntent.FLAG_NO_CREATE import android.app.PendingIntent.FLAG_MUTABLE import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.Service import android.content.Context import android.content.Intent import android.os.Build.VERSION.SDK_INT import android.os.Parcelable import android.os.SystemClock import android.util.Log import androidx.core.content.getSystemService import java.util.UUID class IntentCacheManager<S : Service, T : Parcelable>(private val context: Context, private val clazz: Class<S>, private val type: Int) { private val lock = Any() private lateinit var content: ArrayList<T> private lateinit var id: String private var isReady: Boolean = false private val pendingActions: MutableList<() -> Unit> = arrayListOf() init { val pendingIntent = PendingIntent.getService(context, type, getIntent(), if (SDK_INT >= 31) FLAG_MUTABLE else 0) val alarmManager = context.getSystemService<AlarmManager>() if (SDK_INT >= 19) { alarmManager?.setWindow(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TEN_YEARS, -1, pendingIntent) } else { alarmManager?.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TEN_YEARS, pendingIntent) } pendingIntent.send() } private fun getIntent() = Intent(context, clazz).apply { action = ACTION putExtra(EXTRA_IS_CACHE, true) putExtra(EXTRA_CACHE_TYPE, this@IntentCacheManager.type) } fun add(entry: T, check: (T) -> Boolean = { false }) = runIfReady { val iterator = content.iterator() while (iterator.hasNext()) { if (check(iterator.next())) { iterator.remove() } } content.add(entry) updateIntent() } fun remove(entry: T) = runIfReady { if (content.remove(entry)) updateIntent() } fun removeIf(check: (T) -> Boolean) = runIfReady { var removed = false val iterator = content.iterator() while (iterator.hasNext()) { if (check(iterator.next())) { iterator.remove() removed = true } } if (removed) updateIntent() } fun clear() = runIfReady { content.clear() updateIntent() } fun getId(): String? = if (this::id.isInitialized) id else null fun getEntries(): List<T> = if (this::content.isInitialized) content else emptyList() fun processIntent(intent: Intent) { if (isCache(intent) && getType(intent) == type) { synchronized(lock) { content = intent.getParcelableArrayListExtra(EXTRA_DATA) ?: arrayListOf() id = intent.getStringExtra(EXTRA_ID) ?: UUID.randomUUID().toString() if (!intent.hasExtra(EXTRA_ID)) { Log.d(TAG, "Created new intent cache with id $id") } else if (intent.hasExtra(EXTRA_DATA)) { Log.d(TAG, "Recovered data from intent cache with id $id") } pendingActions.forEach { it() } pendingActions.clear() isReady = true updateIntent() } } } private fun runIfReady(action: () -> Unit) { synchronized(lock) { if (isReady) { action() } else { pendingActions.add(action) } } } private fun updateIntent() { synchronized(lock) { if (isReady) { val intent = getIntent().apply { putExtra(EXTRA_ID, id) putParcelableArrayListExtra(EXTRA_DATA, content) } val pendingIntent = PendingIntent.getService(context, type, intent, FLAG_NO_CREATE or FLAG_UPDATE_CURRENT or if (SDK_INT >= 31) FLAG_MUTABLE else 0) if (pendingIntent == null) { Log.w(TAG, "Failed to update existing pending intent, will likely have a loss of information") } } } } companion object { private const val TAG = "IntentCacheManager" private const val TEN_YEARS = 315360000000L private const val ACTION = "org.microg.gms.ACTION_INTENT_CACHE_MANAGER" private const val EXTRA_IS_CACHE = "org.microg.gms.IntentCacheManager.is_cache" private const val EXTRA_CACHE_TYPE = "org.microg.gms.IntentCacheManager.cache_type" private const val EXTRA_ID = "org.microg.gms.IntentCacheManager.id" private const val EXTRA_DATA = "org.microg.gms.IntentCacheManager.data" inline fun<reified S: Service, T: Parcelable> create(context: Context, type: Int) = IntentCacheManager<S, T>(context, S::class.java, type) fun isCache(intent: Intent): Boolean = try { intent.getBooleanExtra(EXTRA_IS_CACHE, false) } catch (e: Exception) { false } fun getType(intent: Intent): Int { val ret = intent.getIntExtra(EXTRA_CACHE_TYPE, -1) if (ret == -1) throw IllegalArgumentException() return ret } } } No newline at end of file play-services-location/core/build.gradle +8 −3 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2020, microG Project Team * SPDX-FileCopyrightText: 2023 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ Loading @@ -11,15 +11,20 @@ dependencies { compileOnly project(':play-services-location-system-api') implementation project(':play-services-base-core') implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" implementation "androidx.preference:preference-ktx:$preferenceVersion" 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-service:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "com.android.volley:volley:$volleyVersion" implementation 'org.microg:address-formatter:0.3.1' compileOnly project(':play-services-maps') } android { Loading play-services-location/core/src/main/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.BODY_SENSORS"/> Loading Loading
play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt +16 −0 Original line number Diff line number Diff line Loading @@ -145,6 +145,22 @@ object SettingsContract { ) } object Location { private const val id = "location" fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" const val WIFI_MLS = "location_wifi_mls" const val WIFI_MOVING = "location_wifi_moving" const val CELL_MLS = "location_cell_mls" val PROJECTION = arrayOf( WIFI_MLS, WIFI_MOVING, CELL_MLS ) } private fun <T> withoutCallingIdentity(f: () -> T): T { val identity = Binder.clearCallingIdentity() try { Loading
play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt +26 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import org.microg.gms.settings.SettingsContract.CheckIn 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.Profile import org.microg.gms.settings.SettingsContract.SafetyNet import org.microg.gms.settings.SettingsContract.getAuthority Loading Loading @@ -67,6 +68,7 @@ class SettingsProvider : ContentProvider() { SafetyNet.getContentUri(context!!) -> querySafetyNet(projection ?: SafetyNet.PROJECTION) DroidGuard.getContentUri(context!!) -> queryDroidGuard(projection ?: DroidGuard.PROJECTION) Profile.getContentUri(context!!) -> queryProfile(projection ?: Profile.PROJECTION) Location.getContentUri(context!!) -> queryLocation(projection ?: Location.PROJECTION) else -> null } Loading @@ -86,6 +88,7 @@ class SettingsProvider : ContentProvider() { SafetyNet.getContentUri(context!!) -> updateSafetyNet(values) DroidGuard.getContentUri(context!!) -> updateDroidGuard(values) Profile.getContentUri(context!!) -> updateProfile(values) Location.getContentUri(context!!) -> updateLocation(values) else -> return 0 } return 1 Loading Loading @@ -288,6 +291,29 @@ class SettingsProvider : ContentProvider() { editor.apply() } private fun queryLocation(p: Array<out String>): Cursor = MatrixCursor(p).addRow(p) { key -> when (key) { Location.WIFI_MLS -> getSettingsBoolean(key, true) Location.WIFI_MOVING -> getSettingsBoolean(key, true) Location.CELL_MLS -> getSettingsBoolean(key, true) else -> throw IllegalArgumentException("Unknown key: $key") } } private fun updateLocation(values: ContentValues) { if (values.size() == 0) return val editor = preferences.edit() values.valueSet().forEach { (key, value) -> when (key) { Location.WIFI_MLS -> editor.putBoolean(key, value as Boolean) Location.WIFI_MOVING -> editor.putBoolean(key, value as Boolean) Location.CELL_MLS -> editor.putBoolean(key, value as Boolean) else -> throw IllegalArgumentException("Unknown key: $key") } } editor.apply() } private fun MatrixCursor.addRow( p: Array<out String>, valueGetter: (String) -> Any? Loading
play-services-base/core/src/main/kotlin/org/microg/gms/utils/IntentCacheManager.kt 0 → 100644 +150 −0 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2023 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ package org.microg.gms.utils import android.app.AlarmManager import android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP import android.app.PendingIntent import android.app.PendingIntent.FLAG_NO_CREATE import android.app.PendingIntent.FLAG_MUTABLE import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.app.Service import android.content.Context import android.content.Intent import android.os.Build.VERSION.SDK_INT import android.os.Parcelable import android.os.SystemClock import android.util.Log import androidx.core.content.getSystemService import java.util.UUID class IntentCacheManager<S : Service, T : Parcelable>(private val context: Context, private val clazz: Class<S>, private val type: Int) { private val lock = Any() private lateinit var content: ArrayList<T> private lateinit var id: String private var isReady: Boolean = false private val pendingActions: MutableList<() -> Unit> = arrayListOf() init { val pendingIntent = PendingIntent.getService(context, type, getIntent(), if (SDK_INT >= 31) FLAG_MUTABLE else 0) val alarmManager = context.getSystemService<AlarmManager>() if (SDK_INT >= 19) { alarmManager?.setWindow(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TEN_YEARS, -1, pendingIntent) } else { alarmManager?.set(ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + TEN_YEARS, pendingIntent) } pendingIntent.send() } private fun getIntent() = Intent(context, clazz).apply { action = ACTION putExtra(EXTRA_IS_CACHE, true) putExtra(EXTRA_CACHE_TYPE, this@IntentCacheManager.type) } fun add(entry: T, check: (T) -> Boolean = { false }) = runIfReady { val iterator = content.iterator() while (iterator.hasNext()) { if (check(iterator.next())) { iterator.remove() } } content.add(entry) updateIntent() } fun remove(entry: T) = runIfReady { if (content.remove(entry)) updateIntent() } fun removeIf(check: (T) -> Boolean) = runIfReady { var removed = false val iterator = content.iterator() while (iterator.hasNext()) { if (check(iterator.next())) { iterator.remove() removed = true } } if (removed) updateIntent() } fun clear() = runIfReady { content.clear() updateIntent() } fun getId(): String? = if (this::id.isInitialized) id else null fun getEntries(): List<T> = if (this::content.isInitialized) content else emptyList() fun processIntent(intent: Intent) { if (isCache(intent) && getType(intent) == type) { synchronized(lock) { content = intent.getParcelableArrayListExtra(EXTRA_DATA) ?: arrayListOf() id = intent.getStringExtra(EXTRA_ID) ?: UUID.randomUUID().toString() if (!intent.hasExtra(EXTRA_ID)) { Log.d(TAG, "Created new intent cache with id $id") } else if (intent.hasExtra(EXTRA_DATA)) { Log.d(TAG, "Recovered data from intent cache with id $id") } pendingActions.forEach { it() } pendingActions.clear() isReady = true updateIntent() } } } private fun runIfReady(action: () -> Unit) { synchronized(lock) { if (isReady) { action() } else { pendingActions.add(action) } } } private fun updateIntent() { synchronized(lock) { if (isReady) { val intent = getIntent().apply { putExtra(EXTRA_ID, id) putParcelableArrayListExtra(EXTRA_DATA, content) } val pendingIntent = PendingIntent.getService(context, type, intent, FLAG_NO_CREATE or FLAG_UPDATE_CURRENT or if (SDK_INT >= 31) FLAG_MUTABLE else 0) if (pendingIntent == null) { Log.w(TAG, "Failed to update existing pending intent, will likely have a loss of information") } } } } companion object { private const val TAG = "IntentCacheManager" private const val TEN_YEARS = 315360000000L private const val ACTION = "org.microg.gms.ACTION_INTENT_CACHE_MANAGER" private const val EXTRA_IS_CACHE = "org.microg.gms.IntentCacheManager.is_cache" private const val EXTRA_CACHE_TYPE = "org.microg.gms.IntentCacheManager.cache_type" private const val EXTRA_ID = "org.microg.gms.IntentCacheManager.id" private const val EXTRA_DATA = "org.microg.gms.IntentCacheManager.data" inline fun<reified S: Service, T: Parcelable> create(context: Context, type: Int) = IntentCacheManager<S, T>(context, S::class.java, type) fun isCache(intent: Intent): Boolean = try { intent.getBooleanExtra(EXTRA_IS_CACHE, false) } catch (e: Exception) { false } fun getType(intent: Intent): Int { val ret = intent.getIntExtra(EXTRA_CACHE_TYPE, -1) if (ret == -1) throw IllegalArgumentException() return ret } } } No newline at end of file
play-services-location/core/build.gradle +8 −3 Original line number Diff line number Diff line /* * SPDX-FileCopyrightText: 2020, microG Project Team * SPDX-FileCopyrightText: 2023 microG Project Team * SPDX-License-Identifier: Apache-2.0 */ Loading @@ -11,15 +11,20 @@ dependencies { compileOnly project(':play-services-location-system-api') implementation project(':play-services-base-core') implementation "androidx.lifecycle:lifecycle-service:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion" implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion" implementation "androidx.preference:preference-ktx:$preferenceVersion" 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-service:$lifecycleVersion" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion" implementation "com.android.volley:volley:$volleyVersion" implementation 'org.microg:address-formatter:0.3.1' compileOnly project(':play-services-maps') } android { Loading
play-services-location/core/src/main/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,7 @@ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.BODY_SENSORS"/> Loading