Skip to content

Commit

Permalink
Acquire ContentProviderClient in syncer implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
sunkup committed Jul 15, 2024
1 parent b319b2d commit 32a5e5f
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
package at.bitfire.davdroid.sync

import android.accounts.Account
import android.content.ContentProviderClient
import android.content.Context
import android.content.SyncResult
import androidx.test.platform.app.InstrumentationRegistry
import at.bitfire.davdroid.R
import at.bitfire.davdroid.db.AppDatabase
import at.bitfire.davdroid.network.HttpClient
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import org.junit.Assert.assertEquals
Expand Down Expand Up @@ -67,7 +65,7 @@ class SyncerTest {

val syncCalled = AtomicInteger()

override fun sync(provider: ContentProviderClient) {
override fun sync() {
Thread.sleep(1000)
syncCalled.incrementAndGet()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,28 @@ class AddressBookSyncer @AssistedInject constructor(
const val PREVIOUS_GROUP_METHOD = "previous_group_method"
}

override fun sync(provider: ContentProviderClient) {
override fun sync() {

// use contacts provider for address books (not address book authority)
val contactsAuthority = ContactsContract.AUTHORITY

// acquire ContentProviderClient
val provider = try {
context.contentResolver.acquireContentProviderClient(contactsAuthority)
} catch (e: SecurityException) {
Logger.log.log(Level.WARNING, "Missing permissions for authority $contactsAuthority", e)
null
}

if (provider == null) {
/* Can happen if
- we're not allowed to access the content provider, or
- the content provider is not available at all, for instance because the respective
system app, like "contacts storage" is disabled */
Logger.log.warning("Couldn't connect to content provider of authority $contactsAuthority")
syncResult.stats.numParseExceptions++ // hard sync error
return
}

// 1. find address book collections to be synced
val remoteAddressBooks = mutableMapOf<HttpUrl, Collection>()
Expand Down
22 changes: 21 additions & 1 deletion app/src/main/kotlin/at/bitfire/davdroid/sync/CalendarSyncer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,29 @@ class CalendarSyncer @AssistedInject constructor(
fun create(account: Account, extras: Array<String>, authority: String, syncResult: SyncResult): CalendarSyncer
}

override fun sync(provider: ContentProviderClient) {
override fun sync() {

// 0. preparations

// acquire ContentProviderClient
val provider = try {
context.contentResolver.acquireContentProviderClient(authority)
} catch (e: SecurityException) {
Logger.log.log(Level.WARNING, "Missing permissions for authority $authority", e)
null
}

if (provider == null) {
/* Can happen if
- we're not allowed to access the content provider, or
- the content provider is not available at all, for instance because the respective
system app, like "calendar storage" is disabled */
Logger.log.warning("Couldn't connect to content provider of authority $authority")
syncResult.stats.numParseExceptions++ // hard sync error
return
}

// Update colors
val accountSettings = AccountSettings(context, account)
if (accountSettings.getEventColors())
AndroidCalendar.insertColors(provider, account)
Expand Down
20 changes: 19 additions & 1 deletion app/src/main/kotlin/at/bitfire/davdroid/sync/JtxSyncer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,28 @@ class JtxSyncer @AssistedInject constructor(
fun create(account: Account, extras: Array<String>, authority: String, syncResult: SyncResult): JtxSyncer
}

override fun sync(provider: ContentProviderClient) {
override fun sync() {

// 0. preparations

// acquire ContentProviderClient
val provider = try {
context.contentResolver.acquireContentProviderClient(authority)
} catch (e: SecurityException) {
Logger.log.log(Level.WARNING, "Missing permissions for authority $authority", e)
null
}

if (provider == null) {
/* Can happen if
- we're not allowed to access the content provider, or
- the content provider is not available at all, for instance because the respective
system app, like "calendar storage" is disabled */
Logger.log.warning("Couldn't connect to content provider of authority $authority")
syncResult.stats.numParseExceptions++ // hard sync error
return
}

// check whether jtx Board is new enough
try {
TaskProvider.checkVersion(context, TaskProvider.ProviderName.JtxBoard)
Expand Down
33 changes: 4 additions & 29 deletions app/src/main/kotlin/at/bitfire/davdroid/sync/Syncer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,41 +66,17 @@ abstract class Syncer(
* with remote information. Then syncs the actual entries (events, tasks, contacts, etc) of all
* collections.
*/
abstract fun sync(provider: ContentProviderClient)
abstract fun sync()

fun onPerformSync() {
Logger.log.log(Level.INFO, "$authority sync of $account initiated", extras.joinToString(", "))

// use contacts provider for address books
val contentAuthority =
if (authority == context.getString(R.string.address_books_authority))
ContactsContract.AUTHORITY
else
authority

// acquire ContentProviderClient of authority to be synced
val provider = try {
context.contentResolver.acquireContentProviderClient(contentAuthority)
} catch (e: SecurityException) {
Logger.log.log(Level.WARNING, "Missing permissions for authority $contentAuthority", e)
null
}

if (provider == null) {
/* Can happen if
- we're not allowed to access the content provider, or
- the content provider is not available at all, for instance because the respective
system app, like "contacts storage" is disabled */
Logger.log.warning("Couldn't connect to content provider of authority $contentAuthority")
syncResult.stats.numParseExceptions++ // hard sync error
return
}

// run sync
try {
val runSync = /* ose */ true
if (runSync)
sync(provider)
sync()

} catch (e: DeadObjectException) {
/* May happen when the remote process dies or (since Android 14) when IPC (for instance with the calendar provider)
Expand All @@ -112,16 +88,15 @@ abstract class Syncer(
Logger.log.log(Level.WARNING, "Account was removed during synchronization", e)

} catch (e: Exception) {
Logger.log.log(Level.SEVERE, "Couldn't sync $contentAuthority", e)
Logger.log.log(Level.SEVERE, "Couldn't sync $authority", e)
syncResult.stats.numParseExceptions++ // Hard sync error

} finally {
if (httpClient.isInitialized())
httpClient.value.close()
provider.close()
Logger.log.log(
Level.INFO,
"$contentAuthority sync of $account finished",
"$authority sync of $account finished",
extras.joinToString(", "))
}
}
Expand Down
21 changes: 19 additions & 2 deletions app/src/main/kotlin/at/bitfire/davdroid/sync/TaskSyncer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package at.bitfire.davdroid.sync

import android.accounts.Account
import android.accounts.AccountManager
import android.content.ContentProviderClient
import android.content.Context
import android.content.SyncResult
import android.os.Build
Expand Down Expand Up @@ -46,10 +45,28 @@ class TaskSyncer @AssistedInject constructor(
fun create(account: Account, extras: Array<String>, authority: String, syncResult: SyncResult): TaskSyncer
}

override fun sync(provider: ContentProviderClient) {
override fun sync() {

// 0. preparations

// acquire ContentProviderClient
val provider = try {
context.contentResolver.acquireContentProviderClient(authority)
} catch (e: SecurityException) {
Logger.log.log(Level.WARNING, "Missing permissions for authority $authority", e)
null
}

if (provider == null) {
/* Can happen if
- we're not allowed to access the content provider, or
- the content provider is not available at all, for instance because the respective
system app, like "calendar storage" is disabled */
Logger.log.warning("Couldn't connect to content provider of authority $authority")
syncResult.stats.numParseExceptions++ // hard sync error
return
}

// Acquire task provider
val providerName = TaskProvider.ProviderName.fromAuthority(authority)
val taskProvider = try {
Expand Down

0 comments on commit 32a5e5f

Please sign in to comment.