Unify all of the sync adapter exception handling.

The exception handling was duplicated across every sync backend.
It was redundant and made it easy to not handle all of the exceptions correctly
everywhere.
pull/96/head
Tom Hacohen 5 years ago
parent 6250cacd30
commit 2615fbd9ce

@ -40,70 +40,34 @@ class AddressBooksSyncAdapterService : SyncAdapterService() {
private class AddressBooksSyncAdapter(context: Context) : SyncAdapterService.SyncAdapter(context) {
override val syncErrorTitle = R.string.sync_error_contacts
override val notificationManager = SyncNotification(context, "journals-contacts", Constants.NOTIFICATION_CONTACTS_SYNC)
override fun onPerformSyncDo(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
val contactsProvider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)
if (contactsProvider == null) {
Logger.log.severe("Couldn't access contacts provider")
syncResult.databaseError = true
return
}
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
super.onPerformSync(account, extras, authority, provider, syncResult)
val notificationManager = SyncNotification(context, "journals-contacts", Constants.NOTIFICATION_CONTACTS_SYNC)
notificationManager.cancel()
try {
val contactsProvider = context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)
if (contactsProvider == null) {
Logger.log.severe("Couldn't access contacts provider")
syncResult.databaseError = true
return
}
val settings = AccountSettings(context, account)
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return
RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run()
updateLocalAddressBooks(contactsProvider, account)
contactsProvider.release()
val accountManager = AccountManager.get(context)
for (addressBookAccount in accountManager.getAccountsByType(App.addressBookAccountType)) {
Logger.log.log(Level.INFO, "Running sync for address book", addressBookAccount)
val syncExtras = Bundle(extras)
syncExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true)
syncExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true)
ContentResolver.requestSync(addressBookAccount, ContactsContract.AUTHORITY, syncExtras)
}
} catch (e: Exceptions.ServiceUnavailableException) {
syncResult.stats.numIoExceptions++
syncResult.delayUntil = if (e.retryAfter > 0) e.retryAfter else Constants.DEFAULT_RETRY_DELAY
} catch (e: Exceptions.IgnorableHttpException) {
// Ignore
} catch (e: Exception) {
if (e is ContactsStorageException || e is SQLiteException) {
Logger.log.log(Level.SEVERE, "Couldn't prepare local address books", e)
syncResult.databaseError = true
}
val settings = AccountSettings(context, account)
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_contacts, account.name)
RefreshCollections(account, CollectionInfo.Type.ADDRESS_BOOK).run()
notificationManager.setThrowable(e)
updateLocalAddressBooks(contactsProvider, account)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(KEY_ACCOUNT, account)
if (e !is Exceptions.UnauthorizedException) {
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase)
}
contactsProvider.release()
notificationManager.notify(title, context.getString(syncPhase))
} catch (e: OutOfMemoryError) {
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_contacts, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(KEY_ACCOUNT, account)
notificationManager.notify(title, context.getString(syncPhase))
val accountManager = AccountManager.get(context)
for (addressBookAccount in accountManager.getAccountsByType(App.addressBookAccountType)) {
Logger.log.log(Level.INFO, "Running sync for address book", addressBookAccount)
val syncExtras = Bundle(extras)
syncExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true)
syncExtras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true)
ContentResolver.requestSync(addressBookAccount, ContactsContract.AUTHORITY, syncExtras)
}
Logger.log.info("Address book sync complete")

@ -31,68 +31,31 @@ import java.util.*
import java.util.logging.Level
class CalendarsSyncAdapterService : SyncAdapterService() {
override fun syncAdapter(): AbstractThreadedSyncAdapter {
return SyncAdapter(this)
}
private class SyncAdapter(context: Context) : SyncAdapterService.SyncAdapter(context) {
override val syncErrorTitle = R.string.sync_error_calendar
override val notificationManager = SyncNotification(context, "journals-calendar", Constants.NOTIFICATION_CALENDAR_SYNC)
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
super.onPerformSync(account, extras, authority, provider, syncResult)
val notificationManager = SyncNotification(context, "journals-calendar", Constants.NOTIFICATION_CALENDAR_SYNC)
notificationManager.cancel()
try {
val settings = AccountSettings(context, account)
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return
override fun onPerformSyncDo(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
val settings = AccountSettings(context, account)
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return
RefreshCollections(account, CollectionInfo.Type.CALENDAR).run()
RefreshCollections(account, CollectionInfo.Type.CALENDAR).run()
updateLocalCalendars(provider, account, settings)
updateLocalCalendars(provider, account, settings)
val principal = HttpUrl.get(settings.uri!!)!!
val principal = HttpUrl.get(settings.uri!!)!!
for (calendar in AndroidCalendar.find(account, provider, LocalCalendar.Factory, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
Logger.log.info("Synchronizing calendar #" + calendar.id + ", URL: " + calendar.name)
CalendarSyncManager(context, account, settings, extras, authority, syncResult, calendar, principal).use {
it.performSync()
}
for (calendar in AndroidCalendar.find(account, provider, LocalCalendar.Factory, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
Logger.log.info("Synchronizing calendar #" + calendar.id + ", URL: " + calendar.name)
CalendarSyncManager(context, account, settings, extras, authority, syncResult, calendar, principal).use {
it.performSync()
}
} catch (e: Exceptions.ServiceUnavailableException) {
syncResult.stats.numIoExceptions++
syncResult.delayUntil = if (e.retryAfter > 0) e.retryAfter else Constants.DEFAULT_RETRY_DELAY
} catch (e: Exceptions.IgnorableHttpException) {
// Ignore
} catch (e: Exception) {
if (e is CalendarStorageException || e is SQLiteException) {
Logger.log.log(Level.SEVERE, "Couldn't prepare local calendars", e)
syncResult.databaseError = true
}
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_calendar, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(KEY_ACCOUNT, account)
if (e !is Exceptions.UnauthorizedException) {
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase)
}
notificationManager.notify(title, context.getString(syncPhase))
} catch (e: OutOfMemoryError) {
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_calendar, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(KEY_ACCOUNT, account)
notificationManager.notify(title, context.getString(syncPhase))
}
Logger.log.info("Calendar sync complete")

@ -30,63 +30,34 @@ class ContactsSyncAdapterService : SyncAdapterService() {
private class ContactsSyncAdapter(context: Context) : SyncAdapterService.SyncAdapter(context) {
override val syncErrorTitle = R.string.sync_error_contacts
override val notificationManager = SyncNotification(context, "journals-contacts", Constants.NOTIFICATION_CONTACTS_SYNC)
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
super.onPerformSync(account, extras, authority, provider, syncResult)
val notificationManager = SyncNotification(context, "journals-contacts", Constants.NOTIFICATION_CONTACTS_SYNC)
notificationManager.cancel()
override fun onPerformSyncDo(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
val addressBook = LocalAddressBook(context, account, provider)
val settings: AccountSettings
try {
val addressBook = LocalAddressBook(context, account, provider)
val settings: AccountSettings
try {
settings = AccountSettings(context, addressBook.mainAccount)
} catch (e: InvalidAccountException) {
Logger.log.info("Skipping sync due to invalid account.")
Logger.log.info(e.localizedMessage)
return
} catch (e: ContactsStorageException) {
Logger.log.info("Skipping sync due to invalid account.")
Logger.log.info(e.localizedMessage)
return
}
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return
Logger.log.info("Synchronizing address book: " + addressBook.url)
Logger.log.info("Taking settings from: " + addressBook.mainAccount)
settings = AccountSettings(context, addressBook.mainAccount)
} catch (e: InvalidAccountException) {
Logger.log.info("Skipping sync due to invalid account.")
Logger.log.info(e.localizedMessage)
return
} catch (e: ContactsStorageException) {
Logger.log.info("Skipping sync due to invalid account.")
Logger.log.info(e.localizedMessage)
return
}
val principal = HttpUrl.get(settings.uri!!)!!
ContactsSyncManager(context, account, settings, extras, authority, provider, syncResult, addressBook, principal).use {
it.performSync()
}
} catch (e: Exceptions.ServiceUnavailableException) {
syncResult.stats.numIoExceptions++
syncResult.delayUntil = if (e.retryAfter > 0) e.retryAfter else Constants.DEFAULT_RETRY_DELAY
} catch (e: Exceptions.IgnorableHttpException) {
// Ignore
} catch (e: Exception) {
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_contacts, account.name)
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(settings))
return
notificationManager.setThrowable(e)
Logger.log.info("Synchronizing address book: " + addressBook.url)
Logger.log.info("Taking settings from: " + addressBook.mainAccount)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(KEY_ACCOUNT, account)
if (e !is Exceptions.UnauthorizedException) {
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase)
}
notificationManager.notify(title, context.getString(syncPhase))
} catch (e: OutOfMemoryError) {
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_contacts, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(KEY_ACCOUNT, account)
notificationManager.notify(title, context.getString(syncPhase))
val principal = HttpUrl.get(settings.uri!!)!!
ContactsSyncManager(context, account, settings, extras, authority, provider, syncResult, addressBook, principal).use {
it.performSync()
}
Logger.log.info("Contacts sync complete")

@ -12,6 +12,7 @@ import android.accounts.Account
import android.app.PendingIntent
import android.app.Service
import android.content.*
import android.database.sqlite.SQLiteException
import android.net.ConnectivityManager
import android.net.wifi.WifiManager
import android.os.Bundle
@ -19,6 +20,8 @@ import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.util.Pair
import at.bitfire.ical4android.CalendarStorageException
import at.bitfire.vcard4android.ContactsStorageException
import com.etesync.syncadapter.*
import com.etesync.syncadapter.journalmanager.Crypto
import com.etesync.syncadapter.journalmanager.Exceptions
@ -27,6 +30,7 @@ import com.etesync.syncadapter.log.Logger
import com.etesync.syncadapter.model.CollectionInfo
import com.etesync.syncadapter.model.JournalEntity
import com.etesync.syncadapter.model.JournalModel
import com.etesync.syncadapter.ui.DebugInfoActivity
import com.etesync.syncadapter.ui.PermissionsActivity
import com.etesync.syncadapter.utils.NotificationUtils
import okhttp3.HttpUrl
@ -45,12 +49,53 @@ abstract class SyncAdapterService : Service() {
abstract class SyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context, false) {
abstract val syncErrorTitle: Int
abstract val notificationManager: SyncNotification
abstract fun onPerformSyncDo(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult)
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
Logger.log.log(Level.INFO, "$authority sync of $account has been initiated.", extras.keySet().toTypedArray())
// required for dav4android (ServiceLoader)
Thread.currentThread().contextClassLoader = context.classLoader
notificationManager.cancel()
try {
onPerformSyncDo(account, extras, authority, provider, syncResult)
} catch (e: Exceptions.ServiceUnavailableException) {
syncResult.stats.numIoExceptions++
syncResult.delayUntil = if (e.retryAfter > 0) e.retryAfter else Constants.DEFAULT_RETRY_DELAY
} catch (e: Exceptions.IgnorableHttpException) {
// Ignore
} catch (e: Exception) {
if (e is ContactsStorageException || e is CalendarStorageException || e is SQLiteException) {
Logger.log.log(Level.SEVERE, "Couldn't prepare local journals", e)
syncResult.databaseError = true
}
val syncPhase = R.string.sync_phase_journals
val title = context.getString(syncErrorTitle, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(Constants.KEY_ACCOUNT, account)
if (e !is Exceptions.UnauthorizedException) {
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase)
}
notificationManager.notify(title, context.getString(syncPhase))
} catch (e: OutOfMemoryError) {
val syncPhase = R.string.sync_phase_journals
val title = context.getString(syncErrorTitle, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(Constants.KEY_ACCOUNT, account)
notificationManager.notify(title, context.getString(syncPhase))
}
}
override fun onSecurityException(account: Account, extras: Bundle, authority: String, syncResult: SyncResult) {

@ -45,72 +45,36 @@ class TasksSyncAdapterService: SyncAdapterService() {
class TasksSyncAdapter(
context: Context
): SyncAdapter(context) {
override val syncErrorTitle = R.string.sync_error_tasks
override val notificationManager = SyncNotification(context, "journals-tasks", Constants.NOTIFICATION_TASK_SYNC)
override fun onPerformSync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
super.onPerformSync(account, extras, authority, provider, syncResult)
override fun onPerformSyncDo(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) {
val notificationManager = SyncNotification(context, "journals-tasks", Constants.NOTIFICATION_TASK_SYNC)
notificationManager.cancel()
val taskProvider = TaskProvider.fromProviderClient(context, provider)
try {
val taskProvider = TaskProvider.fromProviderClient(context, provider)
// make sure account can be seen by OpenTasks
if (Build.VERSION.SDK_INT >= 26)
AccountManager.get(context).setAccountVisibility(account, taskProvider.name.packageName, AccountManager.VISIBILITY_VISIBLE)
// make sure account can be seen by OpenTasks
if (Build.VERSION.SDK_INT >= 26)
AccountManager.get(context).setAccountVisibility(account, taskProvider.name.packageName, AccountManager.VISIBILITY_VISIBLE)
val accountSettings = AccountSettings(context, account)
/* don't run sync if
- sync conditions (e.g. "sync only in WiFi") are not met AND
- this is is an automatic sync (i.e. manual syncs are run regardless of sync conditions)
*/
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(accountSettings))
return
val accountSettings = AccountSettings(context, account)
/* don't run sync if
- sync conditions (e.g. "sync only in WiFi") are not met AND
- this is is an automatic sync (i.e. manual syncs are run regardless of sync conditions)
*/
if (!extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL) && !checkSyncConditions(accountSettings))
return
RefreshCollections(account, CollectionInfo.Type.TASKS).run()
updateLocalTaskLists(taskProvider, account, accountSettings)
RefreshCollections(account, CollectionInfo.Type.TASKS).run()
val principal = HttpUrl.get(accountSettings.uri!!)!!
updateLocalTaskLists(taskProvider, account, accountSettings)
val principal = HttpUrl.get(accountSettings.uri!!)!!
for (taskList in AndroidTaskList.find(account, taskProvider, LocalTaskList.Factory, "${TaskContract.TaskLists.SYNC_ENABLED}!=0", null)) {
Logger.log.info("Synchronizing task list #${taskList.id} [${taskList.syncId}]")
TasksSyncManager(context, account, accountSettings, extras, authority, syncResult, taskList, principal).use {
it.performSync()
}
}
} catch (e: Exceptions.ServiceUnavailableException) {
syncResult.stats.numIoExceptions++
syncResult.delayUntil = if (e.retryAfter > 0) e.retryAfter else Constants.DEFAULT_RETRY_DELAY
} catch (e: Exceptions.IgnorableHttpException) {
// Ignore
} catch (e: Exception) {
if (e is SQLiteException) {
Logger.log.log(Level.SEVERE, "Couldn't prepare local task list", e)
syncResult.databaseError = true
}
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_tasks, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(Constants.KEY_ACCOUNT, account)
if (e !is Exceptions.UnauthorizedException) {
detailsIntent.putExtra(DebugInfoActivity.KEY_AUTHORITY, authority)
detailsIntent.putExtra(DebugInfoActivity.KEY_PHASE, syncPhase)
for (taskList in AndroidTaskList.find(account, taskProvider, LocalTaskList.Factory, "${TaskContract.TaskLists.SYNC_ENABLED}!=0", null)) {
Logger.log.info("Synchronizing task list #${taskList.id} [${taskList.syncId}]")
TasksSyncManager(context, account, accountSettings, extras, authority, syncResult, taskList, principal).use {
it.performSync()
}
notificationManager.notify(title, context.getString(syncPhase))
} catch (e: OutOfMemoryError) {
val syncPhase = R.string.sync_phase_journals
val title = context.getString(R.string.sync_error_tasks, account.name)
notificationManager.setThrowable(e)
val detailsIntent = notificationManager.detailsIntent
detailsIntent.putExtra(Constants.KEY_ACCOUNT, account)
notificationManager.notify(title, context.getString(syncPhase))
}
Logger.log.info("Task sync complete")

Loading…
Cancel
Save