From 068e87819cab40e36f187d1fa36bd73f19312ff8 Mon Sep 17 00:00:00 2001 From: JanisPlayer <54918417+JanisPlayer@users.noreply.github.com> Date: Thu, 5 Nov 2026 13:51:13 +0100 Subject: [PATCH] fix(autoupload): throttle rapid worker restarts, decouple charging constraint, and delay foreground service --- .../client/jobs/BackgroundJobManagerImpl.kt | 2 +- .../jobs/autoUpload/AutoUploadWorker.kt | 24 +++++++++++++++++-- .../network/SupportedNetworkTransports.kt | 3 +-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index 54f3852d8763..3fdd2b277034 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -537,7 +537,7 @@ internal class BackgroundJobManagerImpl( .putLong(AutoUploadWorker.SYNCED_FOLDER_ID, syncedFolderID) .build() - val constraints = SupportedNetworkTransports.getConstraints(requiresCharging = syncedFolder.isChargingOnly) + val constraints = SupportedNetworkTransports.getConstraints() val requestBuilder = oneTimeRequestBuilder( jobClass = AutoUploadWorker::class, diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index b2a70f7ed622..a4917c2bfdd2 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -54,6 +54,7 @@ import kotlinx.coroutines.ensureActive import kotlinx.coroutines.isActive import kotlinx.coroutines.withContext import java.io.File +import java.util.concurrent.ConcurrentHashMap import kotlin.coroutines.cancellation.CancellationException import kotlin.time.Duration.Companion.milliseconds @@ -77,6 +78,8 @@ class AutoUploadWorker( const val OVERRIDE_POWER_SAVING = "overridePowerSaving" const val SYNCED_FOLDER_ID = "syncedFolderId" const val NOTIFICATION_ID = 266 + private const val MIN_EXECUTION_INTERVAL_MS = 10_000L + private val lastExecutionTimestampByFolder = ConcurrentHashMap() } private val syncFolderHelper = SyncFolderHelper(context) @@ -90,10 +93,21 @@ class AutoUploadWorker( get() = inputData.getBoolean(OVERRIDE_POWER_SAVING, false) override suspend fun doWork(): Result { + val syncFolderId = inputData.getLong(SYNCED_FOLDER_ID, -1) + val now = System.currentTimeMillis() + val lastExecution = lastExecutionTimestampByFolder[syncFolderId] ?: 0L + + if (now - lastExecution < MIN_EXECUTION_INTERVAL_MS && !overridePowerSaving) { + Log_OC.w( + TAG, + "Throttling: AutoUploadWorker executed ${now - lastExecution}ms ago for folder $syncFolderId. Postponing." + ) + return Result.success() + } + lastExecutionTimestampByFolder[syncFolderId] = now + return try { - trySetForeground() - val syncFolderId = inputData.getLong(SYNCED_FOLDER_ID, -1) syncedFolder = syncedFolderProvider.getSyncedFolderByID(syncFolderId) ?.takeIf { it.isEnabled } ?: return Result.failure() @@ -115,6 +129,7 @@ class AutoUploadWorker( return Result.success() } + trySetForeground() uploadFiles(syncedFolder) // only update last scan time after uploading files @@ -208,6 +223,11 @@ class AutoUploadWorker( return true } + if (syncedFolder.isChargingOnly && !overridePowerSaving && !powerManagementService.battery.isCharging) { + Log_OC.d(TAG, "⚠️ Skipping: charging required, but device is not connected to power") + return true + } + return false } diff --git a/app/src/main/java/com/nextcloud/client/network/SupportedNetworkTransports.kt b/app/src/main/java/com/nextcloud/client/network/SupportedNetworkTransports.kt index 72db24a15702..8e0ee2d89551 100644 --- a/app/src/main/java/com/nextcloud/client/network/SupportedNetworkTransports.kt +++ b/app/src/main/java/com/nextcloud/client/network/SupportedNetworkTransports.kt @@ -27,7 +27,7 @@ object SupportedNetworkTransports { } } - fun getConstraints(requiresCharging: Boolean = false): Constraints { + fun getConstraints(): Constraints { val networkRequest = NetworkRequest.Builder() .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) @@ -38,7 +38,6 @@ object SupportedNetworkTransports { return Constraints.Builder() .setRequiredNetworkRequest(networkRequest, NetworkType.CONNECTED) - .setRequiresCharging(requiresCharging) .build() }