Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,7 @@ internal interface FirebaseSessionsComponent {
},
scope = CoroutineScope(blockingDispatcher),
produceFile = {
appContext.dataStoreFile("firebaseSessions/sessionConfigsDataStore.data").also {
prepDataStoreFile(it)
}
produceFile(appContext, "firebaseSessions/sessionConfigsDataStore.data")
},
)

Expand All @@ -172,12 +170,22 @@ internal interface FirebaseSessionsComponent {
},
scope = CoroutineScope(blockingDispatcher),
produceFile = {
appContext.dataStoreFile("firebaseSessions/sessionDataStore.data").also {
prepDataStoreFile(it)
}
produceFile(appContext, "firebaseSessions/sessionDataStore.data")
},
)


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

} else {
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 <T> createDataStore(
serializer: Serializer<T>,
corruptionHandler: ReplaceFileCorruptionHandler<T>,
Expand Down