sync(chunk 2c): Keystore credentials, AccountManager, stub sync adapter
The platform half of chunk 2; the account-add UI is 2d, since it is a design task and the piece that needs an on-device review. A stub ContentProvider turned out to be required and was not in the plan: a sync adapter registers against a content authority, and we publish no provider since :provider was deleted. Without one there is nothing for contentAuthority to name, nothing for requestSync to address, and hasAuthorityAccess() makes every ContentResolver sync call a silent no-op at targetSdk 34+. - CredentialStore: Keystore AES/GCM, blob in its own DataStore file. security-crypto is formally deprecated and terminal. Decryption failure means re-authenticate, never a crash — including ProviderException, which is a RuntimeException and escapes the obvious catches. - CalDavAccounts + SyncAuthenticator: no password reaches AccountManager, which stores them as plain TEXT. The authenticator never returns null — a null is the protocol for "answering asynchronously", and nothing here does, so Settings would wait forever. addAccount refuses with a readable message until 2d ships the screen, rather than opening the home screen and hanging. - SyncAdapterService: enqueue and wait on the unique work *name*, not the request id — enqueueUniqueWork is async so the id is unknown when the wait starts, and under KEEP it may never exist at all. Being deduplicated is not a failure. - Account type and authority are per build variant, so debug and release do not fight over ownership. SyncContractTest guards the Kotlin/resValue pair. - The credential blob is the only thing excluded from backup: Keystore keys are non-exportable, so a restored ciphertext can never be decrypted. Known trade-off recorded in network_security_config.xml and SYNC-PLAN.md: the user CA store is trusted for all traffic, which chunk 5's cert4android should replace rather than sit beside. The instrumented tests here compile but have not been run — device work waits for an explicit go-ahead.
This commit is contained in:
@@ -26,7 +26,12 @@ espressoCore = "3.7.0"
|
||||
kotlinxDatetime = "0.7.0"
|
||||
kotlinxCoroutines = "1.10.2"
|
||||
turbine = "1.2.0"
|
||||
hiltNavigationCompose = "1.3.0"
|
||||
# androidx.hilt — one version for the whole group. hilt-work and
|
||||
# hilt-navigation-compose splitting versions inside a group is the kind of thing
|
||||
# that resolves fine and then fails at runtime.
|
||||
androidxHilt = "1.3.0"
|
||||
# WorkManager: sync runs here, triggered *through* the sync-adapter framework.
|
||||
work = "2.11.2"
|
||||
navigationCompose = "2.9.0"
|
||||
lifecycleCompose = "2.10.0"
|
||||
androidxTestRules = "1.7.0"
|
||||
@@ -125,7 +130,10 @@ kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-cor
|
||||
turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" }
|
||||
|
||||
# Hilt navigation-compose (for hiltViewModel() in Composables)
|
||||
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
|
||||
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHilt" }
|
||||
androidx-hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "androidxHilt" }
|
||||
androidx-hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "androidxHilt" }
|
||||
androidx-work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
|
||||
|
||||
# Navigation-compose (the NavHost / back stack)
|
||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
||||
|
||||
Reference in New Issue
Block a user