From f09b16df77c909ee02d410d5d32c97a59ad5505a Mon Sep 17 00:00:00 2001 From: daniele-verducci Date: Thu, 29 Oct 2026 18:52:21 +0100 Subject: [PATCH] Avoid concurrent folder sync to avoid duplicates in filelist Signed-off-by: daniele-verducci --- .../operations/RefreshFolderOperation.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index dc27a5f236a3..3a86e8be556b 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -55,6 +55,7 @@ import java.util.List; import java.util.Map; import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -73,6 +74,8 @@ public class RefreshFolderOperation extends RemoteOperation { private static final String TAG = RefreshFolderOperation.class.getSimpleName(); + private static final Map FOLDER_LOCKS = new ConcurrentHashMap<>(); + public static final String EVENT_SINGLE_FOLDER_CONTENTS_SYNCED = RefreshFolderOperation.class.getName() + ".EVENT_SINGLE_FOLDER_CONTENTS_SYNCED"; public static final String EVENT_SINGLE_FOLDER_SHARES_SYNCED = @@ -260,15 +263,24 @@ public List getChildren() { */ @Override protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; - mFailsInKeptInSyncFound = 0; - mConflictsFound = 0; - mForgottenLocalFiles.clear(); - if (mLocalFolder == null) { Log_OC.e(TAG, "Local folder is null, cannot run refresh folder operation"); return new RemoteOperationResult<>(ResultCode.FILE_NOT_FOUND); } + // filelist doesn't have a constraint on the file, so duplicates are possible if two folder sync are launched + // at the same time (may happen on app start, as startMetadataSyncForRoot is called by onCreate and + // syncAndUpdateFolder is called by onResume at short distance) + final String lockKey = user.getAccountName() + mLocalFolder.getRemotePath(); + synchronized (FOLDER_LOCKS.computeIfAbsent(lockKey, key -> new Object())) { + return runLocked(client); + } + } + + private RemoteOperationResult runLocked(OwnCloudClient client) { + RemoteOperationResult result; + mFailsInKeptInSyncFound = 0; + mConflictsFound = 0; + mForgottenLocalFiles.clear(); // Account metadata is refreshed after the folder listing so that it never delays the file list. It is not // needed to render the folder, and blocking on it adds several sequential requests before the first PROPFIND.