From b00cead87837ae2cb62791a92350eea9f82b689c Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 16 May 2022 16:25:04 +0200 Subject: [PATCH 1/9] Setup sharedPreferences to store number of transfer trial by file. - Add static value for sharedPreferences name in SynchronizationService - Add statuc value for trial limit in SynchronizationService - Rename "queueOperation" into "queueSyncRequest" and add a checking for already failed transfer count - Rename "queueOperations" into "queueSyncRequests" and add a checking for already failed transfer count - Update SynchronizationService.onRemoteOperationFinish() to call new method to update failure counter for the given file - Add 'getRequest()' getter in SyncWrapper - Update ObserverService & FileEventListener to replace queueOperation & queueOperations old name by new one - moved new sharedPreference name from SynchronizationService to AppConstants --- .../FileObservers/FileEventListener.java | 2 +- .../e/drive/models/SyncWrapper.java | 4 ++ .../e/drive/services/ObserverService.java | 2 +- .../services/SynchronizationService.java | 51 +++++++++++++++++-- .../e/drive/utils/AppConstants.java | 1 + 5 files changed, 53 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java index a4a0b634..33282233 100644 --- a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java +++ b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java @@ -64,7 +64,7 @@ public class FileEventListener { private void sendSyncRequestToSynchronizationService(SyncRequest request) { Log.d(TAG, "Sending a SyncRequest for " + request.getSyncedFileState().getName()); if (serviceConnection.isBoundToSynchronizationService()) { - serviceConnection.getSynchronizationService().queueOperation(request); + serviceConnection.getSynchronizationService().queueSyncRequest(request); serviceConnection.getSynchronizationService().startSynchronization(); }else{ Log.w(TAG, "Impossible to send SyncRequest. FileEventListener is not bound to SynchronizationService"); diff --git a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java index fdf119d9..316275d8 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java +++ b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java @@ -82,4 +82,8 @@ public class SyncWrapper { } return super.equals(obj); } + + public SyncRequest getRequest() { + return request; + } } diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index ae6ce8f3..8fa2baa6 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -323,7 +323,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene private void passSyncRequestsToSynchronizationService() { if (synchronizationServiceConnection.isBoundToSynchronizationService()) { - synchronizationServiceConnection.getSynchronizationService().queueOperations(syncRequests.values()); + synchronizationServiceConnection.getSynchronizationService().queueSyncRequests(syncRequests.values()); synchronizationServiceConnection.getSynchronizationService().startSynchronization(); } else { Log.e(TAG, "ERROR: impossible to bind ObserverService to SynchronizationService"); diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index 061672de..00a4dbab 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -7,6 +7,8 @@ */ package foundation.e.drive.services; +import static foundation.e.drive.utils.AppConstants.FAILED_TRANSFER_PREF; + import android.accounts.Account; import android.accounts.AccountManager; import android.app.Service; @@ -50,7 +52,7 @@ import foundation.e.drive.utils.DavClientProvider; public class SynchronizationService extends Service implements OnRemoteOperationListener { private final static String TAG = SynchronizationService.class.getSimpleName(); private final SynchronizationBinder binder = new SynchronizationBinder(); - + private final static int FAILURE_LIMIT = 3; private ConcurrentLinkedDeque syncRequestQueue; private ConcurrentHashMap startedSync; //Integer is thread index (1 to workerAmount) @@ -64,7 +66,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation public int onStartCommand(Intent intent, int flags, int startId) { Log.d(TAG, "onStartCommand()"); - SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); final String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); final String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); @@ -95,20 +97,44 @@ public class SynchronizationService extends Service implements OnRemoteOperation Log.w(TAG, "System is low on memory. Service might get killed."); } - public void queueOperation(SyncRequest request){ + + /** + * Add a SyncRequest into waiting queue if it matches some conditions: + * - an equivalent sync Request (same file & same operation type) isn't already running + * - failure limit isn't reach for this file + * + * It also remove already existing request for the same file from the waiting queue + * @param request request to add to waiting queue + */ + public void queueSyncRequest(SyncRequest request){ for (SyncWrapper syncWrapper : startedSync.values()) { if (syncWrapper.equals(request)) { return; } } + final SharedPreferences prefs = getSharedPreferences(FAILED_TRANSFER_PREF, Context.MODE_PRIVATE); + if (prefs.getInt(request.getSyncedFileState().getLocalPath(), 0) >= FAILURE_LIMIT) { + return; + } + syncRequestQueue.remove(request); syncRequestQueue.add(request); } - public void queueOperations(Collection requests){ + /** + * Add a collection of SyncRequest into waiting queue if they met some conditions: + * - an equivalent sync Request (same file & same operation type) isn't already running + * - failure limit isn't reach for this file + * + * It also remove already existing request for the same file from the waiting queue + * @param requests collections of requests to add + */ + public void queueSyncRequests(Collection requests){ for (SyncWrapper syncWrapper : startedSync.values()) { requests.removeIf(syncRequest -> syncWrapper.equals(syncRequest)); } + final SharedPreferences prefs = getSharedPreferences(FAILED_TRANSFER_PREF, Context.MODE_PRIVATE); + requests.removeIf(syncRequest -> prefs.getInt(syncRequest.getSyncedFileState().getLocalPath(), 0) >= FAILURE_LIMIT); syncRequestQueue.removeAll(requests); syncRequestQueue.addAll(requests); @@ -121,7 +147,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation } } - private void startWorker(Integer threadIndex){ if (!canStart(threadIndex)) return; @@ -165,13 +190,21 @@ public class SynchronizationService extends Service implements OnRemoteOperation @Override public void onRemoteOperationFinish(RemoteOperation callerOperation, RemoteOperationResult result) { Log.d(TAG, "onRemoteOperationFinish()"); + SyncWrapper callerWrapper = null; for (Map.Entry keyValue : startedSync.entrySet()) { if (keyValue.getValue().getRemoteOperation().equals(callerOperation)) { keyValue.getValue().setRunning(false); + callerWrapper = keyValue.getValue(); startWorker(keyValue.getKey()); + break; } } + if (!result.isSuccess() && callerWrapper != null) { + updateFailureCounter(callerWrapper.getRequest()); + } + + if (callerOperation instanceof RemoveFileOperation){ if ( result.isSuccess() ) { DbHelper.manageSyncedFileStateDB( ( ( RemoveFileOperation ) callerOperation ).getSyncedFileState(), @@ -259,6 +292,14 @@ public class SynchronizationService extends Service implements OnRemoteOperation } } + + private void updateFailureCounter(SyncRequest request) { + final SharedPreferences prefs = getSharedPreferences(FAILED_TRANSFER_PREF, Context.MODE_PRIVATE); + final String failure_key = request.getSyncedFileState().getLocalPath(); + final int previous_failure_count = prefs.getInt(failure_key, 0); + prefs.edit().putInt(failure_key, previous_failure_count+1); + } + public class SynchronizationBinder extends Binder{ public SynchronizationService getService(){ return SynchronizationService.this; diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index 8f036bab..5be1c6b9 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -38,6 +38,7 @@ public abstract class AppConstants { public static final String ACCOUNT_DATA_ALIAS_KEY = "alias"; public static final String ACCOUNT_DATA_EMAIL = "email"; + public final static String FAILED_TRANSFER_PREF = "failed_transfer"; public static final String[] MEDIA_SYNCABLE_CATEGORIES = new String[]{"Images", "Movies", "Music", "Ringtones", "Documents", "Podcasts"}; public static final String[] SETTINGS_SYNCABLE_CATEGORIES = new String[]{"Rom settings"}; -- GitLab From a57b4e6507b410238de7c28efb057ab378f61168 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 16 May 2022 16:43:23 +0200 Subject: [PATCH 2/9] Reset file transfer's failure counter on Application's onCreate() Also fix coding style for brackets and set some variables final in EdriveApplication.java --- .../foundation/e/drive/EdriveApplication.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java index a8f39f23..f4f7024f 100644 --- a/app/src/main/java/foundation/e/drive/EdriveApplication.java +++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java @@ -31,7 +31,6 @@ import foundation.e.drive.utils.CommonUtils; * It is instantiated before any other class. */ public class EdriveApplication extends Application { - private static final String TAG = "EdriveApplication"; private RecursiveFileObserver mFileObserver = null; private FileEventListener fileEventListener; @@ -45,22 +44,24 @@ public class EdriveApplication extends Application { final String pathForObserver = Environment.getExternalStorageDirectory().getAbsolutePath(); mFileObserver = new RecursiveFileObserver(getApplicationContext(), pathForObserver, fileEventListener); - SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + final SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); CommonUtils.createNotificationChannel(getApplicationContext()); if (prefs.getString(AccountManager.KEY_ACCOUNT_NAME, null) != null) { Log.d(TAG, "Account already registered"); startRecursiveFileObserver(); - Intent SynchronizationServiceIntent = new Intent(getApplicationContext(), SynchronizationService.class); + resetTransferTrialCounter(); + + final Intent SynchronizationServiceIntent = new Intent(getApplicationContext(), SynchronizationService.class); startService(SynchronizationServiceIntent); } else { - Account mAccount = CommonUtils.getAccount(getString(R.string.eelo_account_type), AccountManager.get(this)); - if (mAccount == null) {return ;} + final Account mAccount = CommonUtils.getAccount(getString(R.string.eelo_account_type), AccountManager.get(this)); + if (mAccount == null) { return; } - String accountName = mAccount.name; - String accountType = mAccount.type; + final String accountName = mAccount.name; + final String accountType = mAccount.type; prefs.edit().putString(AccountManager.KEY_ACCOUNT_NAME, accountName) .putString(AccountManager.KEY_ACCOUNT_TYPE, accountType) @@ -71,23 +72,33 @@ public class EdriveApplication extends Application { /** * Start Recursive FileObserver if not already watching */ - public void startRecursiveFileObserver(){ + public void startRecursiveFileObserver() { if (!mFileObserver.isWatching()) { fileEventListener.bindToSynchronizationService(); mFileObserver.startWatching(); Log.d(TAG, "Starting RecursiveFileObserver on root folder"); - } - else { + } else { Log.w(TAG, "RecursiveFileObserver (for media's root folder) was already running"); } } - public void stopRecursiveFileObserver(){ + public void stopRecursiveFileObserver() { mFileObserver.stopWatching(); fileEventListener.unbindFromSynchronizationService(); Log.d(TAG, "RecursiveFileObserver on root folder stops watching "); } + + /** + * Clear sharedPreference that store failed transfer counter. + * Doing this here, allow to restart it once a day. + */ + private void resetTransferTrialCounter() { + final SharedPreferences prefs = getSharedPreferences(AppConstants.FAILED_TRANSFER_PREF, Context.MODE_PRIVATE); + prefs.edit().clear().commit(); + } + + @Override public void onLowMemory() { super.onLowMemory(); -- GitLab From 150ff3104f866d179155f7df25bb61d3b92821ec Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Mon, 16 May 2022 14:57:26 +0000 Subject: [PATCH 3/9] Apply 1 suggestion(s) to 1 file(s) --- .../foundation/e/drive/services/SynchronizationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index 00a4dbab..4f023e24 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -129,7 +129,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation * It also remove already existing request for the same file from the waiting queue * @param requests collections of requests to add */ - public void queueSyncRequests(Collection requests){ + public void queueSyncRequests(Collection requests) { for (SyncWrapper syncWrapper : startedSync.values()) { requests.removeIf(syncRequest -> syncWrapper.equals(syncRequest)); } -- GitLab From c087b972e6137be3f080b63145ce2759e3222056 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Mon, 16 May 2022 14:58:02 +0000 Subject: [PATCH 4/9] Apply 1 suggestion(s) to 1 file(s) --- .../foundation/e/drive/services/SynchronizationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index 4f023e24..74695264 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -106,7 +106,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation * It also remove already existing request for the same file from the waiting queue * @param request request to add to waiting queue */ - public void queueSyncRequest(SyncRequest request){ + public void queueSyncRequest(SyncRequest request) { for (SyncWrapper syncWrapper : startedSync.values()) { if (syncWrapper.equals(request)) { return; -- GitLab From 3ee65bfbeaae10089fb86617f78185da8a7adede Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 16 May 2022 17:22:39 +0200 Subject: [PATCH 5/9] add missing 'commit()' instruction to save data in sharedPreference --- .../foundation/e/drive/services/SynchronizationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index 00a4dbab..97591565 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -297,7 +297,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation final SharedPreferences prefs = getSharedPreferences(FAILED_TRANSFER_PREF, Context.MODE_PRIVATE); final String failure_key = request.getSyncedFileState().getLocalPath(); final int previous_failure_count = prefs.getInt(failure_key, 0); - prefs.edit().putInt(failure_key, previous_failure_count+1); + prefs.edit().putInt(failure_key, previous_failure_count+1).commit(); } public class SynchronizationBinder extends Binder{ -- GitLab From 7f7db62c7c20ec619e82979a295a308bf2f105d3 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 16 May 2022 18:07:33 +0200 Subject: [PATCH 6/9] make better usage of variable --- .../foundation/e/drive/services/SynchronizationService.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index 80913be0..cf3a1dd1 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -157,8 +157,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation final RemoteOperation operation = syncWrapper.getRemoteOperation(); if (operation != null) { - - if (CommonUtils.isThisSyncAllowed(account, request.getSyncedFileState().isMediaType())) { CommonUtils.createNotificationChannel(this); Log.v(TAG, " starts " + request.getSyncedFileState().getName() @@ -180,7 +178,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation final SyncWrapper syncWrapper = startedSync.get(threadIndex); - if ( (syncWrapper != null && syncWrapper.isRunning()) + if ((syncWrapper != null && syncWrapper.isRunning()) || !CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed)) { return false; } @@ -193,8 +191,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation SyncWrapper callerWrapper = null; for (Map.Entry keyValue : startedSync.entrySet()) { if (keyValue.getValue().getRemoteOperation().equals(callerOperation)) { - keyValue.getValue().setRunning(false); callerWrapper = keyValue.getValue(); + callerWrapper.setRunning(false); startWorker(keyValue.getKey()); break; } -- GitLab From a8e58003c806dcdec2b9bc8093d0936bf7fbb9d4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 16 May 2022 18:53:18 +0200 Subject: [PATCH 7/9] failure counter is now reset when transfer succeed --- .../e/drive/services/SynchronizationService.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index cf3a1dd1..321dcaf4 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -198,8 +198,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation } } - if (!result.isSuccess() && callerWrapper != null) { - updateFailureCounter(callerWrapper.getRequest()); + if (callerWrapper != null) { + updateFailureCounter(callerWrapper.getRequest(), result.isSuccess()); } @@ -291,11 +291,17 @@ public class SynchronizationService extends Service implements OnRemoteOperation } - private void updateFailureCounter(SyncRequest request) { + private void updateFailureCounter(SyncRequest request, boolean success) { final SharedPreferences prefs = getSharedPreferences(FAILED_TRANSFER_PREF, Context.MODE_PRIVATE); final String failure_key = request.getSyncedFileState().getLocalPath(); final int previous_failure_count = prefs.getInt(failure_key, 0); - prefs.edit().putInt(failure_key, previous_failure_count+1).commit(); + final SharedPreferences.Editor pref_editor = prefs.edit(); + if (success) { + pref_editor.remove(failure_key); + } else { + pref_editor.putInt(failure_key, previous_failure_count + 1); + } + pref_editor.commit(); } public class SynchronizationBinder extends Binder{ -- GitLab From e3d085cdfc07f0573873db641d0d5fcb7af9af61 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 16 May 2022 18:57:23 +0200 Subject: [PATCH 8/9] rename EdriveApplication.resetTransferTrialCounter() --- .../main/java/foundation/e/drive/EdriveApplication.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java index f4f7024f..337c9fa1 100644 --- a/app/src/main/java/foundation/e/drive/EdriveApplication.java +++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java @@ -11,12 +11,9 @@ package foundation.e.drive; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Application; -import android.app.NotificationChannel; -import android.app.NotificationManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.os.Build; import android.os.Environment; import android.util.Log; @@ -51,7 +48,7 @@ public class EdriveApplication extends Application { Log.d(TAG, "Account already registered"); startRecursiveFileObserver(); - resetTransferTrialCounter(); + resetTransferFailureCounter(); final Intent SynchronizationServiceIntent = new Intent(getApplicationContext(), SynchronizationService.class); startService(SynchronizationServiceIntent); @@ -93,7 +90,7 @@ public class EdriveApplication extends Application { * Clear sharedPreference that store failed transfer counter. * Doing this here, allow to restart it once a day. */ - private void resetTransferTrialCounter() { + private void resetTransferFailureCounter() { final SharedPreferences prefs = getSharedPreferences(AppConstants.FAILED_TRANSFER_PREF, Context.MODE_PRIVATE); prefs.edit().clear().commit(); } -- GitLab From bf8a7d00f319de85c6fdfca829a51f5da0970c24 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 16 May 2022 19:00:20 +0200 Subject: [PATCH 9/9] remove new sharedPreferences when /e/ account is removed --- app/src/main/java/foundation/e/drive/services/ResetService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/foundation/e/drive/services/ResetService.java b/app/src/main/java/foundation/e/drive/services/ResetService.java index f96250f7..176d232d 100644 --- a/app/src/main/java/foundation/e/drive/services/ResetService.java +++ b/app/src/main/java/foundation/e/drive/services/ResetService.java @@ -92,6 +92,8 @@ public class ResetService extends Service { .remove(AppConstants.KEY_LAST_SYNC_TIME) .apply(); } + + deleteSharedPreferences(AppConstants.FAILED_TRANSFER_PREF); } private void removeCachedFiles() { -- GitLab