Environment
- react-native-onesignal: 5.4.2
- OneSignal Android SDK: 5.7.7
- Platform: Android
Steps to Reproduce
- Install app and grant notification permission
- Reinstall the app (or clear app data)
- Reopen — do NOT toggle the notification permission
Expected Behavior
Notifications work normally after reinstall. Subscription status becomes SUBSCRIBED.
Actual Behavior
OneSignal dashboard shows "Permission Not Granted". getOptedInAsync() returns false.
The state may eventually recover (hours later), or never recover at all.
Root Cause
After reading the SDK source, the chain is:
1. NotificationsManager.permission is initialized correctly:
// NotificationsManager.kt
override var permission: Boolean = NotificationHelper.areNotificationsEnabled(appContext)
After reinstall, permission is already granted → permission = true from the start.
2. Fresh subscription has status = NO_PERMISSION:
// UserSwitcher.kt — createAndSwitchToNewUser()
status = currentPushSubscription?.status ?: SubscriptionStatus.NO_PERMISSION
After data clear, no previous subscription exists → status = NO_PERMISSION.
3. retrievePushTokenAndUpdateSubscription() — the function that fixes status — has only two triggers:
// DeviceRegistrationListener.kt
override fun onNotificationPermissionChange(permission: Boolean) {
retrievePushTokenAndUpdateSubscription() // trigger A
}
override fun onModelReplaced(model: ConfigModel, tag: String) {
if (tag != ModelChangeTags.HYDRATE) return
retrievePushTokenAndUpdateSubscription() // trigger B
}
4. Neither trigger fires:
- Trigger A (
onNotificationPermissionChange): never fires because setPermissionStatusAndFire() only fires when oldPermission != newPermission. Since permission starts as true (correctly read at init), and the system permission never changes, no event is emitted.
- Trigger B (
onModelReplaced(HYDRATE)): only fires when the ConfigModel is re-fetched from the backend. If the config cache is still valid, no re-fetch occurs → never fires.
Result: status stays NO_PERMISSION indefinitely. The subscription is optedIn = model.optedIn && status != NO_PERMISSION = false.
Suggested Fix
In DeviceRegistrationListener.start(), add a startup check:
override fun start() {
_configModelStore.subscribe(this)
_notificationsManager.addPermissionObserver(this)
_subscriptionManager.subscribe(this)
// Fix: if permission is already granted but subscription status is NO_PERMISSION,
// trigger token retrieval immediately on startup.
if (_notificationsManager.permission) {
retrievePushTokenAndUpdateSubscription()
}
}
This is safe — retrievePushTokenAndUpdateSubscription() is idempotent and already handles the case where a token exists.
Environment
Steps to Reproduce
Expected Behavior
Notifications work normally after reinstall. Subscription status becomes
SUBSCRIBED.Actual Behavior
OneSignal dashboard shows "Permission Not Granted".
getOptedInAsync()returnsfalse.The state may eventually recover (hours later), or never recover at all.
Root Cause
After reading the SDK source, the chain is:
1.
NotificationsManager.permissionis initialized correctly:After reinstall, permission is already granted →
permission = truefrom the start.2. Fresh subscription has
status = NO_PERMISSION:After data clear, no previous subscription exists →
status = NO_PERMISSION.3.
retrievePushTokenAndUpdateSubscription()— the function that fixesstatus— has only two triggers:4. Neither trigger fires:
onNotificationPermissionChange): never fires becausesetPermissionStatusAndFire()only fires whenoldPermission != newPermission. Sincepermissionstarts astrue(correctly read at init), and the system permission never changes, no event is emitted.onModelReplaced(HYDRATE)): only fires when theConfigModelis re-fetched from the backend. If the config cache is still valid, no re-fetch occurs → never fires.Result:
statusstaysNO_PERMISSIONindefinitely. The subscription isoptedIn = model.optedIn && status != NO_PERMISSION=false.Suggested Fix
In
DeviceRegistrationListener.start(), add a startup check:This is safe —
retrievePushTokenAndUpdateSubscription()is idempotent and already handles the case where a token exists.