Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/bitfireAT/davx5-ose.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRicki Hirner <hirner@bitfire.at>2022-09-22 21:53:50 +0300
committerRicki Hirner <hirner@bitfire.at>2022-09-22 21:55:08 +0300
commit8ff90954cd32d2ca62e27523b81bd499a9bcc0a3 (patch)
tree1463f0b7b2e305ed1a612753741e0037a34b3c08
parenta7fcc087bc09ed92e0de5ac377c001b8941e1adb (diff)
Don't run AccountsUpdatedListener while account is being renamed
Don't run AccountsUpdatedListener while account is being renamed (closes bitfireAT/davx5#135)
-rw-r--r--app/src/main/java/at/bitfire/davdroid/syncadapter/AccountsUpdatedListener.kt18
-rw-r--r--app/src/main/java/at/bitfire/davdroid/ui/account/RenameAccountFragment.kt20
2 files changed, 36 insertions, 2 deletions
diff --git a/app/src/main/java/at/bitfire/davdroid/syncadapter/AccountsUpdatedListener.kt b/app/src/main/java/at/bitfire/davdroid/syncadapter/AccountsUpdatedListener.kt
index 68804cb7..03caa3f1 100644
--- a/app/src/main/java/at/bitfire/davdroid/syncadapter/AccountsUpdatedListener.kt
+++ b/app/src/main/java/at/bitfire/davdroid/syncadapter/AccountsUpdatedListener.kt
@@ -23,6 +23,7 @@ import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import java.util.concurrent.Semaphore
import java.util.logging.Level
import javax.inject.Singleton
@@ -44,6 +45,13 @@ class AccountsUpdatedListener private constructor(
fun appDatabase(): AppDatabase
}
+ /**
+ * This mutex (semaphore with 1 permit) will be acquired by [onAccountsUpdated]. So if you
+ * want to postpone [onAccountsUpdated] execution because you're modifying accounts non-transactionally,
+ * you can acquire the mutex by yourself. Don't forget to release it as soon as you're done.
+ */
+ val mutex = Semaphore(1)
+
fun listen() {
val accountManager = AccountManager.get(context)
@@ -56,13 +64,21 @@ class AccountsUpdatedListener private constructor(
*
* 1. Remove related address book accounts.
* 2. Remove related service entries from the [AppDatabase].
+ *
+ * Before the accounts are cleaned up, the [mutex] is acquired.
+ * After the accounts are cleaned up, the [mutex] is released.
*/
@AnyThread
override fun onAccountsUpdated(accounts: Array<out Account>) {
/* onAccountsUpdated may be called from the main thread, but cleanupAccounts
requires disk (database) access. So we launch it in a separate thread. */
CoroutineScope(Dispatchers.Default).launch {
- cleanupAccounts(context, accounts)
+ try {
+ mutex.acquire()
+ cleanupAccounts(context, accounts)
+ } finally {
+ mutex.release()
+ }
}
}
diff --git a/app/src/main/java/at/bitfire/davdroid/ui/account/RenameAccountFragment.kt b/app/src/main/java/at/bitfire/davdroid/ui/account/RenameAccountFragment.kt
index ca8c3985..b45c2c5e 100644
--- a/app/src/main/java/at/bitfire/davdroid/ui/account/RenameAccountFragment.kt
+++ b/app/src/main/java/at/bitfire/davdroid/ui/account/RenameAccountFragment.kt
@@ -36,6 +36,7 @@ import at.bitfire.davdroid.log.Logger
import at.bitfire.davdroid.resource.LocalAddressBook
import at.bitfire.davdroid.resource.LocalTaskList
import at.bitfire.davdroid.settings.AccountSettings
+import at.bitfire.davdroid.syncadapter.AccountsUpdatedListener
import at.bitfire.ical4android.TaskProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
@@ -105,6 +106,7 @@ class RenameAccountFragment: DialogFragment() {
@HiltViewModel
class Model @Inject constructor(
@ApplicationContext val context: Context,
+ val accountsUpdatedListener: AccountsUpdatedListener,
val db: AppDatabase
): ViewModel() {
@@ -129,11 +131,27 @@ class RenameAccountFragment: DialogFragment() {
val accountManager = AccountManager.get(context)
try {
+ /* https://github.com/bitfireAT/davx5/issues/135
+ Take AccountsUpdatedListenerLock so that the AccountsUpdateListener doesn't run while we rename the account
+ because this can cause problems when:
+ 1. The account is renamed.
+ 2. The AccountsUpdateListener is called BEFORE the services table is updated.
+ → AccountsUpdateListener removes the "orphaned" services because they belong to the old account which doesn't exist anymore
+ 3. Now the services would be renamed, but they're not here anymore. */
+ accountsUpdatedListener.mutex.acquire()
+
accountManager.renameAccount(oldAccount, newName, {
if (it.result?.name == newName /* success */)
viewModelScope.launch(Dispatchers.Default + NonCancellable) {
onAccountRenamed(accountManager, oldAccount, newName, syncIntervals)
- }
+
+ // release AccountsUpdatedListener mutex at the end of this async coroutine
+ accountsUpdatedListener.mutex.release()
+ } else
+ // release AccountsUpdatedListener mutex now
+ accountsUpdatedListener.mutex.release()
+
+
}, null)
} catch (e: Exception) {
Logger.log.log(Level.WARNING, "Couldn't rename account", e)