Skip to content

Fix Direct Boot accessibility startup#2153

Merged
sds100 merged 4 commits into
keymapperorg:developfrom
smh786:codex/direct-boot-accessibility-guard
Jun 9, 2026
Merged

Fix Direct Boot accessibility startup#2153
sds100 merged 4 commits into
keymapperorg:developfrom
smh786:codex/direct-boot-accessibility-guard

Conversation

@smh786

@smh786 smh786 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Fix Direct Boot startup so KeyMapper does not initialize credential-encrypted database-backed components before the user has unlocked the device.

The root cause found during Q25 testing was that LOCKED_BOOT_COMPLETED started the app process and then called BaseKeyMapperApp.onBootUnlocked(), which initialized normal app components while user 0 was still RUNNING_LOCKED. That path could open /data/user/0/.../databases/key_map_database before credential-encrypted storage was available, causing repeated SQLiteCantOpenDatabaseException crashes.

This PR now:

  • avoids app-wide Direct Boot awareness and marks the accessibility service as not Direct Boot aware
  • keeps app/service Hilt dependencies lazy enough that a Direct Boot process start does not eagerly construct CE/database-backed objects
  • delays accessibility controller creation until UserManager.isUserUnlocked
  • stops calling onBootUnlocked() from LOCKED_BOOT_COMPLETED
  • calls onBootUnlocked() from normal BOOT_COMPLETED, which is delivered after unlock

Reproduction

Tested on a Zinwa Q25 running BenOS Uma.1.

Installed KeyMapper 4.2.0-foss over the ROM-provided 3.2.1-foss system app, with KeyMapper Accessibility enabled and no external workaround app installed.

After reboot, before first unlock:

User 0: RUNNING_LOCKED
enabled_accessibility_services includes:
io.github.sds100.keymapper/io.github.sds100.keymapper.system.accessibility.MyAccessibilityService

Logcat shows KeyMapper trying to open CE storage before unlock:

Failed to ensure /data/user/0/io.github.sds100.keymapper/databases: mkdir failed: errno 126 (Required key not available)
SQLiteCantOpenDatabaseException: Cannot open database '/data/user/0/io.github.sds100.keymapper/databases/key_map_database'
Directory /data/user/0/io.github.sds100.keymapper/databases doesn't exist

On this device, the repeated Direct Boot crash/restart state appears to corrupt lockscreen physical-keyboard handling until the first unlock. Examples observed before first unlock:

  • Harpocrat Keyboard typed letters as uppercase.
  • Gboard/other keyboards duplicated characters.
  • adb shell getevent -lt showed only one hardware key down/up event, so duplication occurred above the Linux input layer.

Device verification

Built and tested locally on the same Q25 using a debug build installed as io.github.sds100.keymapper.debug.

Before first unlock, after this fix:

  • Android logs that the KeyMapper accessibility service is ignored as non-encryption-aware
  • KeyMapper process may still start for the Direct Boot receiver
  • BaseKeyMapperApp logs Delay init because locked
  • no KeyMapper key_map_database open attempt or SQLite fatal occurs before unlock

After first unlock:

  • KeyMapper logs onBootUnlocked then Init
  • accessibility service remains enabled
  • no crashed accessibility service is recorded

Why

This keeps KeyMapper available after the first unlock while preventing the broken Direct Boot/locked-user startup window from touching credential-encrypted app storage.

@smh786 smh786 changed the title Delay accessibility service init until user unlock Fix Direct Boot accessibility startup Jun 6, 2026
@smh786

smh786 commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Update: I was able to build and test this on the affected Zinwa Q25.

The original service-only guard was not enough. The confirmed root cause was LOCKED_BOOT_COMPLETED calling onBootUnlocked() while user 0 was still RUNNING_LOCKED, which initialized CE/database-backed components before credential-encrypted storage was available.

With the latest branch:

  • before first unlock: no KeyMapper key_map_database SQLite crash
  • Android ignores the accessibility service as non-encryption-aware while locked
  • KeyMapper logs Delay init because locked
  • after first unlock: KeyMapper logs onBootUnlocked then Init
  • accessibility remains enabled and is not marked crashed

This was tested without the external Q25 boot-fix workaround app installed.

@sds100 sds100 self-requested a review June 9, 2026 07:58
@sds100

sds100 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Do i understand correctly that accessibility services always run doing direct mode, even if you do not specify android:directBootAware="true"? https://developer.android.com/privacy-and-security/direct-boot

Otherwise, why would our accessibility service be running during Direct Boot

@smh786

smh786 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Do i understand correctly that accessibility services always run doing direct mode, even if you do not specify android:directBootAware="true"? https://developer.android.com/privacy-and-security/direct-boot

Otherwise, why would our accessibility service be running during Direct Boot

Not quite. My understanding is that accessibility services do not automatically run during Direct Boot unless the component is Direct-Boot-aware.

The reason this happened here is probably that the app had android:directBootAware="true" at the application level. AOSP describes app-level directBootAware as shorthand for marking all app components as encryption aware, so the accessibility service could effectively become eligible unless it overrides that.

The other confirmed path was LOCKED_BOOT_COMPLETED: on the affected Q25, that broadcast was reaching KeyMapper while user 0 was still locked, and the previous code called onBootUnlocked(). That initialised CE/database-backed pieces too early.

So this PR does two things:

  • removes app-level directBootAware=true
  • explicitly marks the accessibility service directBootAware=false
  • stops treating LOCKED_BOOT_COMPLETED as an unlocked boot

The service guard is mostly defensive; the main root cause I saw in logs was CE database initialisation before first unlock.

sds100
sds100 previously approved these changes Jun 9, 2026
@smh786 smh786 dismissed sds100’s stale review June 9, 2026 08:34

The merge-base changed after approval.

@sds100 sds100 merged commit c63be7a into keymapperorg:develop Jun 9, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants