New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Android 13 BadParcelableException: Parcel android.os.Parcel: Unmarshalling unknown type code #515
Comments
Interesting - sorry this is happening -
Updated from what to 5.5.0? This would help bisect and see what happened. It appears that this is likely a serialization/de-serialization issue where types mismatch or are in wrong order, and bisecting the diff between your started-at and ended-up-at versions would be useful |
If you were updating from 5.3.0 then I think it is the alteration in timestamp handling from here: https://github.com/invertase/notifee/pull/449/files Specifically I think the handling of "mTimestamp" in the trigger notification model is suspect, and may not be in sync between serialization / deserialization now. Just a hypothesis though, until I know what range we're looking for I can't be sure. For reference |
Proguard may also have relevance (see https://issuetracker.google.com/issues/177246248) with regards to serializing from one version of an app to another, and since you indicate this is only happening on Android 13 (which has a Parcelable substantially rewritten from Android 12 - https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android12-release/core/java/android/os/Parcel.java ) it may be serialization from one version of Android to another. Does this keep happening on Android 13 phones every time? Or for each Android phone that throws this stack, are they able to run successfully after? This one could be really subtle |
Hmm. That's simplifying, that proguard is disabled, good to know. So it's down to (I think...) either 1- "Android 12 to Android 13 introduces something such that notifications are incorrect across that transition on a specific user device, or I suspect 1 is not the root cause, as you indicate some users on Android 13 see it, then don't see it, then see it again. So I suspect there is either 2a - a bug in the Parcelable code for Android 13 (though I did not see any in the android issuetracker...) or My working hypothesis is 2b. How are you setting / using the APIs here? A reproducer would be most excellent especially if it showed the issue on an Android 13 emulator, because it is a big diff @notifee/react-native@3.0.4...@notifee/react-native@5.50 These look suspect Most of those were on 5.4.0, I wonder if < 5.4.0 would work without reproducing? Not sure if you can do a build like that (intentionally dropping to 5.3.0) but I believe 5.3.0 will work well on Android 13 still, just maybe with a timestamp repeat frequency trigger regression |
I've done a bit more investigation: Someone in the office has a Pixel w/ Android 13 - I got them to install the old app, schedule a notification then upgrade it. No issue there - no error to Sentry, notification was delivered after the upgrade. Don't really want to ask them to downgrade their OS to 12 (is that even possible anymore?) as it's their personal device. As far as looking at the data for some of the users who have hit this in prod: None of them has contacted support about notification issues - so far anyway. It might take them a while to notice however (some of our notifications are weekly or monthly). The user who had the big burst of these errors on Sep 5th has used the app again on the 6th, 7th and 8th, so the problem seems to have cleared for them. Their installation ID remains consistent so they don't seem to have reinstalled the app. They were on Android 12 with the previous version of our app AND were still on 12 when they first upgraded to, and used, the latest version of our app. The first time they used the app on Android 13 was when the burst of errors came through. The user who has had repeated instances of this on Aug 30th, Aug 31st, Sep 1st, Sep 6th, and Sep 7th seems to be having it happen every time they use the app (cross-referencing with other data we have). This user was on Android 12 with the previous version of the app but both the app and the OS upgraded at the same time.
Channel creation is done on app startup, every app startup, pretty much every channel is setup like this due to being largely templated: notifee.createChannel({
id: 'unique-id',
name: 'Unique name',
lightColor: 'App common color',
description: 'Unique description',
importance: AndroidImportance.HIGH,
visibility: AndroidVisibility.PRIVATE,
lights: true,
sound: 'default',
}) One thing that is possibly relevant? We were missing Notification creation/scheduling is largely templated too. Every time a module in our app needs a notification scheduled we currently do the following (essentially): // Check permissions first
const settings = yield notifee.requestPermission()
if (settings.authorizationStatus >= AuthorizationStatus.AUTHORIZED) {
// schedule notifications
} else {
// abandon trying to schedule
}
// Not the most efficient but right now, we kill and reschedule all notifications whenever a module in the app wants a change done
yield notifee.cancelTriggerNotifications()
// osNotification.notification is the Notifee notification
for (const osNotification of osNotifications) {
if (osNotification.schedule.fireDate.isBefore(LocalDateTime.now(clock))) {
throw new NotificationScheduledInPastError('Notification is scheduled in the past.', osNotification.notification.id, LocalDateTime.now(clock)) // handled elsewhere
}
yield notifee.createTriggerNotification(osNotification.notification, {
type: TriggerType.TIMESTAMP,
timestamp: 12345,
repeatFrequency: schedule.repeatInterval ? RepeatFrequency.RELEVANT_VALUE : undefined,
}
} The thing that confuses me about the user who has had this happen in every session since they upgraded to Android 13 - we reschedule all our notifications when the app is launched. So, I would have expected the problem to disappear the first time I'm trying to come up with a repro but I've been unable to reproduce it so far. 😞 |
We've been noticing the same error with Pixel users upgrading their Android versions to 13. Users have told that the app crashes and won't reopen, but reinstalling works. One user feedback stated that on Android 13 notifications will only be shown after reopening the app instead of when they should be. Not sure if this is before or after a reinstallation or if it's even related to this same issue. Our Google Play Console has logged around 3k instances of the Here are our relevant dependency versions:
|
This is evil! Not your fault of course, just I have a hunch this is going to be really subtle. |
Just to be clear on "most rapid path forward", these are I think the options, but all require a reproduction you can trigger or it's just guess work:
With no reproduction (ideally in an android 13 emulator...) this will be really hard though, I would focus everything on a repro if possible |
We are having the same issue in production. We are currently using |
Just offering that I actually have a Pixel 4a (which I may update to Android 13, currently it is on 12) in my household (not my phone, but I have access to it) if there was any reproduction that triggered on a real device reliably, I could also work with it. Not as easy as an emulator of course but feasible since this appears to be a serious issue. |
A stack trace in case that will help get to the bottom of this!
|
@mikehardy So from the stack trace above, it looks like when using the getTriggerNotification() call that it throws the error. Might be able to create a reproduction through that. |
Ah sorry, our version in yarn.lock is indeed 5.1.0. |
Oh, this might possibly be the trigger for us too. I simplified the code I posted above - one thing I omitted - just before we call |
Okay, so we think we know (pending repro)
We need the specific data + Notifee API call to set the trigger notification followed by the getTriggerNotifications call that blows up the repro and then we can all have it under the microscope I think |
FYI we're still trying to find a way to reproduce this with our app in an emulator based on the app configuration of users who are hitting the issue. No luck so far. |
Hello Mike, I am Liam's colleague, I was trying to reproduce this bug but unfortunately my attempts were unsuccessful, I tried the following:
We are considering getting a device that we can upgrade and downgrade between Android 12 and 13. |
@mikehardy I know Java but I'm not that familiar with the Android APIs - is it possible to retrieve this data from the OS without having to unmarshal it (either via Notifee or directly)? Or is the unmarshalling an automatic part of retrieval from the OS? I'm just wondering if we could do a new build which retrieves the raw data so we can start having it in the Sentry reports and hopefully get to the bottom of what the trigger is... |
We got a Pixel 6a for testing the Android 12 to 13 upgrade. Before Android 13 upgrade
Results after Android 13 upgrade
Our conclusion
|
Maddening! I'm still not able to differentiate between cases 2a / 2b but at this point it seems that all Notifee versions are affected, and it's just the Android API 12->13 transition that's the trigger. One correction: in your conclusion you mention Possible avenues of pursuit: 2 is the real fix I think but would rely on careful implementation and careful inspection of what is going in and out of here notifee/android/src/main/java/app/notifee/core/NotificationManager.java Lines 791 to 795 in 30ff7e2
3 is mitigation, and should be done as well. How is the crash triggered? It's lazy - the error is in the serialized byte structure somehow but it remains latent until it deserialization is forced to push the object information across the Java / Javascript bridge. It's happening here - https://github.com/facebook/react-native/blob/40567c21e949ef686367bb39fdb48e34236cbd82/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java#L295 (or, matching the stack above exactly, version located to react-native 0.64: https://github.com/facebook/react-native/blob/400902093aa3ccfc05712a996c592a86f342253a/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java#L291) And more specifically https://github.com/facebook/react-native/blob/400902093aa3ccfc05712a996c592a86f342253a/ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java#L308 where it recursively de-serializes the sub-bundles So it seems that in our code here:
...we can trigger the issue while still in a place we can handle errors by probing deserialization in place, specifically copying / reimplementing this with the rough idea of "until no more values are Bundles, get all bundle keys, foreach key check type, if type is bundle (recur...) Then, while still in NotifeeCore's android java code, you catch the exception and bubble it up as Task failure here
It will come through here notifee/packages/react-native/android/src/main/java/io/invertase/notifee/NotifeeApiModule.java Line 91 in 30ff7e2
and it will reject here notifee/packages/react-native/android/src/main/java/io/invertase/notifee/NotifeeReactUtils.java Line 56 in 30ff7e2
At which point app is not going to crash, and calling code knows that scheduled notifications are broken somehow Design question for API users: at this point, is it best to purge the offending data? Hard to say. This is effectively data loss, you likely need to educate the user, and if we auto-purge the bad data there is no way to detect the data loss again so you cannot necessarily educate the user. So it seems best to maybe implement this bad-Bundle-detection, alter the docs to say Thoughts? |
Unfortunately - I need to be clear that my ability to implement+test anything here is limited to comment-based collaboration, just to avoid doubt. I don't have time to engage with this at the code level, but the above comment represents the best thinking I've got on it in available time. Cheers |
I realize I did not answer these directly:
The unmarshal/deserialize is required to go across the react-native bridge from Java to Javascript, it is unavoidable, however it is possible at the Android code level to probe it and/or log it prior to hitting the react-native bridge. Sentry likely has ways to log things so it could be possible to do that - unknown - I know crashlytics does have ways to do it at any rate. Requires some native code, but is possible It is so painful that this only happens on android 12 -> 13 update. The only way to reliably reproduce this will be to have a never-upgraded-to-13 phone, put trigger notifications in while it's on 12, then update to 13 and DO NOT CANCEL THE NOTIFICATIONS OR UNINSTALL. At that point you've got a reliable source of bad data to probe, but if you uninstall the app or cancel the notifications they'll go poof. Or I suppose you could find the workmanager database in the device data and pull it out for storage, at which point you could manually copy it in at any time 🤔 Anyway, other than at the native Android level there's no way to probe this data, and it has to be done post-data-fetch / pre-react-native-bridge |
We're about to do a new release - for now, we've removed our |
Steps to reproduce:
|
@JK0N can you perhaps attach a copy of those notifee databases you have reserved in testing? |
any solution for this issue ? we're seeing a lot of such crashes lately as more and more devices upgrade to 13 |
Our temporary workaround for this is to check if the user has Android 13 (API level 33) and if so cancel all notifications on app start. We store a value in LocalStorage so that the cancellation will only be done once. In our case we can then reschedule the notifications since we know what they were supposed to be. |
None so far - mostly for lack of time. It appears we have the databases you can just drop on to a phone that will reproduce the crash above - it needs triage + workaround based on those |
I can take a look this week to see if I can reproduce the error |
So its happening only if we use getTriggerNotifications right. If we get rid of it will be solve the problem for now and the notifications actions like sending/canceling will work without problem ? @liamjones @JK0N |
@batuhansahan Yes, we've been setting and cancelling fine. Edit: clarification - cancelling all notifications in one go, I haven't tried individual notification cancelling. |
@liamjones after removing getTriggerNotifications app is opened fine except the local notifications not coming anymore and i give the new android 13 feature(allow request permission option with notifee.requestpermissions()) a yes. I dont know why. Gonna debug it. Thanks |
@batuhansahan the notifications may be failing to deliver because they're still present and Notifee can't decode them. If you cancel them and reschedule them they should then be in a readable format again. |
@liamjones i did that already, i even tried uninstalling app and installing again than schedule notifications again no change. I will debug hope to find any error logs on debug mode. edit: Android 13: Creating notification with custom led light blocking notification displaying |
A temporary workaround if your logic is to upsert missing notifications after calling getTriggerNotifications is the following patch and function: export const safeGetTriggerNotifications: typeof notifee.getTriggerNotifications = async () => {
// https://github.com/invertase/notifee/issues/515
const userMightHaveBug = Platform.OS === 'android' && Platform.Version >= 33;
if (!userMightHaveBug) {
return notifee.getTriggerNotifications();
}
try {
return await notifee.getTriggerNotifications();
} catch (error) {
const alreadyTriedNotifeeAndroid13Workaround = await AsyncStorage.getItem(
AsyncStorageKey.android_triedNotifeeAndroid13Workaround
);
if (alreadyTriedNotifeeAndroid13Workaround) {
throw error;
}
await notifee.cancelAllNotifications();
await AsyncStorage.setItem(
AsyncStorageKey.android_triedNotifeeAndroid13Workaround,
new Date().toISOString()
);
return notifee.getTriggerNotifications();
}
}; diff --git a/node_modules/@notifee/react-native/android/src/main/java/io/invertase/notifee/NotifeeReactUtils.java b/node_modules/@notifee/react-native/android/src/main/java/io/invertase/notifee/NotifeeReactUtils.java
index b60e6d4..eed80fe 100644
--- a/node_modules/@notifee/react-native/android/src/main/java/io/invertase/notifee/NotifeeReactUtils.java
+++ b/node_modules/@notifee/react-native/android/src/main/java/io/invertase/notifee/NotifeeReactUtils.java
@@ -66,11 +70,15 @@ class NotifeeReactUtils {
// TODO custom error class with message/code
promise.reject(e);
} else {
- WritableArray writableArray = Arguments.createArray();
- for (Bundle bundle : bundleList) {
- writableArray.pushMap(Arguments.fromBundle(bundle));
+ try {
+ WritableArray writableArray = Arguments.createArray();
+ for (Bundle bundle : bundleList) {
+ writableArray.pushMap(Arguments.fromBundle(bundle));
+ }
+ promise.resolve(writableArray);
+ } catch (Exception error) {
+ promise.reject(error);
}
- promise.resolve(writableArray);
}
}
|
Hello 👋, to help manage issues we automatically close stale issues. This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?
Thank you for your contributions. |
Good day. I have an issue with my project - suddenly when I turn off my app and opened it - it crashes and gives me this error:
Maybe somebody had the same problem. |
Still having this issue at this date, as a workaround I have implemented a simple hook that cancel scheduled notifications when android 13 is detected for the first time on the app. Here it is in case it can help: import {useEffect} from 'react'
import {Platform} from 'react-native'
import {cancelAllNotifications} from '~/components/notifications/notifications'
import {getStorage, setStorage} from '~/utils/storage'
const STORAGE_KEY = 'hasDeletedAllNotificationsBecauseOfAndroid13'
export const useFixAndroid13NotificationIssue = () => {
useEffect(() => {
if (Platform.OS === 'android' && Platform.Version >= 33) {
const done = getStorage(STORAGE_KEY, false)
if (!done) {
console.log('cancel pending notifications')
cancelAllNotifications()
setStorage(STORAGE_KEY, true)
}
}
}, [])
} for the utility functions, I am using MMKV and a simple wrapper around notifee: const storage = new MMKV()
export const getStorage = <T>(key: string, defaultValue: T) => {
const value = storage.getString(key) as any
if (value === undefined) {
return defaultValue
}
return JSON.parse(value) as T
}
export const setStorage = (key: string, value: any) => {
storage.set(key, JSON.stringify(value))
} export const cancelAllNotifications = async () => {
await notifee.cancelAllNotifications()
} Tested with the database provided by @JK0N, thank you all for the investigations on this annoying issue. |
@mikehardy Can we reopen? Same issue on last version. Perhaps we could add something to the doc if it can't be fixed in the library. |
We've been seeing this a reasonable amount since our app was updated to Notifee 5.5.0 (86 instances across 10 users in the last 8 days), any ideas?
It seems to be happening purely on Android 13. It's also only happening on various models of Pixel phones but I assume that's because it's the only one with Android 13 available so far...
This is on React Native 0.66.3 and our top-level build.gradle contains:
Stack trace (it's always the same type code and offset):
The text was updated successfully, but these errors were encountered: