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

Commit 0a3d1bcc authored by Mohammed Althaf T's avatar Mohammed Althaf T 😊
Browse files

Avoid too many request after auth fail

parent 3b385fa4
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -34,4 +34,6 @@ object Constants {
    const val E_SYNC_URL = "e.email"

    const val MURENA_DAV_URL = "https://murena.io/remote.php/dav"

    const val HTTP_STATUS_CODE_TOO_MANY_REQUESTS = 429
}
+2 −1
Original line number Diff line number Diff line
@@ -13,7 +13,8 @@ data class Credentials(
    val authState: AuthState? = null,
    val certificateAlias: String? = null,
    val serverUri: URI? = null,
    val clientSecret: String? = null
    val clientSecret: String? = null,
    var passwordNeedsUpdate: Boolean = false
) {

    override fun toString(): String {
+10 −1
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ class AccountSettings(
        const val KEY_AUTH_STATE = "auth_state"
        const val KEY_CLIENT_SECRET = "client_secret"
        const val KEY_CERTIFICATE_ALIAS = "certificate_alias"
        const val KEY_PASSWORD_NEEDS_UPDATE = "password_needs_update"

        const val KEY_WIFI_ONLY = "wifi_only"               // sync on WiFi only (default: false)
        const val KEY_WIFI_ONLY_SSIDS = "wifi_only_ssids"   // restrict sync to specific WiFi SSIDs
@@ -156,6 +157,9 @@ class AccountSettings(
                if (credentials.clientSecret != null) {
                    bundle.putString(KEY_CLIENT_SECRET, credentials.clientSecret)
                }

                bundle.putString(KEY_PASSWORD_NEEDS_UPDATE,
                    credentials.passwordNeedsUpdate.toString())
            }

            if (!cookies.isNullOrEmpty()) {
@@ -270,7 +274,9 @@ class AccountSettings(
                accountManager.getUserData(account, KEY_USERNAME),
                accountManager.getPassword(account),
                null,
                accountManager.getUserData(account, KEY_CERTIFICATE_ALIAS)
                accountManager.getUserData(account, KEY_CERTIFICATE_ALIAS),
                passwordNeedsUpdate = accountManager.getUserData(
                    account, KEY_PASSWORD_NEEDS_UPDATE).toBoolean()
            )
        } else {
            Credentials(
@@ -287,6 +293,9 @@ class AccountSettings(
        // Basic/Digest auth
        accountManager.setAndVerifyUserData(account, KEY_USERNAME, credentials.userName)
        accountManager.setPassword(account, credentials.password)
        accountManager.setAndVerifyUserData(account, KEY_PASSWORD_NEEDS_UPDATE,
            credentials.passwordNeedsUpdate.toString()
        )

        // client certificate
        accountManager.setAndVerifyUserData(account, KEY_CERTIFICATE_ALIAS, credentials.certificateAlias)
+17 −4
Original line number Diff line number Diff line
@@ -179,8 +179,14 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
     * Call performSync with default retry values
     */
    fun performSync() {
        val authState = accountSettings.credentials().authState
        val credentials = accountSettings.credentials()
        if (credentials.passwordNeedsUpdate) {
            val exception = UnauthorizedException(context.getString(R.string.sync_error_authentication_failed))
            notifyException(exception, null, null)
            return
        }

        val authState = credentials.authState
        if (authState == null || !authState.needsTokenRefresh) {
            performSync(DEFAULT_RETRY_AFTER, DEFAULT_SECOND_RETRY_AFTER, DEFAULT_MAX_RETRY_TIME)
            return
@@ -226,8 +232,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
     * @param secondRetryAfter optional param, in seconds. Used to calculate fibonnacci sequence for rety on unhandled exception
     * @param maxRetryTime optional param, in seconds. On unhandled exception, max time the method should retry.
     */
    fun performSync(retryAfter: Int, secondRetryAfter: Int, maxRetryTime: Int) {

    private fun performSync(retryAfter: Int, secondRetryAfter: Int, maxRetryTime: Int) {
        // dismiss previous error notifications
        notificationManager.cancel(notificationTag, NotificationUtils.NOTIFY_SYNC_ERROR)

@@ -383,6 +388,11 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L

                is UnauthorizedException -> {
                    Logger.log.log(Level.WARNING, "Got 401 Unauthorized", e)
                    val credentials = accountSettings.credentials()
                    if (credentials.authState == null) {
                        credentials.passwordNeedsUpdate = true
                        accountSettings.credentials(credentials)
                    }
                    notifyException(e, local, remote)
                    return@unwrapExceptions
                }
@@ -411,6 +421,10 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
        if (retryAfter > 0 && secondRetryAfter > 0 && retryAfter <= maxRetryTime) {
            try {
                Logger.log.severe("Faced unhandled exception $e, Will retry sync")
                if (e is HttpException && e.code == Constants.HTTP_STATUS_CODE_TOO_MANY_REQUESTS) {
                    Logger.log.info("HTTP 429 Too Many Requests: Retry sync cancelled")
                    return false
                }
                Logger.log.info("Retry sync after $retryAfter seconds")
                Thread.sleep(retryAfter * 1000L)
                performSync(secondRetryAfter, retryAfter + secondRetryAfter, maxRetryTime)
@@ -944,7 +958,6 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
                .setCategory(NotificationCompat.CATEGORY_ERROR)
        viewItemAction?.let { builder.addAction(it) }


        notificationManager.notifyIfPossible(tag, NotificationUtils.NOTIFY_SYNC_ERROR, builder.build())
    }

+14 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ import android.accounts.Account
import android.accounts.AccountManager
import android.annotation.SuppressLint
import android.app.Application
import android.app.NotificationManager
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -39,6 +40,7 @@ import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.TaskUtils
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.syncadapter.SyncUtils
import at.bitfire.davdroid.syncadapter.SyncWorker
import at.bitfire.davdroid.syncadapter.Syncer
import at.bitfire.davdroid.ui.UiUtils
@@ -133,6 +135,13 @@ class SettingsActivity: AppCompatActivity() {
            return true
        }

        private fun cancelNotifications() {
            context?.getSystemService(NotificationManager::class.java)?.let { notificationManager ->
                notificationManager.cancelAll()
                Logger.log.info("All notifications canceled successfully")
            } ?: Logger.log.warning("Failed to cancel notifications: Context is null")
        }

        private fun initSettings() {
            // preference group: sync
            findPreference<ListPreference>(getString(R.string.settings_sync_interval_contacts_key))!!.let {
@@ -292,7 +301,12 @@ class SettingsActivity: AppCompatActivity() {
                                certificateAlias = credentials.certificateAlias,
                                authState = credentials.authState,
                                clientSecret = credentials.clientSecret,
                                passwordNeedsUpdate = false,
                                serverUri = credentials.serverUri))
                            // Cancel all notifications on password change.
                            cancelNotifications()
                            // Sync accounts once password is changed.
                            context?.let { SyncUtils.syncAllAccounts(it) }
                            false
                        }
                    } else