Merge branch 'main' into shielded-address-rotation

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Milan Cerovsky 2025-05-19 10:42:53 +02:00
commit 0ea712c9b4
2 changed files with 35 additions and 20 deletions

View File

@ -6,7 +6,8 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2
## [Unreleased] ## [Unreleased]
### Added ### Fixed
- Shared preferences object cached in-memory and locked with semaphore in order to improve stability of security-crypto library
- Shielded address is not rotated by navigating to Receive and Request screens from homepage - Shielded address is not rotated by navigating to Receive and Request screens from homepage
## [2.0.2 (962)] - 2025-05-14 ## [2.0.2 (962)] - 2025-05-14

View File

@ -30,7 +30,7 @@ import java.util.concurrent.Executors
* this instance lives for the lifetime of the application. Constructing multiple instances will * this instance lives for the lifetime of the application. Constructing multiple instances will
* potentially corrupt preference data and will leak resources. * potentially corrupt preference data and will leak resources.
*/ */
class AndroidPreferenceProvider private constructor( class AndroidPreferenceProvider(
private val sharedPreferences: SharedPreferences, private val sharedPreferences: SharedPreferences,
private val dispatcher: CoroutineDispatcher private val dispatcher: CoroutineDispatcher
) : PreferenceProvider { ) : PreferenceProvider {
@ -163,15 +163,26 @@ class AndroidPreferenceProvider private constructor(
} }
} }
companion object { companion object Factory : AndroidPreferenceFactory by AndroidPreferenceFactoryImpl()
suspend fun newStandard( }
context: Context,
filename: String interface AndroidPreferenceFactory {
): PreferenceProvider { suspend fun newStandard(context: Context, filename: String): PreferenceProvider
/*
* Because of this line, we don't want multiple instances of this object created suspend fun newEncrypted(context: Context, filename: String): PreferenceProvider
* because we don't clean up the thread afterwards. }
*/
private class AndroidPreferenceFactoryImpl : AndroidPreferenceFactory {
private val semaphore = Mutex()
private val standardCache = mutableMapOf<String, PreferenceProvider>()
private val encryptedCache = mutableMapOf<String, PreferenceProvider>()
override suspend fun newStandard(context: Context, filename: String) =
getOrCreate(standardCache, filename) {
/*
* Because of this line, we don't want multiple instances of this object created
* because we don't clean up the thread afterwards.
*/
val singleThreadedDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() val singleThreadedDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
val sharedPreferences = val sharedPreferences =
@ -182,14 +193,12 @@ class AndroidPreferenceProvider private constructor(
return AndroidPreferenceProvider(sharedPreferences, singleThreadedDispatcher) return AndroidPreferenceProvider(sharedPreferences, singleThreadedDispatcher)
} }
suspend fun newEncrypted( override suspend fun newEncrypted(context: Context, filename: String) =
context: Context, getOrCreate(encryptedCache, filename) {
filename: String /*
): PreferenceProvider { * Because of this line, we don't want multiple instances of this object created
/* * because we don't clean up the thread afterwards.
* Because of this line, we don't want multiple instances of this object created */
* because we don't clean up the thread afterwards.
*/
val singleThreadedDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() val singleThreadedDispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
val sharedPreferences = val sharedPreferences =
@ -212,5 +221,10 @@ class AndroidPreferenceProvider private constructor(
return AndroidPreferenceProvider(sharedPreferences, singleThreadedDispatcher) return AndroidPreferenceProvider(sharedPreferences, singleThreadedDispatcher)
} }
}
private suspend inline fun getOrCreate(
map: MutableMap<String, PreferenceProvider>,
filename: String,
block: () -> PreferenceProvider
): PreferenceProvider = semaphore.withLock { map.getOrPut(filename, block) }
} }