From 51c4beba8eee2dee6e43fe573b6d9ccd1992b868 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 31 Jul 2024 19:34:45 +0700 Subject: [PATCH] UpdateWorker: Try to build and use tmp AuthData if required Users can skip opening Aurora Store for a long time after setting it once. They expect the auto-updates to continue working without any intervention. However, authdata can get expired over time. Try to build temp authdata and use it to check for updates without saving it in such cases. Signed-off-by: Aayush Gupta --- .../store/data/providers/AuthProvider.kt | 104 ++++++++++++++++++ .../aurora/store/data/work/UpdateWorker.kt | 10 +- .../java/com/aurora/store/util/AppUtil.kt | 23 ++-- .../store/view/ui/splash/SplashFragment.kt | 2 +- .../store/viewmodel/auth/AuthViewModel.kt | 55 ++------- 5 files changed, 133 insertions(+), 61 deletions(-) diff --git a/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt b/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt index bf7480d63..4e012b6ad 100644 --- a/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt +++ b/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt @@ -21,17 +21,25 @@ package com.aurora.store.data.providers import android.content.Context import com.aurora.Constants +import com.aurora.Constants.ACCOUNT_SIGNED_IN import com.aurora.gplayapi.data.models.AuthData +import com.aurora.gplayapi.helpers.AuthHelper import com.aurora.gplayapi.helpers.AuthValidator +import com.aurora.store.R import com.aurora.store.data.model.AccountType +import com.aurora.store.data.model.Auth import com.aurora.store.data.network.HttpClient import com.aurora.store.util.Log import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences.PREFERENCE_AUTH_DATA +import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS +import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION import com.google.gson.Gson import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import java.util.Locale +import java.util.Properties import javax.inject.Inject import javax.inject.Singleton @@ -41,6 +49,30 @@ class AuthProvider @Inject constructor( private val gson: Gson ) { + private val spoofProvider: SpoofProvider get() = SpoofProvider(context) + val properties: Properties + get() { + val currentProperties = if (spoofProvider.isDeviceSpoofEnabled()) { + spoofProvider.getSpoofDeviceProperties() + } else { + NativeDeviceInfoProvider(context).getNativeDeviceProperties() + } + setVendingVersion(currentProperties) + return currentProperties + } + val locale: Locale + get() = if (spoofProvider.isLocaleSpoofEnabled()) { + spoofProvider.getSpoofLocale() + } else { + Locale.getDefault() + } + + val dispenserURL: String? + get() { + val dispensers = Preferences.getStringSet(context, PREFERENCE_DISPENSER_URLS) + return if (dispensers.isNotEmpty()) dispensers.random() else null + } + val authData: AuthData? get() = getSavedAuthData() val isAnonymous: Boolean @@ -49,6 +81,23 @@ class AuthProvider @Inject constructor( return AccountType.valueOf(name) == AccountType.ANONYMOUS } + private val signedIn: Boolean + get() = Preferences.getBoolean(context, ACCOUNT_SIGNED_IN) + + /** + * Builds and returns AuthData based on signed-in account type + */ + suspend fun getTmpAuthData(): AuthData? { + return when { + !signedIn -> null + !isAnonymous -> buildGoogleAuthData() + else -> buildAnonymousAuthData() + } + } + + /** + * Checks whether saved AuthData is valid or not + */ suspend fun isSavedAuthDataValid(): Boolean { return withContext(Dispatchers.IO) { try { @@ -70,4 +119,59 @@ class AuthProvider @Inject constructor( null } } + + private suspend fun buildGoogleAuthData(): AuthData? { + return withContext(Dispatchers.IO) { + try { + val email = Preferences.getString(context, Constants.ACCOUNT_EMAIL_PLAIN) + val aasToken = Preferences.getString(context, Constants.ACCOUNT_AAS_PLAIN) + + return@withContext AuthHelper.build( + email = email, + token = aasToken, + tokenType = AuthHelper.Token.AAS, + properties = properties, + locale = locale + ) + } catch (exception: Exception) { + Log.e("Failed to generate Session", exception) + return@withContext null + } + } + } + + private suspend fun buildAnonymousAuthData(): AuthData? { + return withContext(Dispatchers.IO) { + try { + val playResponse = HttpClient + .getPreferredClient(context) + .getAuth(dispenserURL!!) + + val auth = gson.fromJson(String(playResponse.responseBytes), Auth::class.java) + return@withContext AuthHelper.build( + email = auth.email, + token = auth.auth, + tokenType = AuthHelper.Token.AUTH, + isAnonymous = true, + properties = properties, + locale = locale + ) + } catch (exception: Exception) { + Log.e("Failed to generate AuthData", exception) + return@withContext null + } + } + } + + private fun setVendingVersion(currentProperties: Properties) { + val vendingVersionIndex = Preferences.getInteger(context, PREFERENCE_VENDING_VERSION) + if (vendingVersionIndex > 0) { + val resources = context.resources + val versionCodes = resources.getStringArray(R.array.pref_vending_version_codes) + val versionStrings = resources.getStringArray(R.array.pref_vending_version) + + currentProperties.setProperty("Vending.version", versionCodes[vendingVersionIndex]) + currentProperties.setProperty("Vending.versionString", versionStrings[vendingVersionIndex]) + } + } } diff --git a/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt b/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt index d5c1327bc..4f7a359e3 100644 --- a/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt +++ b/app/src/main/java/com/aurora/store/data/work/UpdateWorker.kt @@ -121,13 +121,19 @@ class UpdateWorker @AssistedInject constructor( } withContext(Dispatchers.IO) { - if (!authProvider.isSavedAuthDataValid()) { + val authData = if (authProvider.isSavedAuthDataValid()) { + authProvider.authData + } else { + authProvider.getTmpAuthData() + } + + if (authData == null) { Log.i(TAG, "AuthData is not valid, retrying later!") return@withContext Result.retry() } try { - val updatesList = appUtil.checkUpdates() + val updatesList = appUtil.checkUpdates(authData) .filter { it.hasValidCert } .filterNot { it.isSelfUpdate() } diff --git a/app/src/main/java/com/aurora/store/util/AppUtil.kt b/app/src/main/java/com/aurora/store/util/AppUtil.kt index e24306a8a..48ef4da6a 100644 --- a/app/src/main/java/com/aurora/store/util/AppUtil.kt +++ b/app/src/main/java/com/aurora/store/util/AppUtil.kt @@ -6,6 +6,7 @@ import android.util.Log import androidx.core.content.pm.PackageInfoCompat import com.aurora.Constants import com.aurora.gplayapi.data.models.App +import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.helpers.AppDetailsHelper import com.aurora.store.AuroraApp import com.aurora.store.BuildConfig @@ -43,17 +44,18 @@ class AppUtil @Inject constructor( .map { list -> if (!isExtendedUpdateEnabled) list.filter { it.hasValidCert } else list } .stateIn(AuroraApp.scope, SharingStarted.WhileSubscribed(), null) - suspend fun checkUpdates(): List { + suspend fun checkUpdates(tmpAuthData: AuthData? = null): List { Log.i(TAG, "Checking for updates") val packageInfoMap = PackageUtil.getPackageInfoMap(context) - val appUpdatesList = getFilteredInstalledApps(packageInfoMap).filter { - val packageInfo = packageInfoMap[it.packageName] - if (packageInfo != null) { - it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo) - } else { - false - } - }.toMutableList() + val appUpdatesList = getFilteredInstalledApps(tmpAuthData, packageInfoMap) + .filter { + val packageInfo = packageInfoMap[it.packageName] + if (packageInfo != null) { + it.versionCode.toLong() > PackageInfoCompat.getLongVersionCode(packageInfo) + } else { + false + } + }.toMutableList() if (canSelfUpdate(context)) { getSelfUpdate(context, gson)?.let { appUpdatesList.add(it) } @@ -70,10 +72,11 @@ class AppUtil @Inject constructor( } suspend fun getFilteredInstalledApps( + tmpAuthData: AuthData? = null, packageInfoMap: MutableMap? = null ): List { return withContext(Dispatchers.IO) { - val appDetailsHelper = AppDetailsHelper(authProvider.authData!!) + val appDetailsHelper = AppDetailsHelper(tmpAuthData?: authProvider.authData!!) .using(HttpClient.getPreferredClient(context)) (packageInfoMap ?: PackageUtil.getPackageInfoMap(context)).keys.let { packages -> diff --git a/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt b/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt index 56cff652e..2678f00c0 100644 --- a/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/splash/SplashFragment.kt @@ -79,7 +79,7 @@ class SplashFragment : BaseFragment() { attachActions() // Show anonymous logins if we have dispenser URL - if (!viewModel.dispenserURL.isNullOrBlank()) { + if (!viewModel.authProvider.dispenserURL.isNullOrBlank()) { binding.btnAnonymous.visibility = View.VISIBLE } diff --git a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt index 30342380d..ed92a14d1 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt @@ -38,12 +38,9 @@ import com.aurora.store.data.model.AuthState import com.aurora.store.data.network.HttpClient import com.aurora.store.data.providers.AccountProvider import com.aurora.store.data.providers.AuthProvider -import com.aurora.store.data.providers.NativeDeviceInfoProvider -import com.aurora.store.data.providers.SpoofProvider import com.aurora.store.util.AC2DMTask import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences.PREFERENCE_AUTH_DATA -import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext @@ -52,44 +49,18 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.supervisorScope import java.net.ConnectException import java.net.UnknownHostException -import java.util.Locale -import java.util.Properties import javax.inject.Inject @HiltViewModel @SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253 class AuthViewModel @Inject constructor( + val authProvider: AuthProvider, @ApplicationContext private val context: Context, - private val gson: Gson, - private val authProvider: AuthProvider + private val gson: Gson ) : ViewModel() { private val TAG = AuthViewModel::class.java.simpleName - private val spoofProvider: SpoofProvider get() = SpoofProvider(context) - val properties: Properties - get() { - val currentProperties = if (spoofProvider.isDeviceSpoofEnabled()) { - spoofProvider.getSpoofDeviceProperties() - } else { - NativeDeviceInfoProvider(context).getNativeDeviceProperties() - } - setVendingVersion(currentProperties) - return currentProperties - } - val locale: Locale - get() = if (spoofProvider.isLocaleSpoofEnabled()) { - spoofProvider.getSpoofLocale() - } else { - Locale.getDefault() - } - - val dispenserURL: String? - get() { - val dispensers = Preferences.getStringSet(context, PREFERENCE_DISPENSER_URLS) - return if (dispensers.isNotEmpty()) dispensers.random() else null - } - val liveData: MutableLiveData = MutableLiveData() fun observe() { @@ -112,8 +83,8 @@ class AuthViewModel @Inject constructor( email = email, token = aasToken, tokenType = AuthHelper.Token.AAS, - properties = properties, - locale = locale + properties = authProvider.properties, + locale = authProvider.locale ) verifyAndSaveAuth(authData, AccountType.GOOGLE) } catch (exception: Exception) { @@ -131,7 +102,7 @@ class AuthViewModel @Inject constructor( try { val playResponse = HttpClient .getPreferredClient(context) - .getAuth(dispenserURL!!) + .getAuth(authProvider.dispenserURL!!) if (playResponse.isSuccessful) { val tmpAuthData = gson.fromJson( @@ -144,8 +115,8 @@ class AuthViewModel @Inject constructor( token = tmpAuthData.auth, tokenType = AuthHelper.Token.AUTH, isAnonymous = true, - properties = properties, - locale = locale + properties = authProvider.properties, + locale = authProvider.locale ) verifyAndSaveAuth(authData, AccountType.ANONYMOUS) } else { @@ -245,18 +216,6 @@ class AuthViewModel @Inject constructor( } } - private fun setVendingVersion(currentProperties: Properties) { - val vendingVersionIndex = Preferences.getInteger(context, Preferences.PREFERENCE_VENDING_VERSION) - if (vendingVersionIndex > 0) { - val resources = context.resources - val versionCodes = resources.getStringArray(R.array.pref_vending_version_codes) - val versionStrings = resources.getStringArray(R.array.pref_vending_version) - - currentProperties.setProperty("Vending.version", versionCodes[vendingVersionIndex]) - currentProperties.setProperty("Vending.versionString", versionStrings[vendingVersionIndex]) - } - } - private fun configAuthPref(authData: AuthData, type: AccountType, signedIn: Boolean) { if (signedIn) { //Save Auth Data