mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-10-04 18:33:49 +00:00
Correctly handle DeadObjectException (bitfireAT/davx5#578, #591)
* Soft fail sync on DeadObjectException so that it is retried without immediate error message * Handle DeadObjectException (→ retries sync); Syncer: generalize all-catch
This commit is contained in:
parent
ad24dd54c7
commit
857309c451
|
@ -58,27 +58,21 @@ class AddressBookSyncer(
|
|||
provider: ContentProviderClient, // for noop address book provider (not for contacts provider)
|
||||
syncResult: SyncResult
|
||||
) {
|
||||
try {
|
||||
if (updateLocalAddressBooks(account, syncResult)) {
|
||||
context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)?.use { contactsProvider ->
|
||||
for (addressBookAccount in LocalAddressBook.findAll(context, null, account).map { it.account }) {
|
||||
Logger.log.info("Synchronizing address book $addressBookAccount")
|
||||
syncAddresBook(
|
||||
addressBookAccount,
|
||||
extras,
|
||||
ContactsContract.AUTHORITY,
|
||||
httpClient,
|
||||
contactsProvider,
|
||||
syncResult
|
||||
)
|
||||
}
|
||||
if (updateLocalAddressBooks(account, syncResult)) {
|
||||
context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY)?.use { contactsProvider ->
|
||||
for (addressBookAccount in LocalAddressBook.findAll(context, null, account).map { it.account }) {
|
||||
Logger.log.info("Synchronizing address book $addressBookAccount")
|
||||
syncAddresBook(
|
||||
addressBookAccount,
|
||||
extras,
|
||||
ContactsContract.AUTHORITY,
|
||||
httpClient,
|
||||
contactsProvider,
|
||||
syncResult
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Logger.log.log(Level.SEVERE, "Couldn't sync address books", e)
|
||||
}
|
||||
|
||||
Logger.log.info("Address book sync complete")
|
||||
}
|
||||
|
||||
private fun updateLocalAddressBooks(account: Account, syncResult: SyncResult): Boolean {
|
||||
|
|
|
@ -33,26 +33,21 @@ class CalendarSyncer(context: Context): Syncer(context) {
|
|||
provider: ContentProviderClient,
|
||||
syncResult: SyncResult
|
||||
) {
|
||||
try {
|
||||
val accountSettings = AccountSettings(context, account)
|
||||
val accountSettings = AccountSettings(context, account)
|
||||
|
||||
if (accountSettings.getEventColors())
|
||||
AndroidCalendar.insertColors(provider, account)
|
||||
else
|
||||
AndroidCalendar.removeColors(provider, account)
|
||||
if (accountSettings.getEventColors())
|
||||
AndroidCalendar.insertColors(provider, account)
|
||||
else
|
||||
AndroidCalendar.removeColors(provider, account)
|
||||
|
||||
updateLocalCalendars(provider, account, accountSettings)
|
||||
updateLocalCalendars(provider, account, accountSettings)
|
||||
|
||||
val calendars = AndroidCalendar
|
||||
.find(account, provider, LocalCalendar.Factory, "${CalendarContract.Calendars.SYNC_EVENTS}!=0", null)
|
||||
for (calendar in calendars) {
|
||||
Logger.log.info("Synchronizing calendar #${calendar.id}, URL: ${calendar.name}")
|
||||
CalendarSyncManager(context, account, accountSettings, extras, httpClient.value, authority, syncResult, calendar).performSync()
|
||||
}
|
||||
} catch(e: Exception) {
|
||||
Logger.log.log(Level.SEVERE, "Couldn't sync calendars", e)
|
||||
val calendars = AndroidCalendar
|
||||
.find(account, provider, LocalCalendar.Factory, "${CalendarContract.Calendars.SYNC_EVENTS}!=0", null)
|
||||
for (calendar in calendars) {
|
||||
Logger.log.info("Synchronizing calendar #${calendar.id}, URL: ${calendar.name}")
|
||||
CalendarSyncManager(context, account, accountSettings, extras, httpClient.value, authority, syncResult, calendar).performSync()
|
||||
}
|
||||
Logger.log.info("Calendar sync complete")
|
||||
}
|
||||
|
||||
private fun updateLocalCalendars(provider: ContentProviderClient, account: Account, settings: AccountSettings) {
|
||||
|
|
|
@ -64,10 +64,7 @@ class JtxSyncer(context: Context): Syncer(context) {
|
|||
|
||||
} catch (e: TaskProvider.ProviderTooOldException) {
|
||||
TaskUtils.notifyProviderTooOld(context, e)
|
||||
} catch (e: Exception) {
|
||||
Logger.log.log(Level.SEVERE, "Couldn't sync jtx collections", e)
|
||||
}
|
||||
Logger.log.info("jtx sync complete")
|
||||
}
|
||||
|
||||
private fun updateLocalCollections(account: Account, client: ContentProviderClient, settings: AccountSettings) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.content.SyncResult
|
||||
import android.net.Uri
|
||||
import android.os.DeadObjectException
|
||||
import android.os.RemoteException
|
||||
import android.provider.CalendarContract
|
||||
import android.provider.ContactsContract
|
||||
|
@ -296,7 +297,12 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L
|
|||
|
||||
}, { e, local, remote ->
|
||||
when (e) {
|
||||
// sync was cancelled or account has been removed: re-throw to SyncAdapterService (now BaseSyncer)
|
||||
// DeadObjectException (may occur when syncing takes too long and process is demoted to cached):
|
||||
// re-throw to base Syncer → will cause soft error and restart the sync process
|
||||
is DeadObjectException ->
|
||||
throw e
|
||||
|
||||
// sync was cancelled or account has been removed: re-throw to BaseSyncer
|
||||
is InterruptedException,
|
||||
is InterruptedIOException,
|
||||
is InvalidAccountException ->
|
||||
|
|
|
@ -8,6 +8,7 @@ import android.accounts.Account
|
|||
import android.content.ContentProviderClient
|
||||
import android.content.Context
|
||||
import android.content.SyncResult
|
||||
import android.os.DeadObjectException
|
||||
import at.bitfire.davdroid.InvalidAccountException
|
||||
import at.bitfire.davdroid.db.AppDatabase
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
|
@ -54,11 +55,11 @@ abstract class Syncer(val context: Context) {
|
|||
|
||||
@EntryPoint
|
||||
@InstallIn(SingletonComponent::class)
|
||||
interface SyncAdapterEntryPoint {
|
||||
interface SyncerEntryPoint {
|
||||
fun appDatabase(): AppDatabase
|
||||
}
|
||||
|
||||
private val syncAdapterEntryPoint = EntryPointAccessors.fromApplication(context, SyncAdapterEntryPoint::class.java)
|
||||
private val syncAdapterEntryPoint = EntryPointAccessors.fromApplication(context, SyncerEntryPoint::class.java)
|
||||
internal val db = syncAdapterEntryPoint.appDatabase()
|
||||
|
||||
|
||||
|
@ -77,11 +78,24 @@ abstract class Syncer(val context: Context) {
|
|||
val httpClient = lazy { HttpClient.Builder(context, accountSettings).build() }
|
||||
|
||||
try {
|
||||
val runSync = true /* ose */
|
||||
val runSync = /* ose */ true
|
||||
|
||||
if (runSync)
|
||||
sync(account, extras, authority, httpClient, provider, syncResult)
|
||||
|
||||
} catch (e: DeadObjectException) {
|
||||
/* May happen when the remote process dies or (since Android 14) when IPC (for instance with the calendar provider)
|
||||
is suddenly forbidden because our sync process was demoted from a "service process" to a "cached process". */
|
||||
Logger.log.log(Level.WARNING, "Received DeadObjectException, treating as soft error", e)
|
||||
syncResult.stats.numIoExceptions++
|
||||
|
||||
} catch (e: InvalidAccountException) {
|
||||
Logger.log.log(Level.WARNING, "Account was removed during synchronization", e)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Logger.log.log(Level.SEVERE, "Couldn't sync $authority", e)
|
||||
syncResult.stats.numParseExceptions++
|
||||
|
||||
} finally {
|
||||
if (httpClient.isInitialized())
|
||||
httpClient.value.close()
|
||||
|
|
|
@ -27,8 +27,8 @@ androidx-work = "2.9.0"
|
|||
appIntro = "7.0.0-beta02"
|
||||
bitfire-cert4android = "f1cc9b9ca3"
|
||||
bitfire-dav4jvm = "b87d772e44"
|
||||
bitfire-ical4android = "f10bd57dac"
|
||||
bitfire-vcard4android = "adf00bd98a"
|
||||
bitfire-ical4android = "a3e886c738"
|
||||
bitfire-vcard4android = "03a37a8284"
|
||||
commons-collections = "4.4"
|
||||
commons-lang = "3.14.0"
|
||||
# don't update until API level 26 (Android 8) is the minimum API [https://github.com/bitfireAT/davx5/issues/130]
|
||||
|
|
Loading…
Reference in a new issue