Skip to content

JavaDataStorage contains a preferences DataStore with no corruption handler #7191

@jmartinesp

Description

@jmartinesp
  • Android Studio version: Android Studio Narwhal | 2025.1.1 Patch 1
  • Firebase Component: Common
  • Component version: 22.0.0

Steps to reproduce:

I don't think this can be easily reproduced, but I was trying to track what could be causing this error on our app (we have tens of reports of this issue per day):

androidx.datastore.preferences.protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero).
    at androidx.datastore.preferences.protobuf.CodedInputStream$StreamDecoder.readTag(SourceFile:26)
    at androidx.compose.ui.text.input.GapBuffer.getFieldNumber(SourceFile:56)
    at androidx.datastore.preferences.protobuf.MessageSchema.mergeFrom(SourceFile:5)
    at androidx.datastore.preferences.PreferencesProto$PreferenceMap.parseFrom(SourceFile:41)
    at androidx.datastore.preferences.core.PreferencesFileSerializer.readFrom(SourceFile:1)
    at androidx.datastore.core.FileReadScope.readData$suspendImpl(SourceFile:98)
    at androidx.datastore.core.DataStoreImpl$data$1$5.invokeSuspend(SourceFile:131)
    at androidx.datastore.core.DataStoreImpl$data$1$5.invoke(SourceFile:105)
    at androidx.datastore.core.FileStorageConnection.readScope(SourceFile:93)
    at androidx.datastore.core.DataStoreImpl.readDataFromFileOrDefault(SourceFile:17)
    at androidx.datastore.core.DataStoreImpl.access$readDataOrHandleCorruption(SourceFile:223)
    at androidx.datastore.core.DataStoreImpl$InitDataStore$doRun$initData$1.invokeSuspend(SourceFile:128)
    at androidx.datastore.core.DataStoreImpl$InitDataStore$doRun$initData$1.invoke(SourceFile:14)
    at androidx.datastore.core.SingleProcessCoordinator.lock(SourceFile:94)
    at androidx.datastore.core.DataStoreImpl.readAndInitOrPropagateAndThrowFailure(SourceFile:96)
    at androidx.datastore.core.DataStoreImpl.access$handleUpdate(SourceFile:155)
    at androidx.datastore.core.DataStoreImpl$writeActor$3.invokeSuspend(SourceFile:33)
    at androidx.datastore.core.DataStoreImpl$writeActor$3.invoke(SourceFile:13)
    at androidx.datastore.core.SimpleActor$offer$2.invokeSuspend(SourceFile:86)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:9)
    at kotlinx.coroutines.DispatchedTask.run(SourceFile:115)
    at com.google.android.gms.tasks.zzc.run(SourceFile:11)
    at kotlinx.coroutines.scheduling.TaskImpl.run(SourceFile:3)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(SourceFile:88)
androidx.datastore.core.CorruptionException: Unable to parse preferences proto.
    at androidx.datastore.preferences.core.PreferencesFileSerializer.readFrom(SourceFile:316)
    at androidx.datastore.core.FileReadScope.readData$suspendImpl(SourceFile:98)
    at androidx.datastore.core.DataStoreImpl$data$1$5.invokeSuspend(SourceFile:131)
    at androidx.datastore.core.DataStoreImpl$data$1$5.invoke(SourceFile:105)
    at androidx.datastore.core.FileStorageConnection.readScope(SourceFile:93)
    at androidx.datastore.core.DataStoreImpl.readDataFromFileOrDefault(SourceFile:17)
    at androidx.datastore.core.DataStoreImpl.access$readDataOrHandleCorruption(SourceFile:223)
    at androidx.datastore.core.DataStoreImpl$InitDataStore$doRun$initData$1.invokeSuspend(SourceFile:128)
    at androidx.datastore.core.DataStoreImpl$InitDataStore$doRun$initData$1.invoke(SourceFile:14)
    at androidx.datastore.core.SingleProcessCoordinator.lock(SourceFile:94)
    at androidx.datastore.core.DataStoreImpl.readAndInitOrPropagateAndThrowFailure(SourceFile:96)
    at androidx.datastore.core.DataStoreImpl.access$handleUpdate(SourceFile:155)
    at androidx.datastore.core.DataStoreImpl$writeActor$3.invokeSuspend(SourceFile:33)
    at androidx.datastore.core.DataStoreImpl$writeActor$3.invoke(SourceFile:13)
    at androidx.datastore.core.SimpleActor$offer$2.invokeSuspend(SourceFile:86)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(SourceFile:9)
    at kotlinx.coroutines.DispatchedTask.run(SourceFile:115)
    at com.google.android.gms.tasks.zzc.run(SourceFile:11)
    at kotlinx.coroutines.scheduling.TaskImpl.run(SourceFile:3)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(SourceFile:88)

Since DefaultHeartBeatController uses the com.google.android.gms.tasks mentioned in the stacktrace above and it underneath uses HeartBeatInfoStorage which in turns contains a JavaDataStorage instance using preferences data stores I think this could be the root cause, since apparently this data store has no protections against data corruption.

As far as I can tell, Firebase is the only library using this com.google.android.gms.tasks dependency in our codebase.

Relevant Code:

You can find the code here.

private val Context.dataStore: DataStore<Preferences> by
preferencesDataStore(
  name = name,
  produceMigrations = { listOf(SharedPreferencesMigration(it, name)) }
)

As you can see the corruption handler is missing. If I'm not mistaken this means apps can crash with a CorruptionException as soon as any component using this data store are used (such as com.google.firebase.heartbeatinfo.HeartBeatInfoStorage).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions