Conversation
…rtup Co-authored-by: ItosEO <83892149+ItosEO@users.noreply.github.com>
Co-authored-by: ItosEO <83892149+ItosEO@users.noreply.github.com>
|
但是使用标准boot-completed也不行,这个时间点CE已经解密 |
|
@copilot 但是使用标准boot-completed也不行,这个时间点CE已经解密 |
Co-authored-by: ItosEO <83892149+ItosEO@users.noreply.github.com>
你这个判断对,BOOT_COMPLETED 时 CE 通常已可用。根因不只存储位置:
已按你说的场景补修:即使是标准 BOOT_COMPLETED,也可能出现 provider 短暂不可用,之前代码会把这种失败当成空配置并覆盖授权。现在改成 |
There was a problem hiding this comment.
Pull request overview
This PR fixes a reboot-time authorization reset bug on Android 15 (Root startup path), where per-app permission flags reverted to ASK after every reboot. Two root causes are addressed: (1) the config database being stored in Credential Encrypted (CE) storage, making it unavailable during early (direct) boot, and (2) a transient provider load failure being silently misclassified as an empty successful load, causing permission state to be overwritten with defaults.
Changes:
AppDatabase.kt: Database is now opened from Device Protected Storage (DPS) viacreateDeviceProtectedStorageContext(), with a one-time migration from CE → DPS usingmoveDatabaseFrom()wrapped inrunCatchingfor safety.ConfigManager.kt:loadFromManagerWithStatus()now treats anullreply fromcallProvider()as a load failure (status = false), preventing default-config writes when the provider is transiently unavailable during boot.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
manager/src/main/kotlin/roro/stellar/manager/db/AppDatabase.kt |
Opens DB in DPS with CE→DPS migration for direct-boot accessibility |
server/src/main/kotlin/roro/stellar/server/ConfigManager.kt |
Treats null provider reply as load failure to prevent config reset |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| runCatching { deviceContext.moveDatabaseFrom(context.applicationContext, DATABASE_NAME) } | ||
| instance ?: Room.databaseBuilder(deviceContext, AppDatabase::class.java, DATABASE_NAME) | ||
| .fallbackToDestructiveMigration() | ||
| .build().also { instance = it } |
There was a problem hiding this comment.
The moveDatabaseFrom migration call is placed outside the inner instance ?: check but inside the synchronized block. In the double-checked locking pattern, a second thread that was waiting at the synchronized boundary (with instance == null at the outer check) will re-evaluate instance in the inner check and skip building the DB — but it will still execute the moveDatabaseFrom call unnecessarily.
While this is safe in practice because runCatching absorbs the resulting error (the source file no longer exists after the first successful migration), it is cleaner and more efficient to place the migration inside the inner instance ?: check alongside the Room builder call, so that it is only ever attempted once during DB initialization.
| runCatching { deviceContext.moveDatabaseFrom(context.applicationContext, DATABASE_NAME) } | |
| instance ?: Room.databaseBuilder(deviceContext, AppDatabase::class.java, DATABASE_NAME) | |
| .fallbackToDestructiveMigration() | |
| .build().also { instance = it } | |
| instance ?: run { | |
| runCatching { deviceContext.moveDatabaseFrom(context.applicationContext, DATABASE_NAME) } | |
| Room.databaseBuilder(deviceContext, AppDatabase::class.java, DATABASE_NAME) | |
| .fallbackToDestructiveMigration() | |
| .build().also { instance = it } | |
| } |
On some Root startup paths (notably Android 15), authorization state was read as default (
ASK) after reboot, causing repeated permission prompts.This was caused by two related startup risks:
loadFromManagerWithStatus()treatedcallProvider(...) == nullas a successful empty config load, which could trigger writes that overwrite persisted permission flags back to defaults.Changes (minimal and scoped)
AppDatabase.get(...)to open Room DB fromcreateDeviceProtectedStorageContext().moveDatabaseFrom(...)for existing installs.runCatchingto avoid startup crash if source DB is absent/already migrated.reply == nullis treated as load failure (status=false) instead of successful empty config load.Effect on behavior
ASK.Original prompt
🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.