diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt index 99024c3da4..0d591a338e 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt @@ -263,7 +263,6 @@ internal class OneSignalImp( suspendifyOnIO { internalInit(context, appId) } - initState = InitState.SUCCESS return true } @@ -306,22 +305,48 @@ internal class OneSignalImp( ) { Logging.log(LogLevel.DEBUG, "Calling deprecated login(externalId: $externalId, jwtBearerToken: $jwtBearerToken)") - if (!initState.isSDKAccessible()) { - throw IllegalStateException("Must call 'initWithContext' before 'login'") + // Check state and provide appropriate error messages + when (initState) { + InitState.FAILED -> { + throw IllegalStateException("Initialization failed. Cannot proceed.") + } + InitState.NOT_STARTED -> { + throw IllegalStateException("Must call 'initWithContext' before 'login'") + } + InitState.IN_PROGRESS, InitState.SUCCESS -> { + // Continue - these states allow proceeding (will wait if needed) + } } waitForInit() + // Re-check state after waiting - init might have failed during the wait + if (initState == InitState.FAILED) { + throw IllegalStateException("Initialization failed. Cannot proceed.") + } suspendifyOnIO { loginHelper.login(externalId, jwtBearerToken) } } override fun logout() { Logging.log(LogLevel.DEBUG, "Calling deprecated logout()") - if (!initState.isSDKAccessible()) { - throw IllegalStateException("Must call 'initWithContext' before 'logout'") + // Check state and provide appropriate error messages + when (initState) { + InitState.FAILED -> { + throw IllegalStateException("Initialization failed. Cannot proceed.") + } + InitState.NOT_STARTED -> { + throw IllegalStateException("Must call 'initWithContext' before 'logout'") + } + InitState.IN_PROGRESS, InitState.SUCCESS -> { + // Continue - these states allow proceeding (will wait if needed) + } } waitForInit() + // Re-check state after waiting - init might have failed during the wait + if (initState == InitState.FAILED) { + throw IllegalStateException("Initialization failed. Cannot proceed.") + } suspendifyOnIO { logoutHelper.logout() } } @@ -358,6 +383,10 @@ internal class OneSignalImp( withTimeout(MAX_TIMEOUT_TO_INIT) { initAwaiter.awaitSuspend() } + // Re-check state after waiting - init might have failed during the wait + if (initState == InitState.FAILED) { + throw IllegalStateException("Initialization failed. Cannot proceed.") + } } catch (e: TimeoutCancellationException) { throw IllegalStateException("initWithContext was timed out after $MAX_TIMEOUT_TO_INIT ms") } @@ -384,6 +413,10 @@ internal class OneSignalImp( InitState.IN_PROGRESS -> { Logging.debug("Waiting for init to complete...") waitForInit() + // Re-check state after waiting - init might have failed during the wait + if (initState == InitState.FAILED) { + throw IllegalStateException("Initialization failed. Cannot proceed.") + } } InitState.FAILED -> { throw IllegalStateException("Initialization failed. Cannot proceed.") @@ -391,6 +424,10 @@ internal class OneSignalImp( else -> { // SUCCESS waitForInit() + // Re-check state after waiting - init might have failed during the wait + if (initState == InitState.FAILED) { + throw IllegalStateException("Initialization failed. Cannot proceed.") + } } } diff --git a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/application/SDKInitTests.kt b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/application/SDKInitTests.kt index 318f6cb1c1..04a5fa3453 100644 --- a/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/application/SDKInitTests.kt +++ b/OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/application/SDKInitTests.kt @@ -150,8 +150,19 @@ class SDKInitTests : FunSpec({ accessorThread.join(500) // Then - // should complete even SharedPreferences is unavailable + // should complete even SharedPreferences is unavailable (non-blocking) accessorThread.isAlive shouldBe false + + // Release the SharedPreferences lock so internalInit can complete + trigger.complete() + + // Wait for initialization to complete (internalInit runs asynchronously) + var attempts = 0 + while (!os.isInitialized && attempts < 50) { + Thread.sleep(20) + attempts++ + } + os.isInitialized shouldBe true } @@ -224,12 +235,23 @@ class SDKInitTests : FunSpec({ accessorThread.start() accessorThread.join(500) - os.isInitialized shouldBe true + // initWithContext should return immediately (non-blocking) + // but isInitialized won't be true until internalInit completes + // which requires SharedPreferences to be unblocked accessorThread.isAlive shouldBe true - // release the lock on SharedPreferences + // release the lock on SharedPreferences so internalInit can complete trigger.complete() + // Wait for initialization to complete (internalInit runs asynchronously) + var initAttempts = 0 + while (!os.isInitialized && initAttempts < 50) { + Thread.sleep(20) + initAttempts++ + } + + os.isInitialized shouldBe true + accessorThread.join(500) accessorThread.isAlive shouldBe false os.user.externalId shouldBe externalId