Skip to content

Conversation

jrodiz
Copy link

@jrodiz jrodiz commented Oct 15, 2025

The app is attempting to create the firebaseSessions directory inside the app's private files directory, and the operating system is denying this request.

Android has security measures to protect app data when the device is locked. If the device has just been rebooted and the user has not unlocked it for the first time, the app's private storage is encrypted and completely inaccessible. This is known as Credential Encrypted (CE) storage.

The Problem: The code is likely running before the user has unlocked their device. This can happen if a background job, a broadcast receiver, or a foreground service starts immediately on boot. When FirebaseSessionsComponent is initialized under these conditions, it tries to create the DataStore file in an encrypted, inaccessible location. The file system denies the request, resulting in AccessDeniedException.

The Solution: Use Device Encrypted (DE) storage for data that must be accessed before the first user unlock. The androidx.datastore library has direct support for this. Instead of appContext.dataStoreFile(...), We need to use a context that is aware of Device Encrypted storage.

By using createDeviceProtectedStorageContext(), I am explicitly telling the system to place this file in a location that is available even when the device is locked. This is the standard and recommended way to handle file access required before the user unlocks their phone.

Copy link
Contributor

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

@eBlender
Copy link

@mrober could you PTAL?

@visumickey visumickey requested a review from mrober October 15, 2025 18:54
appContext
}
return deContext.dataStoreFile(fileName).also {
prepDataStoreFile(it)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The prepDataStoreFile thing didn't work, we can just remove this

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to keep it, since it contains validations to check if the parent path is a directory or not and also delete it if it is not a directory


private fun produceFile(appContext: Context, fileName: String): File {
val deContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
appContext.createDeviceProtectedStorageContext()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any consequences to using the DE storage permanently? After the device is unlocked, should there be some flow to move to CE storage?

Copy link
Author

@jrodiz jrodiz Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any consequences to using the DE storage permanently?
Some research:

Reduced Security | This is the most critical consequence. Data in DE storage is encrypted, but the key is tied to the device hardware, not the user's PIN, password, or pattern. If a malicious actor gains physical access to the device and can bypass the lock screen through a software vulnerability (or has advanced forensic tools), they may be able to extract the contents of DE storage. In contrast, Credential Encrypted (CE) storage would remain safely encrypted.

No Real Performance Benefit | After the device is unlocked, there is no performance advantage to using DE storage over CE storage. Both are simply mounted file systems. The only benefit of DE storage is its availability before the first unlock.

Copy link
Author

@jrodiz jrodiz Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the device is unlocked, should there be some flow to move to CE storage?

We can use a "data handoff" strategy:

  1. On Boot (Before Unlock): Use createDeviceProtectedStorageContext() to write essential, non-sensitive data required for immediate operation.

  2. After User Unlocks: The Android system sends a broadcast action, ACTION_USER_UNLOCKED.

  3. Once the SDK receives the ACTION_USER_UNLOCKED broadcast
    • Read the temporary data from DE storage.
    • Migrate or merge this data into your main data store located in the now-accessible CE storage.
    • FIND A WAY TO NOTIFY DATASTORE TO USE THE NEW LOCATION
    • Delete the temporary data from DE storage to ensure it's not left behind.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Research update:

A DataStore instance is fundamentally tied to the file it was created with. The file path is provided once during initialization (via the produceFile lambda in your code) and is considered immutable for the lifetime of that singleton DataStore object.

The "correct approach" might be to use 2 instances of DataStores, one for DE and other for CE, after receiving the ACTION_USER_UNLOCKED broadcast

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.

3 participants