diff options
author | Ricki Hirner <hirner@bitfire.at> | 2022-05-29 15:59:23 +0300 |
---|---|---|
committer | Ricki Hirner <hirner@bitfire.at> | 2022-05-29 16:03:36 +0300 |
commit | 99325e893008f98d3244fbfdee586da40f36412f (patch) | |
tree | a4d9e148798f6b71b2aa2e07b86076d3c6e20632 | |
parent | 0a11a0bcfcd5a3494eb5a1c36524ccce4e1b30df (diff) |
Sync adapter: improve HttpClient and cancellation (#100)
* Sync adapter: share single HttpClient between all SyncManagers (should fix bitfireAT/davx5#99)
* HttpClient: use producer for CertManager
* Call setAccountVisibility only when necessary
* Fix tests
20 files changed, 114 insertions, 60 deletions
diff --git a/app/src/androidTest/java/at/bitfire/davdroid/settings/AccountSettingsTest.kt b/app/src/androidTest/java/at/bitfire/davdroid/settings/AccountSettingsTest.kt index 1d7644c8..612f3899 100644 --- a/app/src/androidTest/java/at/bitfire/davdroid/settings/AccountSettingsTest.kt +++ b/app/src/androidTest/java/at/bitfire/davdroid/settings/AccountSettingsTest.kt @@ -33,20 +33,16 @@ class AccountSettingsTest { @Inject lateinit var settingsManager: SettingsManager - @Before - fun inject() { - hiltRule.inject() - } - val context = InstrumentationRegistry.getInstrumentation().targetContext val account = Account("Test Account", context.getString(R.string.account_type)) val fakeCredentials = Credentials("test", "test") - @Before - fun prepareAccount() { + fun setUp() { + hiltRule.inject() + assertTrue(AccountUtils.createAccount(context, account, AccountSettings.initialUserData(fakeCredentials))) ContentResolver.setIsSyncable(account, CalendarContract.AUTHORITY, 1) ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 0) diff --git a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/AccountUtilsTest.kt b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/AccountUtilsTest.kt index 4e48435a..bda06c6d 100644 --- a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/AccountUtilsTest.kt +++ b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/AccountUtilsTest.kt @@ -9,15 +9,34 @@ import android.accounts.AccountManager import android.os.Bundle import androidx.test.platform.app.InstrumentationRegistry import at.bitfire.davdroid.R +import at.bitfire.davdroid.settings.SettingsManager +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule import org.junit.Test import java.util.concurrent.TimeUnit +import javax.inject.Inject +@HiltAndroidTest class AccountUtilsTest { - val context = InstrumentationRegistry.getInstrumentation().targetContext - val account = Account("Test Account", context.getString(R.string.account_type)) + @get:Rule + val hiltRule = HiltAndroidRule(this) + + @Inject + lateinit var settingsManager: SettingsManager + + @Before + fun inject() { + hiltRule.inject() + } + + + val context by lazy { InstrumentationRegistry.getInstrumentation().targetContext } + val account by lazy { Account("Test Account", context.getString(R.string.account_type)) } @Test fun testCreateAccount() { diff --git a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncAdapterTest.kt b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncAdapterTest.kt index b7288cc6..a4a7f03b 100644 --- a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncAdapterTest.kt +++ b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncAdapterTest.kt @@ -10,6 +10,7 @@ import android.content.Context import android.content.SyncResult import android.os.Bundle import androidx.test.platform.app.InstrumentationRegistry +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.settings.SettingsManager @@ -135,6 +136,7 @@ class SyncAdapterTest { account: Account, extras: Bundle, authority: String, + httpClient: Lazy<HttpClient>, provider: ContentProviderClient, syncResult: SyncResult ) { diff --git a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncManagerTest.kt b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncManagerTest.kt index e5821748..b494110c 100644 --- a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncManagerTest.kt +++ b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/SyncManagerTest.kt @@ -14,6 +14,7 @@ import at.bitfire.dav4jvm.PropStat import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.Response.HrefRelation import at.bitfire.dav4jvm.property.GetETag +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.db.Credentials import at.bitfire.davdroid.db.SyncState @@ -74,6 +75,7 @@ class SyncManagerTest { account, Bundle(), "TestAuthority", + HttpClient.Builder().build(), SyncResult(), collection, server diff --git a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/TestSyncManager.kt b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/TestSyncManager.kt index 5203b1c7..6e664bed 100644 --- a/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/TestSyncManager.kt +++ b/app/src/androidTestStandard/java/at/bitfire/davdroid/syncadapter/TestSyncManager.kt @@ -13,6 +13,7 @@ import at.bitfire.dav4jvm.DavResponseCallback import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.property.GetCTag import at.bitfire.davdroid.DavUtils +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.db.SyncState import at.bitfire.davdroid.resource.LocalResource import at.bitfire.davdroid.settings.AccountSettings @@ -27,10 +28,11 @@ class TestSyncManager( account: Account, extras: Bundle, authority: String, + httpClient: HttpClient, syncResult: SyncResult, localCollection: LocalTestCollection, val mockWebServer: MockWebServer -): SyncManager<LocalTestResource, LocalTestCollection, DavCollection>(context, account, AccountSettings(context, account), extras, authority, syncResult, localCollection) { +): SyncManager<LocalTestResource, LocalTestCollection, DavCollection>(context, account, AccountSettings(context, account), httpClient, extras, authority, syncResult, localCollection) { override fun prepare(): Boolean { collectionURL = mockWebServer.url("/") diff --git a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt index a968961d..9e1863bc 100644 --- a/app/src/main/java/at/bitfire/davdroid/HttpClient.kt +++ b/app/src/main/java/at/bitfire/davdroid/HttpClient.kt @@ -91,7 +91,13 @@ class HttpClient private constructor( val logger: java.util.logging.Logger? = Logger.log, val loggerLevel: HttpLoggingInterceptor.Level = HttpLoggingInterceptor.Level.BODY ) { - private var certManager: CustomCertManager? = null + + fun interface CertManagerProducer { + fun certManager(): CustomCertManager + } + + private var appInForeground = false + private var certManagerProducer: CertManagerProducer? = null private var certificateAlias: String? = null private var offerCompression: Boolean = false @@ -136,9 +142,11 @@ class HttpClient private constructor( Logger.log.log(Level.SEVERE, "Can't set proxy, ignoring", e) } - // TODO don't instantiate CustomCertManager in .Builder (causes service leaks) - customCertManager(CustomCertManager(context, true /*BuildConfig.customCertsUI*/, - !(settings.getBoolean(Settings.DISTRUST_SYSTEM_CERTIFICATES)))) + customCertManager() { + // by default, use a CustomCertManager that respects the "distrust system certificates" setting + val trustSystemCerts = !settings.getBoolean(Settings.DISTRUST_SYSTEM_CERTIFICATES) + CustomCertManager(context, true /*BuildConfig.customCertsUI*/, trustSystemCerts) + } } // use account settings for authentication and cookies @@ -178,11 +186,11 @@ class HttpClient private constructor( return this } - fun customCertManager(manager: CustomCertManager) { - certManager = manager + fun customCertManager(producer: CertManagerProducer) { + certManagerProducer = producer } fun setForeground(foreground: Boolean): Builder { - certManager?.appInForeground = foreground + appInForeground = foreground return this } @@ -208,15 +216,6 @@ class HttpClient private constructor( // offer Brotli and gzip compression orig.addInterceptor(BrotliInterceptor) - val trustManager = certManager ?: run { - val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) - factory.init(null as KeyStore?) - factory.trustManagers.first() as X509TrustManager - } - - val hostnameVerifier = certManager?.hostnameVerifier(OkHostnameVerifier) - ?: OkHostnameVerifier - var keyManager: KeyManager? = null certificateAlias?.let { alias -> val context = requireNotNull(context) @@ -249,15 +248,30 @@ class HttpClient private constructor( orig.protocols(listOf(Protocol.HTTP_1_1)) } - val sslContext = SSLContext.getInstance("TLS") - sslContext.init( + if (certManagerProducer != null || keyManager != null) { + val certManager = certManagerProducer?.certManager() + certManager?.appInForeground = appInForeground + + val trustManager = certManager ?: { // fall back to system default trust manager + val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + factory.init(null as KeyStore?) + factory.trustManagers.first() as X509TrustManager + }() + + val hostnameVerifier = certManager?.hostnameVerifier(OkHostnameVerifier) + ?: OkHostnameVerifier + + val sslContext = SSLContext.getInstance("TLS") + sslContext.init( if (keyManager != null) arrayOf(keyManager) else null, arrayOf(trustManager), null) - orig.sslSocketFactory(sslContext.socketFactory, trustManager) - orig.hostnameVerifier(hostnameVerifier) + orig.sslSocketFactory(sslContext.socketFactory, trustManager) + orig.hostnameVerifier(hostnameVerifier) - return HttpClient(orig.build(), certManager) + return HttpClient(orig.build(), certManager) + } else + return HttpClient(orig.build(), null) } } diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt index bc270e07..275ab782 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/AddressBooksSyncAdapterService.kt @@ -13,6 +13,7 @@ import android.content.pm.PackageManager import android.os.Bundle import android.provider.ContactsContract import androidx.core.content.ContextCompat +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.closeCompat import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection @@ -34,7 +35,7 @@ class AddressBooksSyncAdapterService : SyncAdapterService() { appDatabase: AppDatabase ) : SyncAdapter(context, appDatabase) { - override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, httpClient: Lazy<HttpClient>, provider: ContentProviderClient, syncResult: SyncResult) { try { val accountSettings = AccountSettings(context, account) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt index 2e2d2588..c3840055 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarSyncManager.kt @@ -14,6 +14,7 @@ import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.exception.DavException import at.bitfire.dav4jvm.property.* import at.bitfire.davdroid.DavUtils +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.db.SyncState import at.bitfire.davdroid.log.Logger @@ -46,10 +47,11 @@ class CalendarSyncManager( account: Account, accountSettings: AccountSettings, extras: Bundle, + httpClient: HttpClient, authority: String, syncResult: SyncResult, localCalendar: LocalCalendar -): SyncManager<LocalEvent, LocalCalendar, DavCalendar>(context, account, accountSettings, extras, authority, syncResult, localCalendar) { +): SyncManager<LocalEvent, LocalCalendar, DavCalendar>(context, account, accountSettings, httpClient, extras, authority, syncResult, localCalendar) { override fun prepare(): Boolean { collectionURL = (localCollection.name ?: return false).toHttpUrlOrNull() ?: return false diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt index 0570c10b..83633b5b 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/CalendarsSyncAdapterService.kt @@ -10,6 +10,7 @@ import android.content.Context import android.content.SyncResult import android.os.Bundle import android.provider.CalendarContract +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Service @@ -34,7 +35,7 @@ class CalendarsSyncAdapterService: SyncAdapterService() { appDatabase: AppDatabase ) : SyncAdapter(context, appDatabase) { - override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, httpClient: Lazy<HttpClient>, provider: ContentProviderClient, syncResult: SyncResult) { try { val accountSettings = AccountSettings(context, account) @@ -58,7 +59,7 @@ class CalendarsSyncAdapterService: SyncAdapterService() { .sortedByDescending { priorityCalendars.contains(it.id) } for (calendar in calendars) { Logger.log.info("Synchronizing calendar #${calendar.id}, URL: ${calendar.name}") - CalendarSyncManager(context, account, accountSettings, extras, authority, syncResult, calendar).use { + CalendarSyncManager(context, account, accountSettings, extras, httpClient.value, authority, syncResult, calendar).let { it.performSync() } } diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt index e41f6dbc..83aa7496 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncAdapterService.kt @@ -11,6 +11,7 @@ import android.content.Context import android.content.SyncResult import android.os.Bundle import android.provider.ContactsContract +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.resource.LocalAddressBook @@ -31,7 +32,7 @@ class ContactsSyncAdapterService: SyncAdapterService() { appDatabase: AppDatabase ) : SyncAdapter(context, appDatabase) { - override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, httpClient: Lazy<HttpClient>, provider: ContentProviderClient, syncResult: SyncResult) { try { val addressBook = LocalAddressBook(context, account, provider) val accountSettings = AccountSettings(context, addressBook.mainAccount) @@ -62,7 +63,7 @@ class ContactsSyncAdapterService: SyncAdapterService() { Logger.log.info("Synchronizing address book: ${addressBook.url}") Logger.log.info("Taking settings from: ${addressBook.mainAccount}") - ContactsSyncManager(context, account, accountSettings, extras, authority, syncResult, provider, addressBook).use { + ContactsSyncManager(context, account, accountSettings, httpClient.value, extras, authority, syncResult, provider, addressBook).let { it.performSync() } } catch(e: Exception) { diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt index 3f6e940a..2401a0a0 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/ContactsSyncManager.kt @@ -82,12 +82,13 @@ class ContactsSyncManager( context: Context, account: Account, accountSettings: AccountSettings, + httpClient: HttpClient, extras: Bundle, authority: String, syncResult: SyncResult, val provider: ContentProviderClient, localAddressBook: LocalAddressBook -): SyncManager<LocalAddress, LocalAddressBook, DavAddressBook>(context, account, accountSettings, extras, authority, syncResult, localAddressBook) { +): SyncManager<LocalAddress, LocalAddressBook, DavAddressBook>(context, account, accountSettings, httpClient, extras, authority, syncResult, localAddressBook) { companion object { infix fun <T> Set<T>.disjunct(other: Set<T>) = (this - other) union (other - this) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncAdapterService.kt index 9d8e6526..2653a184 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncAdapterService.kt @@ -12,6 +12,7 @@ import android.content.Context import android.content.SyncResult import android.os.Build import android.os.Bundle +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Service @@ -37,7 +38,7 @@ class JtxSyncAdapterService: SyncAdapterService() { appDatabase: AppDatabase ) : SyncAdapter(context, appDatabase) { - override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, httpClient: Lazy<HttpClient>, provider: ContentProviderClient, syncResult: SyncResult) { try { // check whether jtx Board is new enough @@ -62,7 +63,7 @@ class JtxSyncAdapterService: SyncAdapterService() { val collections = JtxCollection.find(account, provider, context, LocalJtxCollection.Factory, null, null) for (collection in collections) { Logger.log.info("Synchronizing $collection") - JtxSyncManager(context, account, accountSettings, extras, authority, syncResult, collection).use { + JtxSyncManager(context, account, accountSettings, extras, httpClient.value, authority, syncResult, collection).let { it.performSync() } } diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncManager.kt index 638c36f5..c003e659 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/JtxSyncManager.kt @@ -14,6 +14,7 @@ import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.exception.DavException import at.bitfire.dav4jvm.property.* import at.bitfire.davdroid.DavUtils +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.db.SyncState import at.bitfire.davdroid.log.Logger @@ -38,10 +39,11 @@ class JtxSyncManager( account: Account, accountSettings: AccountSettings, extras: Bundle, + httpClient: HttpClient, authority: String, syncResult: SyncResult, localCollection: LocalJtxCollection -): SyncManager<LocalJtxICalObject, LocalJtxCollection, DavCalendar>(context, account, accountSettings, extras, authority, syncResult, localCollection) { +): SyncManager<LocalJtxICalObject, LocalJtxCollection, DavCalendar>(context, account, accountSettings, httpClient, extras, authority, syncResult, localCollection) { override fun prepare(): Boolean { collectionURL = (localCollection.url ?: return false).toHttpUrlOrNull() ?: return false diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt index 7fbb2a3a..969cbd25 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncAdapterService.kt @@ -13,6 +13,7 @@ import android.net.wifi.WifiManager import android.os.Bundle import androidx.core.content.getSystemService import at.bitfire.davdroid.ConcurrentUtils +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.InvalidAccountException import at.bitfire.davdroid.PermissionUtils import at.bitfire.davdroid.db.AppDatabase @@ -103,7 +104,7 @@ abstract class SyncAdapterService: Service() { } - abstract fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) + abstract fun sync(account: Account, extras: Bundle, authority: String, httpClient: Lazy<HttpClient>, 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().joinToString(", ")) @@ -114,11 +115,14 @@ abstract class SyncAdapterService: Service() { // required for ServiceLoader -> ical4j -> ical4android Thread.currentThread().contextClassLoader = context.classLoader + val accountSettings by lazy { AccountSettings(context, account) } + val httpClient = lazy { HttpClient.Builder(context, accountSettings).build() } + try { val runSync = /* always true in open-source edition */ true if (runSync) - sync(account, extras, authority, provider, syncResult) + sync(account, extras, authority, httpClient, provider, syncResult) } catch (e: InvalidAccountException) { Logger.log.log( @@ -126,6 +130,9 @@ abstract class SyncAdapterService: Service() { "Account was removed during synchronization", e ) + } finally { + if (httpClient.isInitialized()) + httpClient.value.close() } }) Logger.log.log(Level.INFO, "Sync for $currentSyncKey finished", syncResult) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt index ac143ab6..443f75ba 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/SyncManager.kt @@ -66,11 +66,12 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L val context: Context, val account: Account, val accountSettings: AccountSettings, + val httpClient: HttpClient, val extras: Bundle, val authority: String, val syncResult: SyncResult, val localCollection: CollectionType -): AutoCloseable { +) { @EntryPoint @InstallIn(SingletonComponent::class) @@ -122,8 +123,6 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L protected val notificationManager = NotificationManagerCompat.from(context) protected val notificationTag = localCollection.tag - protected val httpClient = HttpClient.Builder(context, accountSettings).build() - protected lateinit var collectionURL: HttpUrl protected lateinit var davCollection: RemoteType @@ -132,11 +131,6 @@ abstract class SyncManager<ResourceType: LocalResource<*>, out CollectionType: L val workDispatcher = getWorkDispatcher() - override fun close() { - httpClient.close() - } - - fun performSync() { // dismiss previous error notifications notificationManager.cancel(notificationTag, NotificationUtils.NOTIFY_SYNC_ERROR) diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt index b6e496d5..8b03e803 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncAdapterService.kt @@ -11,6 +11,7 @@ import android.content.Context import android.content.SyncResult import android.os.Build import android.os.Bundle +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.Service @@ -37,14 +38,20 @@ open class TasksSyncAdapterService: SyncAdapterService() { appDatabase: AppDatabase, ) : SyncAdapter(context, appDatabase) { - override fun sync(account: Account, extras: Bundle, authority: String, provider: ContentProviderClient, syncResult: SyncResult) { + override fun sync(account: Account, extras: Bundle, authority: String, httpClient: Lazy<HttpClient>, provider: ContentProviderClient, syncResult: SyncResult) { try { val providerName = TaskProvider.ProviderName.fromAuthority(authority) val taskProvider = TaskProvider.fromProviderClient(context, providerName, provider) // make sure account can be seen by task provider - if (Build.VERSION.SDK_INT >= 26) - AccountManager.get(context).setAccountVisibility(account, providerName.packageName, AccountManager.VISIBILITY_VISIBLE) + if (Build.VERSION.SDK_INT >= 26) { + /* Warning: If setAccountVisibility is called, Android 12 broadcasts the + AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION Intent. This cancels running syncs + and starts them again! So make sure setAccountVisibility is only called when necessary. */ + val am = AccountManager.get(context) + if (am.getAccountVisibility(account, providerName.packageName) != AccountManager.VISIBILITY_VISIBLE) + am.setAccountVisibility(account, providerName.packageName, AccountManager.VISIBILITY_VISIBLE) + } val accountSettings = AccountSettings(context, account) /* don't run sync if @@ -62,7 +69,7 @@ open class TasksSyncAdapterService: SyncAdapterService() { .sortedByDescending { priorityTaskLists.contains(it.id) } for (taskList in taskLists) { Logger.log.info("Synchronizing task list #${taskList.id} [${taskList.syncId}]") - TasksSyncManager(context, account, accountSettings, extras, authority, syncResult, taskList).use { + TasksSyncManager(context, account, accountSettings, httpClient.value, extras, authority, syncResult, taskList).let { it.performSync() } } @@ -114,4 +121,4 @@ open class TasksSyncAdapterService: SyncAdapterService() { } -} +}
\ No newline at end of file diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt index 3987c498..d1dbc363 100644 --- a/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt +++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/TasksSyncManager.kt @@ -14,6 +14,7 @@ import at.bitfire.dav4jvm.Response import at.bitfire.dav4jvm.exception.DavException import at.bitfire.dav4jvm.property.* import at.bitfire.davdroid.DavUtils +import at.bitfire.davdroid.HttpClient import at.bitfire.davdroid.R import at.bitfire.davdroid.db.SyncState import at.bitfire.davdroid.log.Logger @@ -40,11 +41,12 @@ class TasksSyncManager( context: Context, account: Account, accountSettings: AccountSettings, + httpClient: HttpClient, extras: Bundle, authority: String, syncResult: SyncResult, localCollection: LocalTaskList -): SyncManager<LocalTask, LocalTaskList, DavCalendar>(context, account, accountSettings, extras, authority, syncResult, localCollection) { +): SyncManager<LocalTask, LocalTaskList, DavCalendar>(context, account, accountSettings, httpClient, extras, authority, syncResult, localCollection) { override fun prepare(): Boolean { collectionURL = (localCollection.syncId ?: return false).toHttpUrlOrNull() ?: return false diff --git a/app/src/main/res/xml/account_authenticator.xml b/app/src/main/res/xml/account_authenticator.xml index bf6bd348..85eaa203 100644 --- a/app/src/main/res/xml/account_authenticator.xml +++ b/app/src/main/res/xml/account_authenticator.xml @@ -3,4 +3,4 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:smallIcon="@mipmap/ic_launcher" - android:accountPreferences="@xml/sync_prefs" /> + android:accountPreferences="@xml/sync_prefs" />
\ No newline at end of file diff --git a/app/src/main/res/xml/account_authenticator_address_book.xml b/app/src/main/res/xml/account_authenticator_address_book.xml index 5ad7d913..6305f31e 100644 --- a/app/src/main/res/xml/account_authenticator_address_book.xml +++ b/app/src/main/res/xml/account_authenticator_address_book.xml @@ -3,4 +3,4 @@ android:icon="@mipmap/ic_launcher" android:label="@string/account_title_address_book" android:smallIcon="@mipmap/ic_launcher" - android:accountPreferences="@xml/sync_prefs" /> + android:accountPreferences="@xml/sync_prefs" />
\ No newline at end of file diff --git a/app/src/main/res/xml/sync_address_books.xml b/app/src/main/res/xml/sync_address_books.xml index f7284820..61bfbaeb 100644 --- a/app/src/main/res/xml/sync_address_books.xml +++ b/app/src/main/res/xml/sync_address_books.xml @@ -2,4 +2,4 @@ android:accountType="@string/account_type" android:contentAuthority="@string/address_books_authority" android:allowParallelSyncs="true" - android:supportsUploading="false" /> + android:supportsUploading="false" />
\ No newline at end of file |