Audit DownloadHelper & UpdateHelper scopes

Action Items:
- monitor changes for ~3days, do self-test to ensure it doesn't break anything
- also verify changes on older devices & other android skins MIUI, OneUI, EMUI
This commit is contained in:
Rahul Patel
2025-02-28 03:21:38 +05:30
parent 33462d772a
commit b24eafea7a
4 changed files with 46 additions and 39 deletions

View File

@@ -22,8 +22,8 @@ package com.aurora.store
import android.app.Application
import android.content.Context
import android.util.Log.INFO
import android.util.Log.DEBUG
import android.util.Log.INFO
import androidx.core.content.ContextCompat
import androidx.hilt.work.HiltWorkerFactory
import androidx.work.Configuration
@@ -43,8 +43,11 @@ import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences
import com.google.android.material.color.DynamicColors
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
import org.lsposed.hiddenapibypass.HiddenApiBypass
import javax.inject.Inject
@@ -71,8 +74,7 @@ class AuroraApp : Application(), Configuration.Provider, SingletonImageLoader.Fa
.build()
companion object {
// Alternative to GlobalScope
var scope = MainScope()
var scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
private set
val enqueuedInstalls: MutableSet<String> = mutableSetOf()
@@ -95,8 +97,10 @@ class AuroraApp : Application(), Configuration.Provider, SingletonImageLoader.Fa
NotificationUtil.createNotificationChannel(this)
// Initialize Download and Update helpers to observe and trigger downloads
downloadHelper.init()
updateHelper.init()
scope.launch {
downloadHelper.init()
updateHelper.init()
}
//Register broadcast receiver for package install/uninstall
ContextCompat.registerReceiver(
@@ -112,7 +116,12 @@ class AuroraApp : Application(), Configuration.Provider, SingletonImageLoader.Fa
override fun onLowMemory() {
super.onLowMemory()
scope.cancel("onLowMemory() called by system")
scope = MainScope()
scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
}
override fun onTerminate() {
super.onTerminate()
scope.cancel()
}
override fun newImageLoader(context: Context): ImageLoader {

View File

@@ -16,12 +16,12 @@ import com.aurora.store.data.room.update.Update
import com.aurora.store.data.work.DownloadWorker
import com.aurora.store.util.PathUtil
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
/**
@@ -49,30 +49,26 @@ class DownloadHelper @Inject constructor(
/**
* Removes failed download from the queue and starts observing for newly enqueued apps.
*/
fun init() {
AuroraApp.scope.launch {
suspend fun init() {
withContext(Dispatchers.IO) {
cancelFailedDownloads(downloadDao.downloads().firstOrNull() ?: emptyList())
}.invokeOnCompletion {
observeDownloads()
}
observeDownloads()
}
private fun observeDownloads() {
AuroraApp.scope.launch {
downloadDao.downloads().collectLatest { list ->
// Check and trigger next download in queue, if any
if (!list.any { it.downloadStatus == DownloadStatus.DOWNLOADING }) {
val enqueuedDownloads = list.filter { it.downloadStatus == DownloadStatus.QUEUED }
enqueuedDownloads.firstOrNull()?.let {
try {
Log.i(DOWNLOAD_WORKER, "Downloading ${it.packageName}")
trigger(it)
} catch (exception: Exception) {
Log.i(DOWNLOAD_WORKER, "Failed to download app", exception)
downloadDao.updateStatus(it.packageName, DownloadStatus.FAILED)
private suspend fun observeDownloads() {
downloadDao.downloads().collect { list ->
try {
if (list.none { it.downloadStatus == DownloadStatus.DOWNLOADING }) {
list.find { it.downloadStatus == DownloadStatus.QUEUED }
?.let { queuedDownload ->
Log.i(TAG, "Enqueued download worker for ${queuedDownload.packageName}")
trigger(queuedDownload)
}
}
}
} catch (exception: Exception) {
Log.e(TAG, "Failed to enqueue download worker", exception)
}
}
}
@@ -178,8 +174,9 @@ class DownloadHelper @Inject constructor(
// Ensure all app downloads are unique to preserve individual records
WorkManager.getInstance(context)
.enqueueUniqueWork(
"$DOWNLOAD_WORKER/${download.packageName}",
ExistingWorkPolicy.KEEP, work
"$DOWNLOAD_WORKER/${download.packageName}/${download.versionCode}",
ExistingWorkPolicy.KEEP,
work
)
}
}

View File

@@ -25,13 +25,14 @@ import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_IDLE
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_METERED
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit.HOURS
import java.util.concurrent.TimeUnit.MINUTES
import javax.inject.Inject
@@ -68,12 +69,12 @@ class UpdateHelper @Inject constructor(
/**
* Deletes invalid updates from database and starts observing events
*/
fun init() {
AuroraApp.scope.launch {
suspend fun init() {
withContext(Dispatchers.IO) {
deleteInvalidUpdates()
}.invokeOnCompletion {
observeUpdates()
}
observeUpdates()
}
private fun observeUpdates() {
@@ -137,7 +138,7 @@ class UpdateHelper @Inject constructor(
* @see [UpdateWorker]
*/
fun scheduleAutomatedCheck() {
Log.i(TAG,"Scheduling periodic app updates!")
Log.i(TAG, "Scheduling periodic app updates!")
WorkManager.getInstance(context)
.enqueueUniquePeriodicWork(
UPDATE_WORKER,
@@ -151,7 +152,7 @@ class UpdateHelper @Inject constructor(
* @see [UpdateWorker]
*/
fun updateAutomatedCheck() {
Log.i(TAG,"Updating periodic app updates!")
Log.i(TAG, "Updating periodic app updates!")
WorkManager.getInstance(context).updateWork(getAutoUpdateWork())
}

View File

@@ -176,7 +176,7 @@ class DownloadWorker @AssistedInject constructor(
throw Exceptions.DownloadCancelledException()
}
downloadFile(file)
downloadFile(download.packageName, file)
download.downloadedFiles++
}
} catch (exception: Exception) {
@@ -298,8 +298,8 @@ class DownloadWorker @AssistedInject constructor(
* @param gFile A [GPlayFile] to download
* @return A [Boolean] indicating whether the file was downloaded or not.
*/
private suspend fun downloadFile(gFile: GPlayFile): Boolean = withContext(Dispatchers.IO) {
Log.i(TAG, "Downloading ${gFile.name}")
private suspend fun downloadFile(packageName: String, gFile: GPlayFile): Boolean = withContext(Dispatchers.IO) {
Log.i(TAG, "Downloading $packageName @ ${gFile.name}")
val file = PathUtil.getLocalFile(appContext, gFile, download)
// If file exists and has integrity intact, no need to download again