fix(android): keep the app files dir resolvable without a foreground Activity - #1145
Open
DepengWang wants to merge 2 commits into
Open
DepengWang wants to merge 2 commits into
DepengWang wants to merge 2 commits into
Conversation
android_credentials_path() asked the JVM for Context.getFilesDir() on
every call, through Tao's Activity registry. That registry only holds an
Activity while it is in the foreground, so the lookup fails with "Tao
Android context not yet initialized" whenever the Tauri Activity is
backgrounded or finished, which is nearly all the time the IME is in use.
The in-memory credential cache hid this until something forced a reload.
Seen on a OnePlus (CPH2573) with encrypted sync signed in:
- A sync restore started right after a dictation while no Activity was
in the foreground. Four seconds later the vault logged
credential read failed: resolve Android credential directory:
Tao Android context not yet initialized
- The restore stayed pending (pendingRestore in generation.json, the
restore-*.bin left behind), and the sync write gate rejects every write
while a restore is pending.
- From then on the splash marker could not be saved, so the startup
animation replayed on every settings open ("failed to persist splash
marker: recovery_required"), and tapping the mic did nothing: starting
a dictation failed at the credential read, and that warning is
de-duplicated, so nothing was logged or shown.
- The process stayed alive in this state for 25 hours without a crash.
Use android_storage::android_data_dir(), which is resolved once at
startup and cached; it is the same {filesDir}/OpenLess directory. With
this change the pending restore on that phone completed by itself on the
next process start and dictation worked again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous commit made android_credentials_path() use android_storage::android_data_dir(). That cache may have been filled from TAURI_ANDROID_APP_DATA_DIR when the JNI lookup failed, which the credential-keystore contract test rightly rejects: the vault must only ever live under the JNI-resolved Context.getFilesDir(), never under an environment or temporary path. Restore android_credentials_path() and fix the lookup itself instead. app_files_dir() now: - caches the directory once resolved (it is fixed for the life of the process), and - falls back to the Context registered by the Application / runtime service when Tao has no foreground Activity. That Context returns the same getFilesDir() and stays valid during IME use. The vault path is still derived only from getFilesDir(), and every other caller of app_files_dir() gets the same robustness. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On Android, the credential vault resolved its file path with a live JNI lookup that only works while the Tauri Activity is in the foreground. When encrypted sync ran a restore while the IME was in use and no Activity was visible, the restore failed half-way and stayed pending. The sync write gate then rejected every later write, and the IME stopped working with no visible error until the process was restarted.
This PR makes the JNI helper
app_files_dir()cache its result and fall back to the always-registered Context when no Activity is in the foreground.What happened (OnePlus CPH2573, encrypted sync signed in)
Taken from the on-device
openless.logand app state; the process stayed alive for the whole period, no crash.pendingRestorewritten toencrypted-sync/generation.json,restore-*.bincreated).[vault] credential read failed: resolve Android credential directory: Tao Android context not yet initializedSyncWriteGate::begin_mutationreturnsRecoveryRequiredwhilepending_restoreis set.[splash] failed to persist splash marker: recovery_required, so the startup animation replays each time;mictap/heartbeatcounters), thestartcommand reaches Rust, and it fails at the credential read. That warning is de-duplicated against the previous one, so nothing is logged at the tap and the user sees no error.Cause
android_credentials_path()callsjni::app_files_dir()on every use. That helper went only throughtao::platform::android::prelude::main_android_context(), and the comment at the top ofandroid/jni.rsalready notes that Tao only keeps an Activity in that map while it is resumed.Introduced with the Keystore-backed vault in #838; it becomes reachable once something reloads credentials while no Activity is in the foreground, which a sync restore does.
Change
app_files_dir()(inandroid/jni.rs) now:OnceLockonce resolved; it cannot change during the life of the process;nativeRegisterActivityContext(the Application at startup, then the runtime service). That Context returns the samegetFilesDir()and stays valid while only the IME is running.android_credentials_path()is unchanged: the vault path is still derived only from the JNIgetFilesDir(), never fromTAURI_ANDROID_APP_DATA_DIRor a temp directory.Note on the two commits
The first commit pointed the vault at
android_storage::android_data_dir(). That cache can be filled from the environment variable when JNI fails, so it would have weakened the "no environment or temporary fallback" rule thatandroid-credential-keystore-contract.test.mjsenforces; CI caught it. The second commit reverts that and fixes the lookup itself. Happy to squash.Testing
android-credential-keystore-contract.test.mjspasses; all ofscripts/*.test.mjs: 53 of 56 pass, and the 3 failures (android-apk-workflow-contract,ci-cache-usage,ci-changed-areas) fail identically on an untouchedupstream/betacheckout on this machine.cargo check --locked --target aarch64-linux-androidon this branch; hostcargo check --locked;rustfmt --checkon the touched file.pendingRestoregone,restore-*.binremoved, generation 267 -> 270), three dictations ran normally, and later settings opens no longer logged the splash-marker failure.Not tested:
Not addressed here
temporarily_unavailableseveral times during a cold start, which could hit the same window.🤖 Generated with Claude Code