Loading app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +17 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,8 @@ class AccountSettings( const val COOKIE_KEY = NCAccountUtils.Constants.KEY_OKHTTP_COOKIES const val COOKIE_SEPARATOR = NCAccountUtils.Constants.OKHTTP_COOKIE_SEPARATOR const val AUTH_EXCEPTION_DETECTED = "auth_exception_detected" /** Static property to indicate whether AccountSettings migration is currently running. * **Access must be `synchronized` with `AccountSettings::class.java`.** */ @Volatile Loading Loading @@ -647,6 +649,21 @@ class AccountSettings( ).isNullOrBlank() } fun noAuthExceptionDetected(): Boolean { return accountManager.getUserData( account, AUTH_EXCEPTION_DETECTED ).isNullOrBlank() } fun updateAuthExceptionDetectedStatus(detected: Boolean) { accountManager.setUserData( account, AUTH_EXCEPTION_DETECTED, if (detected) true.toString() else null ) } fun clearCookie() { accountManager.setUserData(account, COOKIE_KEY, null) accountManager.setUserData( Loading app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncManager.kt +19 −14 Original line number Diff line number Diff line Loading @@ -96,7 +96,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L companion object { const val DEBUG_INFO_MAX_RESOURCE_DUMP_SIZE = 100*FileUtils.ONE_KB.toInt() const val UNAUTHORIZED_NOTIFICATION_TAG = "Unauthorized" const val MAX_MULTIGET_RESOURCES = 10 const val DEFAULT_RETRY_AFTER = 5 Loading Loading @@ -207,16 +207,6 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L ) ) accountSettings.credentials( Credentials( account.name, null, authState, null, clientSecret = clientSecretString ) ) executor.execute { performSync(DEFAULT_RETRY_AFTER, DEFAULT_SECOND_RETRY_AFTER, DEFAULT_MAX_RETRY_TIME) } Loading @@ -233,6 +223,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L * @param maxRetryTime optional param, in seconds. On unhandled exception, max time the method should retry. */ fun performSync(retryAfter: Int, secondRetryAfter: Int, maxRetryTime: Int) { // dismiss previous error notifications notificationManager.cancel(notificationTag, NotificationUtils.NOTIFY_SYNC_ERROR) Loading Loading @@ -354,6 +345,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L else Logger.log.info("Remote collection didn't change, no reason to sync") }, { e, local, remote -> Logger.log.info("SyncManager exception: $e") when (e) { // sync was cancelled or account has been removed: re-throw to SyncAdapterService (now BaseSyncer) is InterruptedException, Loading Loading @@ -385,6 +377,12 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L return@unwrapExceptions } is UnauthorizedException -> { Logger.log.log(Level.WARNING, "Got 401 Unauthorized", e) notifyException(e, local, remote) return@unwrapExceptions } // all others else -> { // sometimes sync is kicked in when no network is not available. Loading Loading @@ -842,6 +840,8 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L private fun notifyException(e: Throwable, local: ResourceType?, remote: HttpUrl?) { val message: String var title: String = localCollection.title var tag: String = notificationTag when (e) { is IOException, Loading @@ -851,11 +851,13 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L syncResult.stats.numIoExceptions++ } is UnauthorizedException -> { title = context.getString(R.string.sync_error_authentification_failed_title, accountSettings.credentials().userName) tag = UNAUTHORIZED_NOTIFICATION_TAG message = context.getString(R.string.sync_error_authentication_failed) syncResult.stats.numAuthExceptions++ // persistent session cookie is present. Probably the session is outDated. no need to show the notification if (accountSettings.containsPersistentCookie()) { if (accountSettings.containsPersistentCookie() && accountSettings.noAuthExceptionDetected()) { Logger.log.log(Level.FINE, "Authorization error. Session outDated") return } Loading Loading @@ -887,6 +889,8 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L } } Logger.log.info("Notification sent") val contentIntent: Intent var viewItemAction: NotificationCompat.Action? = null Loading Loading @@ -920,7 +924,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L val builder = NotificationUtils.newBuilder(context, channel) builder .setSmallIcon(R.drawable.ic_sync_problem_notify) .setContentTitle(localCollection.title) .setContentTitle(title) .setContentText(message) .setStyle(NotificationCompat.BigTextStyle(builder).bigText(message)) .setSubText(mainAccount.name) Loading @@ -930,7 +934,8 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L .setCategory(NotificationCompat.CATEGORY_ERROR) viewItemAction?.let { builder.addAction(it) } notificationManager.notifyIfPossible(notificationTag, NotificationUtils.NOTIFY_SYNC_ERROR, builder.build()) notificationManager.notifyIfPossible(tag, NotificationUtils.NOTIFY_SYNC_ERROR, builder.build()) } private fun buildDebugInfoIntent(e: Throwable, local: ResourceType?, remote: HttpUrl?) = Loading app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncWorker.kt +4 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,7 @@ class SyncWorker @AssistedInject constructor( val isCookiePresent = accountSettings.containsPersistentCookie() try { Logger.log.log(Level.INFO, "Starting onPerformSync for $account") syncThread = Thread.currentThread() syncer.onPerformSync(account, extras.toTypedArray(), authority, provider, result) } catch (e: SecurityException) { Loading @@ -383,6 +384,7 @@ class SyncWorker @AssistedInject constructor( if (isCookiePresent && result.stats.numAuthExceptions > 0) { // probably the session is outDated. retry without the sessionCookie accountSettings.clearCookie() accountSettings.updateAuthExceptionDetectedStatus(detected = true) return Result.retry() } Loading Loading @@ -435,6 +437,8 @@ class SyncWorker @AssistedInject constructor( } } accountSettings.updateAuthExceptionDetectedStatus(detected = false) Logger.log.info("Sync completed successfully") return Result.success() } Loading app/src/main/res/values/strings.xml +2 −1 Original line number Diff line number Diff line Loading @@ -538,7 +538,8 @@ <string name="sync_error_permissions_text">Additional permissions required</string> <string name="sync_error_tasks_too_old">%s too old</string> <string name="sync_error_tasks_required_version">Minimum required version: %1$s</string> <string name="sync_error_authentication_failed">Authentication failed (check login credentials)</string> <string name="sync_error_authentification_failed_title">Your account %1$s</string> <string name="sync_error_authentication_failed">Your login/password is incorrect, please login again.</string> <string name="sync_error_io">Network or I/O error – %s</string> <string name="sync_error_http_dav">HTTP server error – %s</string> <string name="sync_error_local_storage">Local storage error – %s</string> Loading Loading
app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +17 −0 Original line number Diff line number Diff line Loading @@ -124,6 +124,8 @@ class AccountSettings( const val COOKIE_KEY = NCAccountUtils.Constants.KEY_OKHTTP_COOKIES const val COOKIE_SEPARATOR = NCAccountUtils.Constants.OKHTTP_COOKIE_SEPARATOR const val AUTH_EXCEPTION_DETECTED = "auth_exception_detected" /** Static property to indicate whether AccountSettings migration is currently running. * **Access must be `synchronized` with `AccountSettings::class.java`.** */ @Volatile Loading Loading @@ -647,6 +649,21 @@ class AccountSettings( ).isNullOrBlank() } fun noAuthExceptionDetected(): Boolean { return accountManager.getUserData( account, AUTH_EXCEPTION_DETECTED ).isNullOrBlank() } fun updateAuthExceptionDetectedStatus(detected: Boolean) { accountManager.setUserData( account, AUTH_EXCEPTION_DETECTED, if (detected) true.toString() else null ) } fun clearCookie() { accountManager.setUserData(account, COOKIE_KEY, null) accountManager.setUserData( Loading
app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncManager.kt +19 −14 Original line number Diff line number Diff line Loading @@ -96,7 +96,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L companion object { const val DEBUG_INFO_MAX_RESOURCE_DUMP_SIZE = 100*FileUtils.ONE_KB.toInt() const val UNAUTHORIZED_NOTIFICATION_TAG = "Unauthorized" const val MAX_MULTIGET_RESOURCES = 10 const val DEFAULT_RETRY_AFTER = 5 Loading Loading @@ -207,16 +207,6 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L ) ) accountSettings.credentials( Credentials( account.name, null, authState, null, clientSecret = clientSecretString ) ) executor.execute { performSync(DEFAULT_RETRY_AFTER, DEFAULT_SECOND_RETRY_AFTER, DEFAULT_MAX_RETRY_TIME) } Loading @@ -233,6 +223,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L * @param maxRetryTime optional param, in seconds. On unhandled exception, max time the method should retry. */ fun performSync(retryAfter: Int, secondRetryAfter: Int, maxRetryTime: Int) { // dismiss previous error notifications notificationManager.cancel(notificationTag, NotificationUtils.NOTIFY_SYNC_ERROR) Loading Loading @@ -354,6 +345,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L else Logger.log.info("Remote collection didn't change, no reason to sync") }, { e, local, remote -> Logger.log.info("SyncManager exception: $e") when (e) { // sync was cancelled or account has been removed: re-throw to SyncAdapterService (now BaseSyncer) is InterruptedException, Loading Loading @@ -385,6 +377,12 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L return@unwrapExceptions } is UnauthorizedException -> { Logger.log.log(Level.WARNING, "Got 401 Unauthorized", e) notifyException(e, local, remote) return@unwrapExceptions } // all others else -> { // sometimes sync is kicked in when no network is not available. Loading Loading @@ -842,6 +840,8 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L private fun notifyException(e: Throwable, local: ResourceType?, remote: HttpUrl?) { val message: String var title: String = localCollection.title var tag: String = notificationTag when (e) { is IOException, Loading @@ -851,11 +851,13 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L syncResult.stats.numIoExceptions++ } is UnauthorizedException -> { title = context.getString(R.string.sync_error_authentification_failed_title, accountSettings.credentials().userName) tag = UNAUTHORIZED_NOTIFICATION_TAG message = context.getString(R.string.sync_error_authentication_failed) syncResult.stats.numAuthExceptions++ // persistent session cookie is present. Probably the session is outDated. no need to show the notification if (accountSettings.containsPersistentCookie()) { if (accountSettings.containsPersistentCookie() && accountSettings.noAuthExceptionDetected()) { Logger.log.log(Level.FINE, "Authorization error. Session outDated") return } Loading Loading @@ -887,6 +889,8 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L } } Logger.log.info("Notification sent") val contentIntent: Intent var viewItemAction: NotificationCompat.Action? = null Loading Loading @@ -920,7 +924,7 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L val builder = NotificationUtils.newBuilder(context, channel) builder .setSmallIcon(R.drawable.ic_sync_problem_notify) .setContentTitle(localCollection.title) .setContentTitle(title) .setContentText(message) .setStyle(NotificationCompat.BigTextStyle(builder).bigText(message)) .setSubText(mainAccount.name) Loading @@ -930,7 +934,8 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L .setCategory(NotificationCompat.CATEGORY_ERROR) viewItemAction?.let { builder.addAction(it) } notificationManager.notifyIfPossible(notificationTag, NotificationUtils.NOTIFY_SYNC_ERROR, builder.build()) notificationManager.notifyIfPossible(tag, NotificationUtils.NOTIFY_SYNC_ERROR, builder.build()) } private fun buildDebugInfoIntent(e: Throwable, local: ResourceType?, remote: HttpUrl?) = Loading
app/src/main/kotlin/at/bitfire/davdroid/syncadapter/SyncWorker.kt +4 −0 Original line number Diff line number Diff line Loading @@ -363,6 +363,7 @@ class SyncWorker @AssistedInject constructor( val isCookiePresent = accountSettings.containsPersistentCookie() try { Logger.log.log(Level.INFO, "Starting onPerformSync for $account") syncThread = Thread.currentThread() syncer.onPerformSync(account, extras.toTypedArray(), authority, provider, result) } catch (e: SecurityException) { Loading @@ -383,6 +384,7 @@ class SyncWorker @AssistedInject constructor( if (isCookiePresent && result.stats.numAuthExceptions > 0) { // probably the session is outDated. retry without the sessionCookie accountSettings.clearCookie() accountSettings.updateAuthExceptionDetectedStatus(detected = true) return Result.retry() } Loading Loading @@ -435,6 +437,8 @@ class SyncWorker @AssistedInject constructor( } } accountSettings.updateAuthExceptionDetectedStatus(detected = false) Logger.log.info("Sync completed successfully") return Result.success() } Loading
app/src/main/res/values/strings.xml +2 −1 Original line number Diff line number Diff line Loading @@ -538,7 +538,8 @@ <string name="sync_error_permissions_text">Additional permissions required</string> <string name="sync_error_tasks_too_old">%s too old</string> <string name="sync_error_tasks_required_version">Minimum required version: %1$s</string> <string name="sync_error_authentication_failed">Authentication failed (check login credentials)</string> <string name="sync_error_authentification_failed_title">Your account %1$s</string> <string name="sync_error_authentication_failed">Your login/password is incorrect, please login again.</string> <string name="sync_error_io">Network or I/O error – %s</string> <string name="sync_error_http_dav">HTTP server error – %s</string> <string name="sync_error_local_storage">Local storage error – %s</string> Loading