From 2ff9966bf5d8c9f35533f509a85ecb94f1e0cbfe Mon Sep 17 00:00:00 2001 From: Marvin W Date: Wed, 28 Feb 2024 11:26:02 +0100 Subject: [PATCH] Auth: Allow stripping device name from requests Helps with some banned devices. Enable for -hw build by default. --- .../microg/gms/settings/SettingsContract.kt | 216 +++++++++ .../microg/gms/settings/SettingsProvider.kt | 421 ++++++++++++++++++ .../src/huawei/AndroidManifest.xml | 32 ++ .../src/huaweilh/AndroidManifest.xml | 53 +++ .../java/org/microg/gms/auth/AuthRequest.java | 10 + .../kotlin/org/microg/gms/auth/AuthPrefs.kt | 7 + .../src/main/res/values/strings.xml | 19 +- .../src/main/res/xml/preferences_account.xml | 8 + 8 files changed, 765 insertions(+), 1 deletion(-) create mode 100644 play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt create mode 100644 play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt create mode 100644 play-services-core/src/huawei/AndroidManifest.xml create mode 100644 play-services-core/src/huaweilh/AndroidManifest.xml 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 new file mode 100644 index 00000000..c2b37fdf --- /dev/null +++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsContract.kt @@ -0,0 +1,216 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.settings + +import android.content.ContentValues +import android.content.Context +import android.database.Cursor +import android.net.Uri +import android.os.Binder + +object SettingsContract { + fun getAuthority(context: Context) = "${context.packageName}.microg.settings" + fun getAuthorityUri(context: Context): Uri = Uri.parse("content://${getAuthority(context)}") + + object CheckIn { + private const val id = "check-in" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val ENABLED = "checkin_enable_service" + const val ANDROID_ID = "androidId" + const val DIGEST = "digest" + const val LAST_CHECK_IN = "lastCheckin" + const val SECURITY_TOKEN = "securityToken" + const val VERSION_INFO = "versionInfo" + const val DEVICE_DATA_VERSION_INFO = "deviceDataVersionInfo" + + val PROJECTION = arrayOf( + ENABLED, + ANDROID_ID, + DIGEST, + LAST_CHECK_IN, + SECURITY_TOKEN, + VERSION_INFO, + DEVICE_DATA_VERSION_INFO, + ) + const val PREFERENCES_NAME = "checkin" + const val INITIAL_DIGEST = "1-929a0dca0eee55513280171a8585da7dcd3700f8" + } + + object Gcm { + private const val id = "gcm" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val FULL_LOG = "gcm_full_log" + const val LAST_PERSISTENT_ID = "gcm_last_persistent_id" + const val CONFIRM_NEW_APPS = "gcm_confirm_new_apps" + const val ENABLE_GCM = "gcm_enable_mcs_service" + + const val NETWORK_MOBILE = "gcm_network_mobile" + const val NETWORK_WIFI = "gcm_network_wifi" + const val NETWORK_ROAMING = "gcm_network_roaming" + const val NETWORK_OTHER = "gcm_network_other" + + const val LEARNT_MOBILE = "gcm_learnt_mobile" + const val LEARNT_WIFI = "gcm_learnt_wifi" + const val LEARNT_OTHER = "gcm_learnt_other" + + val PROJECTION = arrayOf( + FULL_LOG, + LAST_PERSISTENT_ID, + CONFIRM_NEW_APPS, + ENABLE_GCM, + NETWORK_MOBILE, + NETWORK_WIFI, + NETWORK_ROAMING, + NETWORK_OTHER, + LEARNT_MOBILE, + LEARNT_WIFI, + LEARNT_OTHER, + ) + } + + object Auth { + private const val id = "auth" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val TRUST_GOOGLE = "auth_manager_trust_google" + const val VISIBLE = "auth_manager_visible" + const val INCLUDE_ANDROID_ID = "auth_include_android_id" + const val STRIP_DEVICE_NAME = "auth_strip_device_name" + + val PROJECTION = arrayOf( + TRUST_GOOGLE, + VISIBLE, + INCLUDE_ANDROID_ID, + STRIP_DEVICE_NAME, + ) + } + + object Exposure { + private const val id = "exposureNotification" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val SCANNER_ENABLED = "exposure_scanner_enabled" + const val LAST_CLEANUP = "exposure_last_cleanup" + + val PROJECTION = arrayOf( + SCANNER_ENABLED, + LAST_CLEANUP, + ) + } + + object SafetyNet { + private const val id = "safety-net" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val ENABLED = "safetynet_enabled" + + val PROJECTION = arrayOf( + ENABLED + ) + } + + object DroidGuard { + private const val id = "droidguard" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val ENABLED = "droidguard_enabled" + const val MODE = "droidguard_mode" + const val NETWORK_SERVER_URL = "droidguard_network_server_url" + const val FORCE_LOCAL_DISABLED = "droidguard_force_local_disabled" + + val PROJECTION = arrayOf( + ENABLED, + MODE, + NETWORK_SERVER_URL, + FORCE_LOCAL_DISABLED, + ) + } + + object Profile { + private const val id = "profile" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val PROFILE = "device_profile" + const val SERIAL = "device_profile_serial" + + val PROJECTION = arrayOf( + PROFILE, + SERIAL + ) + } + + 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 WIFI_LEARNING = "location_wifi_learning" + const val CELL_MLS = "location_cell_mls" + const val CELL_LEARNING = "location_cell_learning" + const val GEOCODER_NOMINATIM = "location_geocoder_nominatim" + + val PROJECTION = arrayOf( + WIFI_MLS, + WIFI_MOVING, + WIFI_LEARNING, + CELL_MLS, + CELL_LEARNING, + GEOCODER_NOMINATIM, + ) + } + + object Vending { + private const val id = "vending" + fun getContentUri(context: Context) = Uri.withAppendedPath(getAuthorityUri(context), id) + fun getContentType(context: Context) = "vnd.android.cursor.item/vnd.${getAuthority(context)}.$id" + + const val LICENSING = "vending_licensing" + + val PROJECTION = arrayOf( + LICENSING + ) + } + + private fun withoutCallingIdentity(f: () -> T): T { + val identity = Binder.clearCallingIdentity() + try { + return f.invoke() + } finally { + Binder.restoreCallingIdentity(identity) + } + } + + @JvmStatic + fun getSettings(context: Context, uri: Uri, projection: Array?, f: (Cursor) -> T): T = withoutCallingIdentity { + val c = context.contentResolver.query(uri, projection, null, null, null) + try { + require(c != null) { "Cursor for query $uri ${projection?.toList()} was null" } + if (!c.moveToFirst()) error("Cursor for query $uri ${projection?.toList()} was empty") + f.invoke(c) + } finally { + c?.close() + } + } + + @JvmStatic + fun setSettings(context: Context, uri: Uri, v: ContentValues.() -> Unit) = withoutCallingIdentity { + val values = ContentValues().apply { v.invoke(this) } + val affected = context.contentResolver.update(uri, values, null, null) + require(affected == 1) { "Update for $uri with $values affected 0 rows"} + } + +} 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 new file mode 100644 index 00000000..065c7079 --- /dev/null +++ b/play-services-base/core/src/main/kotlin/org/microg/gms/settings/SettingsProvider.kt @@ -0,0 +1,421 @@ +/* + * SPDX-FileCopyrightText: 2021, microG Project Team + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.microg.gms.settings + +import android.content.ContentProvider +import android.content.ContentValues +import android.content.Context +import android.content.Context.MODE_PRIVATE +import android.content.SharedPreferences +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.common.PackageUtils.warnIfNotMainProcess +import org.microg.gms.settings.SettingsContract.Auth +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.Vending +import org.microg.gms.settings.SettingsContract.Profile +import org.microg.gms.settings.SettingsContract.SafetyNet +import org.microg.gms.settings.SettingsContract.getAuthority +import java.io.File + +private const val SETTINGS_PREFIX = "org.microg.gms.settings." + +/** + * All settings access should go through this [ContentProvider], + * because it provides safe access from different processes which normal [SharedPreferences] don't. + */ +class SettingsProvider : ContentProvider() { + + private val preferences: SharedPreferences by lazy { + PreferenceManager.getDefaultSharedPreferences(context) + } + private val checkInPrefs by lazy { + context!!.getSharedPreferences(CheckIn.PREFERENCES_NAME, MODE_PRIVATE) + } + private val unifiedNlpPreferences by lazy { + context!!.getSharedPreferences("unified_nlp", MODE_PRIVATE) + } + private val systemDefaultPreferences: SharedPreferences? by lazy { + try { + Context::class.java.getDeclaredMethod( + "getSharedPreferences", + File::class.java, + Int::class.javaPrimitiveType + ).invoke(context, File("/system/etc/microg.xml"), MODE_PRIVATE) as SharedPreferences + } catch (ignored: Exception) { + null + } + } + private val metaDataPreferences: SharedPreferences by lazy { + MetaDataPreferences(context!!, SETTINGS_PREFIX) + } + + override fun onCreate(): Boolean { + return true + } + + override fun query( + uri: Uri, + projection: Array?, + selection: String?, + selectionArgs: Array?, + sortOrder: String? + ): Cursor? = when (uri) { + CheckIn.getContentUri(context!!) -> queryCheckIn(projection ?: CheckIn.PROJECTION) + Gcm.getContentUri(context!!) -> queryGcm(projection ?: Gcm.PROJECTION) + Auth.getContentUri(context!!) -> queryAuth(projection ?: Auth.PROJECTION) + Exposure.getContentUri(context!!) -> queryExposure(projection ?: Exposure.PROJECTION) + 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) + Vending.getContentUri(context!!) -> queryVending(projection ?: Vending.PROJECTION) + else -> null + } + + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array? + ): Int { + warnIfNotMainProcess(context, this.javaClass) + if (values == null) return 0 + when (uri) { + CheckIn.getContentUri(context!!) -> updateCheckIn(values) + Gcm.getContentUri(context!!) -> updateGcm(values) + Auth.getContentUri(context!!) -> updateAuth(values) + Exposure.getContentUri(context!!) -> updateExposure(values) + SafetyNet.getContentUri(context!!) -> updateSafetyNet(values) + DroidGuard.getContentUri(context!!) -> updateDroidGuard(values) + Profile.getContentUri(context!!) -> updateProfile(values) + Location.getContentUri(context!!) -> updateLocation(values) + Vending.getContentUri(context!!) -> updateVending(values) + else -> return 0 + } + return 1 + } + + private fun queryCheckIn(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + CheckIn.ENABLED -> getSettingsBoolean(key, false) + CheckIn.ANDROID_ID -> checkInPrefs.getLong(key, 0) + CheckIn.DIGEST -> checkInPrefs.getString(key, CheckIn.INITIAL_DIGEST) + ?: CheckIn.INITIAL_DIGEST + CheckIn.LAST_CHECK_IN -> checkInPrefs.getLong(key, 0) + CheckIn.SECURITY_TOKEN -> checkInPrefs.getLong(key, 0) + CheckIn.VERSION_INFO -> checkInPrefs.getString(key, "") ?: "" + CheckIn.DEVICE_DATA_VERSION_INFO -> checkInPrefs.getString(key, "") ?: "" + else -> throw IllegalArgumentException() + } + } + + private fun updateCheckIn(values: ContentValues) { + if (values.size() == 0) return + if (values.size() == 1 && values.containsKey(CheckIn.ENABLED)) { + // special case: only changing enabled state + updateCheckInEnabled(values.getAsBoolean(CheckIn.ENABLED)) + return + } + val editor = checkInPrefs.edit() + values.valueSet().forEach { (key, value) -> + if (key == CheckIn.ENABLED) { + // special case: not saved in checkInPrefs + updateCheckInEnabled(value as Boolean) + } + when (key) { + CheckIn.ANDROID_ID -> editor.putLong(key, value as Long) + CheckIn.DIGEST -> editor.putString(key, value as String?) + CheckIn.LAST_CHECK_IN -> editor.putLong(key, value as Long) + CheckIn.SECURITY_TOKEN -> editor.putLong(key, value as Long) + CheckIn.VERSION_INFO -> editor.putString(key, value as String?) + CheckIn.DEVICE_DATA_VERSION_INFO -> editor.putString(key, value as String?) + } + } + editor.apply() + } + + private fun updateCheckInEnabled(enabled: Boolean) { + preferences.edit() + .putBoolean(CheckIn.ENABLED, enabled) + .apply() + } + + private fun queryGcm(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Gcm.ENABLE_GCM -> getSettingsBoolean(key, false) + Gcm.FULL_LOG -> getSettingsBoolean(key, true) + Gcm.CONFIRM_NEW_APPS -> getSettingsBoolean(key, false) + + Gcm.LAST_PERSISTENT_ID -> preferences.getString(key, "") ?: "" + + Gcm.NETWORK_MOBILE -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + Gcm.NETWORK_WIFI -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + Gcm.NETWORK_ROAMING -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + Gcm.NETWORK_OTHER -> Integer.parseInt(preferences.getString(key, "0") ?: "0") + + Gcm.LEARNT_MOBILE -> preferences.getInt(key, 300000) + Gcm.LEARNT_WIFI -> preferences.getInt(key, 300000) + Gcm.LEARNT_OTHER -> preferences.getInt(key, 300000) + + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateGcm(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Gcm.ENABLE_GCM -> editor.putBoolean(key, value as Boolean) + Gcm.FULL_LOG -> editor.putBoolean(key, value as Boolean) + Gcm.CONFIRM_NEW_APPS -> editor.putBoolean(key, value as Boolean) + + Gcm.LAST_PERSISTENT_ID -> editor.putString(key, value as String?) + + Gcm.NETWORK_MOBILE -> editor.putString(key, (value as Int).toString()) + Gcm.NETWORK_WIFI -> editor.putString(key, (value as Int).toString()) + Gcm.NETWORK_ROAMING -> editor.putString(key, (value as Int).toString()) + Gcm.NETWORK_OTHER -> editor.putString(key, (value as Int).toString()) + + Gcm.LEARNT_MOBILE -> editor.putInt(key, value as Int) + Gcm.LEARNT_WIFI -> editor.putInt(key, value as Int) + Gcm.LEARNT_OTHER -> editor.putInt(key, value as Int) + + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryAuth(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Auth.TRUST_GOOGLE -> getSettingsBoolean(key, true) + Auth.VISIBLE -> getSettingsBoolean(key, false) + Auth.INCLUDE_ANDROID_ID -> getSettingsBoolean(key, true) + Auth.STRIP_DEVICE_NAME -> getSettingsBoolean(key, false) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateAuth(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Auth.TRUST_GOOGLE -> editor.putBoolean(key, value as Boolean) + Auth.VISIBLE -> editor.putBoolean(key, value as Boolean) + Auth.INCLUDE_ANDROID_ID -> editor.putBoolean(key, value as Boolean) + Auth.STRIP_DEVICE_NAME -> editor.putBoolean(key, value as Boolean) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryExposure(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Exposure.SCANNER_ENABLED -> getSettingsBoolean(key, false) + Exposure.LAST_CLEANUP -> preferences.getLong(key, 0L) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateExposure(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Exposure.SCANNER_ENABLED -> editor.putBoolean(key, value as Boolean) + Exposure.LAST_CLEANUP -> editor.putLong(key, value as Long) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun querySafetyNet(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + SafetyNet.ENABLED -> getSettingsBoolean(key, false) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateSafetyNet(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + SafetyNet.ENABLED -> editor.putBoolean(key, value as Boolean) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryDroidGuard(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + DroidGuard.ENABLED -> getSettingsBoolean(key, false) + DroidGuard.MODE -> getSettingsString(key) + DroidGuard.NETWORK_SERVER_URL -> getSettingsString(key) + DroidGuard.FORCE_LOCAL_DISABLED -> systemDefaultPreferences?.getBoolean(key, false) ?: false + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateDroidGuard(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + DroidGuard.ENABLED -> editor.putBoolean(key, value as Boolean) + DroidGuard.MODE -> editor.putString(key, value as String) + DroidGuard.NETWORK_SERVER_URL -> editor.putString(key, value as String) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryProfile(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Profile.PROFILE -> getSettingsString(key, "auto") + Profile.SERIAL -> getSettingsString(key) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateProfile(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Profile.PROFILE -> editor.putString(key, value as String?) + Profile.SERIAL -> editor.putString(key, value as String?) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryLocation(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Location.WIFI_MLS -> getSettingsBoolean(key, hasUnifiedNlpLocationBackend("org.microg.nlp.backend.ichnaea")) + Location.WIFI_MOVING -> getSettingsBoolean(key, hasUnifiedNlpLocationBackend("de.sorunome.unifiednlp.trains")) + Location.WIFI_LEARNING -> getSettingsBoolean(key, hasUnifiedNlpLocationBackend("helium314.localbackend", "org.fitchfamily.android.dejavu")) + Location.CELL_MLS -> getSettingsBoolean(key, hasUnifiedNlpLocationBackend("org.microg.nlp.backend.ichnaea")) + Location.CELL_LEARNING -> getSettingsBoolean(key, hasUnifiedNlpLocationBackend("helium314.localbackend", "org.fitchfamily.android.dejavu")) + Location.GEOCODER_NOMINATIM -> getSettingsBoolean(key, hasUnifiedNlpGeocoderBackend("org.microg.nlp.backend.nominatim") ) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + private fun hasUnifiedNlpPrefixInStringSet(key: String, vararg prefixes: String) = getUnifiedNlpSettingsStringSetCompat(key, emptySet()).any { entry -> prefixes.any { prefix -> entry.startsWith(prefix)}} + private fun hasUnifiedNlpLocationBackend(vararg packageNames: String) = hasUnifiedNlpPrefixInStringSet("location_backends", *packageNames.map { "$it/" }.toTypedArray()) + private fun hasUnifiedNlpGeocoderBackend(vararg packageNames: String) = hasUnifiedNlpPrefixInStringSet("geocoder_backends", *packageNames.map { "$it/" }.toTypedArray()) + + 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.WIFI_LEARNING -> editor.putBoolean(key, value as Boolean) + Location.CELL_MLS -> editor.putBoolean(key, value as Boolean) + Location.CELL_LEARNING -> editor.putBoolean(key, value as Boolean) + Location.GEOCODER_NOMINATIM -> editor.putBoolean(key, value as Boolean) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun queryVending(p: Array): Cursor = MatrixCursor(p).addRow(p) { key -> + when (key) { + Vending.LICENSING -> getSettingsBoolean(key, false) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + + private fun updateVending(values: ContentValues) { + if (values.size() == 0) return + val editor = preferences.edit() + values.valueSet().forEach { (key, value) -> + when (key) { + Vending.LICENSING -> editor.putBoolean(key, value as Boolean) + else -> throw IllegalArgumentException("Unknown key: $key") + } + } + editor.apply() + } + + private fun MatrixCursor.addRow( + p: Array, + valueGetter: (String) -> Any? + ): MatrixCursor { + val row = newRow() + for (key in p) row.add(valueGetter.invoke(key)) + return this + } + + override fun getType(uri: Uri): String { + return "vnd.android.cursor.item/vnd.${getAuthority(context!!)}.${uri.path}" + } + + override fun insert(uri: Uri, values: ContentValues?): Uri? { + throw UnsupportedOperationException() + } + + override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { + throw UnsupportedOperationException() + } + + /** + * Returns the current setting of the given [key] + * using the default value from [systemDefaultPreferences] or [def] if not available. + * @return the current setting as [Int], because [ContentProvider] does not support [Boolean]. + */ + private fun getSettingsBoolean(key: String, def: Boolean): Int { + return listOf(preferences, systemDefaultPreferences, metaDataPreferences).getBooleanAsInt(key, def) + } + + private fun getSettingsString(key: String, def: String? = null): String? = listOf(preferences, systemDefaultPreferences, metaDataPreferences).getString(key, def) + private fun getSettingsInt(key: String, def: Int): Int = listOf(preferences, systemDefaultPreferences, metaDataPreferences).getInt(key, def) + private fun getSettingsLong(key: String, def: Long): Long = listOf(preferences, systemDefaultPreferences, metaDataPreferences).getLong(key, def) + private fun getUnifiedNlpSettingsStringSetCompat(key: String, def: Set): Set = listOf(unifiedNlpPreferences, preferences, systemDefaultPreferences).getStringSetCompat(key, def) + + private fun SharedPreferences.getStringSetCompat(key: String, def: Set): Set { + if (SDK_INT >= 11) { + try { + val res = getStringSet(key, null) + if (res != null) return res.filter { it.isNotEmpty() }.toSet() + } catch (ignored: Exception) { + // Ignore + } + } + try { + val str = getString(key, null) + if (str != null) return str.split("\\|".toRegex()).filter { it.isNotEmpty() }.toSet() + } catch (ignored: Exception) { + // Ignore + } + return def + } + + private fun List.getStringSetCompat(key: String, def: Set): Set = foldRight(def) { preferences, defValue -> preferences?.getStringSetCompat(key, defValue) ?: defValue } + private fun List.getString(key: String, def: String?): String? = foldRight(def) { preferences, defValue -> preferences?.getString(key, defValue) ?: defValue } + private fun List.getInt(key: String, def: Int): Int = foldRight(def) { preferences, defValue -> preferences?.getInt(key, defValue) ?: defValue } + private fun List.getLong(key: String, def: Long): Long = foldRight(def) { preferences, defValue -> preferences?.getLong(key, defValue) ?: defValue } + private fun List.getBoolean(key: String, def: Boolean): Boolean = foldRight(def) { preferences, defValue -> preferences?.getBoolean(key, defValue) ?: defValue } + private fun List.getBooleanAsInt(key: String, def: Boolean): Int = if (getBoolean(key, def)) 1 else 0 +} diff --git a/play-services-core/src/huawei/AndroidManifest.xml b/play-services-core/src/huawei/AndroidManifest.xml new file mode 100644 index 00000000..f6b7f6d1 --- /dev/null +++ b/play-services-core/src/huawei/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/huaweilh/AndroidManifest.xml b/play-services-core/src/huaweilh/AndroidManifest.xml new file mode 100644 index 00000000..b2359ec5 --- /dev/null +++ b/play-services-core/src/huaweilh/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java index 55ff536b..adbabdc2 100644 --- a/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java +++ b/play-services-core/src/main/java/org/microg/gms/auth/AuthRequest.java @@ -113,7 +113,17 @@ public class AuthRequest extends HttpFormClient.Request { public AuthRequest fromContext(Context context) { build(context); locale(Utils.getLocale(context)); +<<<<<<< HEAD androidIdHex = Long.toHexString(LastCheckinInfo.read(context).getAndroidId()); +======= + if (AuthPrefs.shouldIncludeAndroidId(context)) { + androidIdHex = Long.toHexString(LastCheckinInfo.read(context).getAndroidId()); + } + if (AuthPrefs.shouldStripDeviceName(context)) { + deviceName = ""; + buildVersion = ""; + } +>>>>>>> 9028d03a (Auth: Allow stripping device name from requests) return this; } diff --git a/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt b/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt index a86db122..7ecc5a8e 100644 --- a/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt +++ b/play-services-core/src/main/kotlin/org/microg/gms/auth/AuthPrefs.kt @@ -24,4 +24,11 @@ object AuthPrefs { } } + @JvmStatic + fun shouldStripDeviceName(context: Context): Boolean { + return SettingsContract.getSettings(context, Auth.getContentUri(context), arrayOf(Auth.STRIP_DEVICE_NAME)) { c -> + c.getInt(0) != 0 + } + } + } diff --git a/play-services-core/src/main/res/values/strings.xml b/play-services-core/src/main/res/values/strings.xml index a420ef9c..81090489 100755 --- a/play-services-core/src/main/res/values/strings.xml +++ b/play-services-core/src/main/res/values/strings.xml @@ -83,9 +83,26 @@ This can take a couple of minutes." Sign-in & security Trust Google for app permissions - When disabled, the user is asked before an apps authorization request is sent to Google. Some applications will fail to use the Google account if this is disabled. + When disabled, the user is asked before an app\'s authorization request is sent to Google. Some applications will fail to use the Google account if this is disabled. Allow apps to find accounts When enabled, all applications on this device will be able to see email address of your Google Accounts without prior authorization. +<<<<<<< HEAD +======= + Authenticate with device registration + When disabled, authentication requests won\'t be linked to the device registration, which may allow unauthorized devices to sign in, but may have unforeseen consequences. + Strip device name for authentication + When enabled, authentication requests won\'t include the device\'s name, which may allow unauthorized devices to sign in, but may have unforeseen consequences. + + Registers your device to Google services and creates a unique device identifier. microG strips identifying bits other than your Google account name from registration data. + Android ID + + Not registered + Last registration: %1$s + Register device + + Status + More +>>>>>>> 9028d03a (Auth: Allow stripping device name from requests) Account Add Google account diff --git a/play-services-core/src/main/res/xml/preferences_account.xml b/play-services-core/src/main/res/xml/preferences_account.xml index 4b564524..d42bdb15 100755 --- a/play-services-core/src/main/res/xml/preferences_account.xml +++ b/play-services-core/src/main/res/xml/preferences_account.xml @@ -58,7 +58,15 @@ android:key="auth_include_android_id" android:summary="@string/pref_auth_include_android_id_summary" android:title="@string/pref_auth_include_android_id_title"/> +<<<<<<< HEAD >>>>>>> 566215bd (UI: Port more to Material 3) +======= + +>>>>>>> 9028d03a (Auth: Allow stripping device name from requests)