-
Notifications
You must be signed in to change notification settings - Fork 378
fix: [SDK-3475] restrict notification component exports #2659
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
Open
fadi-george
wants to merge
2
commits into
main
Choose a base branch
from
fadi/sdk-3475
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+5
−5
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 🟡 Setting
BootUpReceivertoandroid:exported="false"silently disables theandroid.intent.action.QUICKBOOT_POWERONaction that remains in its intent-filter. UnlikeBOOT_COMPLETED(a protected broadcast sent bysystem_serverunder SYSTEM_UID, which still reaches non-exported receivers),QUICKBOOT_POWERONis not in AOSP's protected broadcast list — it is emitted by OEM launchers (HTC Sense, older Lenovo / older Samsung quick-power-on flows) under their own UIDs, so the platform will block cross-UID delivery to a non-exported receiver. Full-reboot restore viaBOOT_COMPLETEDis unaffected, only the quickboot fast-resume path on those legacy OEM devices regresses; fix by dropping the now-unreachableQUICKBOOT_POWERONaction from the filter, splitting it into its own exported receiver, or keeping the receiverexported="true"withandroid:permission="android.permission.RECEIVE_BOOT_COMPLETED"as a guard. Nit — affected device population is very small in 2026.Extended reasoning...
What the bug is.
BootUpReceiverdeclares two actions in its intent-filter:android.intent.action.BOOT_COMPLETEDandandroid.intent.action.QUICKBOOT_POWERON(manifest lines 84-90). The PR flips the receiver fromexported="true"toexported="false". Withexported="false", Android only delivers broadcasts from the same UID or fromsystem_serverfor protected broadcasts.BOOT_COMPLETEDIS in AOSP's protected broadcast list (declared inframeworks/base/core/res/AndroidManifest.xml) and is dispatched bysystem_server, so it continues to reach the receiver.QUICKBOOT_POWERONis NOT in that list — it is an OEM-specific broadcast originally introduced by HTC Sense's quickboot flow and adopted by some older Lenovo / older Samsung quick-power-on launchers, dispatched by an OEM launcher process under its own UID.The code path that triggers it.
BootUpReceiver.ktruns the same logic (NotificationRestoreWorkManager.beginEnqueueingWork) regardless of which action arrived — i.e., the SDK explicitly opted into supporting the quickboot fast-resume path when it addedQUICKBOOT_POWERONto the filter. After this PR, an OEM launcher'sQUICKBOOT_POWERONbroadcast targeted at the OneSignal app's process is cross-UID, the receiver is non-exported, and Android's broadcast access control blocks the delivery. The action remains in the filter but is silently unreachable.Why existing code doesn't prevent it. The other components that were also flipped to
exported="false"in this PR are safe for different reasons:UpgradeReceiverreceivesMY_PACKAGE_REPLACEDfromsystem_server(protected),NotificationDismissReceiverand the twoNotificationOpenedActivityvariants are triggered viaPendingIntentthat runs under the host app's own UID.BootUpReceiverforBOOT_COMPLETEDis similarly safe (protected broadcast fromsystem_server).QUICKBOOT_POWERONis the one action with no such rescue: no protected-broadcast exemption, no same-UID sender, noPendingIntentwrapping.Impact. On the affected legacy OEM devices (mostly older HTC Sense, some older Lenovo, some older Samsung quick-power-on), users who quickboot the device will no longer have OneSignal notifications restored via
NotificationRestoreWorkManager. Full reboots still work becauseBOOT_COMPLETEDis unaffected. This is exactly the scenario the PR description's 'Recommended follow-up coverage' lists as untested ('boot restore'). Impact in 2026 is narrow — HTC stopped selling Android phones around 2017, quickboot is largely deprecated, and Android 8+ also tightens manifest receivers for implicit broadcasts not on the exempt list — so the quickboot regression only meaningfully affects a small legacy population on API 21-25 devices that still emitQUICKBOOT_POWERONfrom a non-system-privileged sender.How to fix. Pick one: (a) drop
<action android:name="android.intent.action.QUICKBOOT_POWERON" />from the intent-filter as a deliberate, documented behavior change since it is now unreachable anyway; (b) splitQUICKBOOT_POWERONinto its own<receiver android:exported="true">so the cross-UID OEM-launcher broadcast can reach it; or (c) revertBootUpReceivertoandroid:exported="true"and addandroid:permission="android.permission.RECEIVE_BOOT_COMPLETED"as a guard (though someQUICKBOOT_POWERONsenders may not hold that permission). The other five components in this PR can stay non-exported.Step-by-step proof.
sendBroadcast(new Intent("android.intent.action.QUICKBOOT_POWERON")).com.onesignal.notifications.receivers.BootUpReceiverdeclared withandroid:exported="false".QUICKBOOT_POWERONis not in AOSP's protected broadcast list, so AMS does NOT apply the SYSTEM_UID-bypass that lets protected broadcasts reach non-exported receivers.BootUpReceiver.onReceiveis never invoked forQUICKBOOT_POWERON.NotificationRestoreWorkManager.beginEnqueueingWorkis not enqueued on the quickboot path. Pending OneSignal notifications are not restored.BOOT_COMPLETEDstill works: on a full reboot,system_server(SYSTEM_UID) dispatches the protectedBOOT_COMPLETEDbroadcast; AMS allows it to reach non-exported receivers;BootUpReceiver.onReceivefires; restore proceeds normally. Only the quickboot fast-resume path is regressed.