So I noticed the following crashes at Firebase Crashlytics of my app which happens only for a couple of devices (so rarely) but still it happens:
Fatal Exception: java.lang.VerifyError Verifier rejected class o3.c: java.security.cert.X509Certificate[] o3.c.E(java.nio.ByteBuffer, java.util.HashMap, java.security.cert.CertificateFactory) failed to verify: java.security.cert.X509Certificate[] o3.c.E(java.nio.ByteBuffer, java.util.HashMap, java.security.cert.CertificateFactory): [0x135] register v0 has type Reference: java.nio.ByteBuffer but expected Precise Reference: byte[] (declaration of 'o3.c' appears in /data/app/~~N1_TwW2swHQZzciPS8e0uw==/com.hidden_my_package.myapp-O87GcvydxMif4uP_FrnEXQ==/base.apk) kotlinx.coroutines.flow.FlowKt__ShareKt.asStateFlow (FlowKt__Share.kt:38)
com.hidden_my_package.datastore.di.DatastoreModule_ProvidePreferencesManagerFactory.providePreferencesManager (DatastoreModule_ProvidePreferencesManagerFactory.java:420) com.hidden_my_package.myapp.DaggerMyApplication_HiltComponents_SingletonC$SingletonCImpl$SwitchingProvider.get (DaggerBVRApplication_HiltComponents_SingletonC.java:420)
I can't reproduce the issue myself.
So based on the stack trace I have the following implementation of DataStore to save users' preferences:
class PreferencesManagerImpl(
private val context: Context,
private val dataStore: DataStore<Preferences>,
lifecycleOwner: LifecycleOwner,
) : PreferencesManager {
override val preferences: StateFlow<Preferences> = preferencesStateFlow.asStateFlow()
...
My custom preferences data class:
// the next free proto number = 119
@Serializable
data class Preferences(
....
@ProtoNumber(62) val preferencesCamera: HashMap<String, PreferencesCamera>, // key - camera id
@ProtoNumber(63) val useMiles: Boolean,
...
) {
// next free proto number 14
@Serializable
data class PreferencesCamera(
@ProtoNumber(1) val cameraAperture: Float,
@ProtoNumber(2) val cameraControlSceneMode: CameraSceneMode?,
@ProtoNumber(3) val cameraControlSceneModeNight: CameraSceneMode?,
@ProtoNumber(4) val cameraExposure: Int,
@ProtoNumber(5) val cameraExposureNight: Int,
...
)
So yes, I use DataStore + Protobuf:
class PreferencesDatastoreSerializer @Inject constructor(
@ApplicationContext private val context: Context
) : Serializer<Preferences> {
override val defaultValue: Preferences
get() = PreferencesDefaults.getDefaultPreferences(context)
override suspend fun readFrom(input: InputStream): Preferences {
return try {
ProtoBuf.decodeFromByteArray<Preferences>(input.readBytes())
} catch (e: Throwable) {
e.printStackTrace()
throw CorruptionException("The data could not be de-serialized", e)
}
}
@Suppress("BlockingMethodInNonBlockingContext")
override suspend fun writeTo(t: Preferences, output: OutputStream) {
output.write(ProtoBuf.encodeToByteArray(t))
}
}
object PreferencesDefaults {
fun getDefaultPreferences(context: Context): Preferences {
...
return Preferences(
...
preferencesCamera = hashMapOf(),
)
}
...
The error mentions something about byte arrays and hashmap during this exception, I don't have byte arrays in my Preference data class, but I use hashmap there:
@ProtoNumber(62) val preferencesCamera: HashMap<String, PreferencesCamera>
By byte arrays it just means that something happened when it was trying to encode or decode data from protobuf file (*.pb), I guess...
But what could be the issue here as it doesn't happen for most users and only for some?