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 <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-07-31 19:34:45 +07:00
parent 32951e598a
commit 51c4beba8e
5 changed files with 133 additions and 61 deletions

View File

@@ -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])
}
}
}

View File

@@ -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() }

View File

@@ -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<Update> {
suspend fun checkUpdates(tmpAuthData: AuthData? = null): List<Update> {
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<String, PackageInfo>? = null
): List<App> {
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 ->

View File

@@ -79,7 +79,7 @@ class SplashFragment : BaseFragment<FragmentSplashBinding>() {
attachActions()
// Show anonymous logins if we have dispenser URL
if (!viewModel.dispenserURL.isNullOrBlank()) {
if (!viewModel.authProvider.dispenserURL.isNullOrBlank()) {
binding.btnAnonymous.visibility = View.VISIBLE
}

View File

@@ -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<AuthState> = 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