Skip to content

fix: [SDK-5366] catch native init errors on .NET Android - #208

Open
abdulraqeeb33 wants to merge 4 commits into
mainfrom
ar/sdk-5366
Open

abdulraqeeb33 wants to merge 4 commits into
mainfrom
ar/sdk-5366

Conversation

@abdulraqeeb33

@abdulraqeeb33 abdulraqeeb33 commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Description

One Line Summary

Catch and log the IllegalStateException native Android throws for calls made before Initialize, instead of letting it crash the app.

Details

Motivation

Native Android throws IllegalStateException ("Must call 'initWithContext' before ...") from login, logout, and the User, Session, Notifications, InAppMessages, and Location getters when init has not started. The wrapper passed that to the app, so OneSignal.Login and similar crashed unless the app caught it.

SDK-5334 made this easier to hit: Initialize("") returns before InitWithContext, so a later valid Login throws. Native Android would have initialized with an empty app id. iOS already logs and returns before init.

Fixes SDK-5366.

Scope

  • New NativeCall helper (Run / Get) wraps the native calls that go through waitForInit. It catches only Java.Lang.IllegalStateException, logs "<api> ignored: <native message>" at error level to logcat, and returns false, null, or the given fallback. Any other exception still reaches the app.
  • Wrapped: Login, Logout, and the user, push subscription, session, notifications, and in-app messages calls.
  • Observer registration in Initialize is wrapped too, one call per manager. InitWithContext returns while native init is still running, and registration waits for it, so a failed init (for example a locked device in direct boot) used to throw out of Initialize.
  • Not wrapped: Debug and the consent setters (native accepts both before init). AndroidLocationManager is unchanged; it already catches.
  • Native stays the only source of init state. The wrapper keeps no init flag, so calls native would accept are never skipped.
  • Every InputGuard rejection (empty app id, empty login, missing tag key, and the rest) writes the debug console line and, on Android, the same message to logcat. Initialize uses the guard again.

Behavior change

A failed init after a real Initialize("appId") no longer throws from later calls. It shows up as a logcat error carrying native's message (OneSignal initWithContext failed.). This matches iOS, which logs and returns.

Known gaps

  • Before init, Notifications.Permission, PushSubscription.OptedIn, and RequestPermissionAsync return false, which a caller cannot tell from a real denial. PermissionNative() returns NotDetermined.
  • Writes made before init are dropped, not replayed. PushSubscription.OptOut() and InAppMessages.Paused = true are the risky ones: the app's intent is lost and only logcat shows it.
  • The catch is by type, so an IllegalStateException native throws for a reason other than init is also logged and dropped.
  • AndroidLocationManager catches Exception (existing behavior), so an init error there is logged as a missing location module.

Testing

Unit testing

None. This repo has no .NET test project.

Manual testing

dotnet build of OneSignalSDK.DotNet.Android passes with no warnings. Not exercised on a device yet.

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
  • I have reviewed this PR myself, ensuring it meets each checklist item

Native Android throws IllegalStateException from login, logout, and the
User, Session, Notifications, InAppMessages, and Location getters when
init has not started. The wrapper passed that straight to the app, so a
call before Initialize, or after Initialize("") returned early, crashed.

Skip those native calls and log once per API until InitWithContext has
been attempted. A started init that fails still throws. Debug and the
consent setters stay ungated because native accepts them before init.
Log the missing appId through Android logcat so release builds show it.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abdulraqeeb33
abdulraqeeb33 requested a review from a team as a code owner October 8, 2026 18:22
@abdulraqeeb33
abdulraqeeb33 marked this pull request as draft October 8, 2026 18:25
Drop the wrapper init flag. Call native and catch IllegalStateException,
logging native's own message at error level and returning the fallback.
Native stays the only source of init state, so calls it would accept are
never skipped.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abdulraqeeb33 abdulraqeeb33 changed the title fix: [SDK-5366] skip Android calls made before Initialize fix: [SDK-5366] catch native init errors on .NET Android Oct 8, 2026
AR Abdul Azeez and others added 2 commits October 8, 2026 13:58
InitWithContext returns while native init is still running. Registering
the user, notification, and in-app message observers waits for it, so a
failed init (for example a locked device in direct boot) threw out of
Initialize. Wrap each registration so one failure does not skip the rest.

PermissionNative now reports NotDetermined when native cannot answer,
instead of Denied.

Co-authored-by: Cursor <cursoragent@cursor.com>
InputGuard lives in the shared library, which cannot reference Android
logging, and its Debug.WriteLine calls were compiled out because the
Core debug build defined no DEBUG constant. Keep the console line and
let Android also write the same message to logcat. Initialize uses the
guard again.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abdulraqeeb33
abdulraqeeb33 marked this pull request as ready for review October 8, 2026 19:47

@fadi-george fadi-george left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for cases like
initialize('') followed by login('...')?
This breaks consistency with other sdks. I'd maybe make a thread on this that is if we should really handle this edge case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants