Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a3f9a101 authored by Guillaume Jacquart's avatar Guillaume Jacquart
Browse files

fix:3040: limit calls to unavailable server.

parent f4560967
Loading
Loading
Loading
Loading
Loading
+46 −2
Original line number Original line Diff line number Diff line
@@ -43,6 +43,8 @@ import com.nextcloud.android.sso.model.SingleSignOnAccount;


import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.NotNull;


import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Calendar;
import java.util.Collections;
import java.util.Collections;
@@ -87,9 +89,14 @@ public class NotesRepository {


    private static final String PREF_KEY_MIGRATION_DONE = "old_note_migration_done";
    private static final String PREF_KEY_MIGRATION_DONE = "old_note_migration_done";
    private static final String PREF_KEY_REMOTE_CONFLICT_RESOLVED = "remote_conflict_resolved_1";
    private static final String PREF_KEY_REMOTE_CONFLICT_RESOLVED = "remote_conflict_resolved_1";
    private static final String PREF_KEY_LAST_SYNC_ATTEMPT = "last_synchronisation_attempt";
    private static final String PREF_KEY_WAIT_BEFORE_NEXT_SYNC = "wait_before_next_synchronisation";


    private static final String TAG = NotesRepository.class.getSimpleName();
    private static final String TAG = NotesRepository.class.getSimpleName();


    private static final long MIN_DELAY_AFTER_FAILED_SYNCHRONIZATION = 5000L;
    private static final long MAX_DELAY_BETWEEN_SYNCHRONIZATIONS = 24 * 60 * 60 * 1000;

    private static NotesRepository instance;
    private static NotesRepository instance;


    private final ApiProvider apiProvider;
    private final ApiProvider apiProvider;
@@ -239,7 +246,7 @@ public class NotesRepository {
            e.printStackTrace();
            e.printStackTrace();
            apiProvider.invalidateAPICache();
            apiProvider.invalidateAPICache();
        }
        }

        serviceWasDown(false);
        db.getAccountDao().deleteAccount(account);
        db.getAccountDao().deleteAccount(account);
        notifyWidgets();
        notifyWidgets();
    }
    }
@@ -777,12 +784,49 @@ public class NotesRepository {
     * This method respects the user preference "Sync on Wi-Fi only".
     * This method respects the user preference "Sync on Wi-Fi only".
     * <p>
     * <p>
     * NoteServerSyncHelper observes changes in the network connection.
     * NoteServerSyncHelper observes changes in the network connection.
     *
     * It also handle exponential back of in case of server unavailability. Return false if the
     * synchronisation should be delayed.
     * The current state can be retrieved with this method.
     * The current state can be retrieved with this method.
     *
     *
     * @return true if sync is possible, otherwise false.
     * @return true if sync is possible, otherwise false.
     */
     */
    public boolean isSyncPossible() {
    public boolean isSyncPossible() {
        return isSyncPossible;
        if (isSyncPossible) {
            final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
            Instant lastSync = Instant.ofEpochMilli(sharedPreferences.getLong(PREF_KEY_LAST_SYNC_ATTEMPT, 0L));
            long waitBeforeNextAttemps = sharedPreferences.getLong(PREF_KEY_WAIT_BEFORE_NEXT_SYNC, 0L);
            boolean cooldownTimeElapsed = lastSync.plus(waitBeforeNextAttemps, ChronoUnit.MILLIS).isBefore(Instant.now());
            if (!cooldownTimeElapsed) {
                Log.i(TAG, "Sync NOT possible, previous ServiceWasDown error was less than " + waitBeforeNextAttemps + " ms ago.");
            }
            return cooldownTimeElapsed;
        } else {
            return false;
        }
    }

    public void serviceWasDown(boolean down) {
        final var sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
        long now = Instant.now().toEpochMilli();
        long waitBeforeNextAttempt = sharedPreferences.getLong(PREF_KEY_WAIT_BEFORE_NEXT_SYNC, 0L);
        if (down) {
            if (waitBeforeNextAttempt < MIN_DELAY_AFTER_FAILED_SYNCHRONIZATION) {
                waitBeforeNextAttempt = MIN_DELAY_AFTER_FAILED_SYNCHRONIZATION;
            } else {
                waitBeforeNextAttempt = 2 * waitBeforeNextAttempt;
                if (waitBeforeNextAttempt > MAX_DELAY_BETWEEN_SYNCHRONIZATIONS) {
                    waitBeforeNextAttempt = MAX_DELAY_BETWEEN_SYNCHRONIZATIONS;
                }
            }
        } else {
            waitBeforeNextAttempt = 0L;
        }

        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putLong(PREF_KEY_LAST_SYNC_ATTEMPT, now);
        editor.putLong(PREF_KEY_WAIT_BEFORE_NEXT_SYNC, waitBeforeNextAttempt);
        editor.commit();
    }
    }


    public boolean isNetworkConnected() {
    public boolean isNetworkConnected() {
+23 −7
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@ import it.niedermann.owncloud.notes.BuildConfig;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.Account;
import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.persistence.entity.Note;
import it.niedermann.owncloud.notes.persistence.sync.NotesAPI;
import it.niedermann.owncloud.notes.persistence.sync.NotesAPI;
import it.niedermann.owncloud.notes.persistence.sync.ServiceDownException;
import it.niedermann.owncloud.notes.shared.model.DBStatus;
import it.niedermann.owncloud.notes.shared.model.DBStatus;
import it.niedermann.owncloud.notes.shared.model.ISyncCallback;
import it.niedermann.owncloud.notes.shared.model.ISyncCallback;
import it.niedermann.owncloud.notes.shared.model.SyncResultStatus;
import it.niedermann.owncloud.notes.shared.model.SyncResultStatus;
@@ -93,6 +94,7 @@ abstract class NotesServerSyncTask extends Thread {
        Log.i(TAG, "STARTING SYNCHRONIZATION");
        Log.i(TAG, "STARTING SYNCHRONIZATION");


        final var status = new SyncResultStatus();
        final var status = new SyncResultStatus();
        try {
            pushLocalChanges(status);
            pushLocalChanges(status);


            if (status.retrySync) {
            if (status.retrySync) {
@@ -104,6 +106,14 @@ abstract class NotesServerSyncTask extends Thread {
                status.pullSuccessful = pullRemoteChanges();
                status.pullSuccessful = pullRemoteChanges();
            }
            }


            repo.serviceWasDown(false);
        } catch (ServiceDownException e) {
            exceptions.add(e);
            status.pushSuccessful = false;
            status.pullSuccessful = false;
            repo.serviceWasDown(true);
        }

        Log.i(TAG, "SYNCHRONIZATION FINISHED");
        Log.i(TAG, "SYNCHRONIZATION FINISHED");


        onPostExecute(status);
        onPostExecute(status);
@@ -152,6 +162,8 @@ abstract class NotesServerSyncTask extends Thread {
                                        Log.e(TAG, "   ...Tried to recreate \"" + note.getTitle() + "\" (#" + note.getId() + ") but the server response was null.");
                                        Log.e(TAG, "   ...Tried to recreate \"" + note.getTitle() + "\" (#" + note.getId() + ") but the server response was null.");
                                        throw new Exception("Server returned null after recreating \"" + note.getTitle() + "\" (#" + note.getId() + ")");
                                        throw new Exception("Server returned null after recreating \"" + note.getTitle() + "\" (#" + note.getId() + ")");
                                    }
                                    }
                                } else if (editResponse.code() == HTTP_NOT_FOUND) {
                                    throw new ServiceDownException("404 on POST a new note");
                                } else {
                                } else {
                                    handleException(createResponse.message());
                                    handleException(createResponse.message());
                                }
                                }
@@ -168,6 +180,8 @@ abstract class NotesServerSyncTask extends Thread {
                                    throw new Exception("Server returned null after creating \"" + note.getTitle() + "\" (#" + note.getId() + ")");
                                    throw new Exception("Server returned null after creating \"" + note.getTitle() + "\" (#" + note.getId() + ")");
                                }
                                }
                                repo.updateRemoteId(note.getId(), remoteNote.getRemoteId());
                                repo.updateRemoteId(note.getId(), remoteNote.getRemoteId());
                            } else if (createResponse.code() == HTTP_NOT_FOUND) {
                                throw new ServiceDownException("404 on POST a new note");
                            } else {
                            } else {
                                handleException(createResponse.message());
                                handleException(createResponse.message());
                            }
                            }
@@ -209,6 +223,8 @@ abstract class NotesServerSyncTask extends Thread {
                if (!retrySync) {
                if (!retrySync) {
                    exceptions.add(e);
                    exceptions.add(e);
                }
                }
            } catch (ServiceDownException e) {
                throw e;
            } catch (Exception e) {
            } catch (Exception e) {
                if (e instanceof TokenMismatchException) {
                if (e instanceof TokenMismatchException) {
                    apiProvider.invalidateAPICache(ssoAccount);
                    apiProvider.invalidateAPICache(ssoAccount);
+7 −0
Original line number Original line Diff line number Diff line
package it.niedermann.owncloud.notes.persistence.sync;

public class ServiceDownException extends RuntimeException {
    public ServiceDownException(String message) {
        super(message);
    }
}