From f09845a035fc23756f4950699abca1a24c372542 Mon Sep 17 00:00:00 2001 From: Nan Date: Thu, 11 Apr 2024 21:20:55 -0700 Subject: [PATCH 1/2] fix WorkManager exception * Before using WorkManager, check for its existence. Else, in rare cases that caused crashing, initialize it ourselves. * Additionally, as another layer to handling this issue, when initializing WorkManager ourselves, surround it in a try-catch as it is may be the slightest bit possible for another thread to initialize it in the interim between checking and initializing. * Provides a method to check if WorkManager is initialized in this process. - This is effectively the `WorkManager.isInitialized()` public method introduced in `androidx.work:work-*:2.8.0-alpha02`. - Please see https://android-review.googlesource.com/c/platform/frameworks/support/+/1941186 for the library's implementation --- .../internal/common/OSWorkManagerHelper.kt | 46 +++++++++++++++++++ .../impl/NotificationGenerationWorkManager.kt | 4 +- .../impl/ReceiveReceiptWorkManager.kt | 4 +- .../impl/NotificationRestoreWorkManager.kt | 4 +- 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt diff --git a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt new file mode 100644 index 0000000000..d0e259f4f3 --- /dev/null +++ b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt @@ -0,0 +1,46 @@ +package com.onesignal.notifications.internal.common + +import android.annotation.SuppressLint +import android.content.Context +import androidx.work.Configuration +import androidx.work.WorkManager +import androidx.work.impl.WorkManagerImpl + +object OSWorkManagerHelper { + /** + * Helper method to provide a way to check if WorkManager is initialized in this process. + * The purpose of this helper is to work around this bug - https://issuetracker.google.com/issues/258176803 + * + * This is effectively the `WorkManager.isInitialized()` public method introduced in androidx.work:work-*:2.8.0-alpha02. + * Please see https://android-review.googlesource.com/c/platform/frameworks/support/+/1941186. + * + * @return `true` if WorkManager has been initialized in this process. + */ + @SuppressWarnings("deprecation") + @SuppressLint("RestrictedApi") + private fun isInitialized(): Boolean { + return WorkManagerImpl.getInstance() != null + } + + /** + * If there is an instance of WorkManager available, use it. Else, in rare cases, initialize it ourselves. + * + * Calling `WorkManager.getInstance(context)` directly can cause an exception if it is null. + * + * @return an instance of WorkManager + */ + @JvmStatic + @Synchronized + fun getInstance(context: Context): WorkManager { + if (!isInitialized()) { + try { + // Initialization can fail if another thread initializes in the small time gap + // https://android.googlesource.com/platform/frameworks/support/+/60ae0eec2a32396c22ad92502cde952c80d514a0/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java#177 + WorkManager.initialize(context, Configuration.Builder().build()) + } catch (e: IllegalStateException) { + // Admittedly starting to get hacky + } + } + return WorkManager.getInstance(context) + } +} diff --git a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/generation/impl/NotificationGenerationWorkManager.kt b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/generation/impl/NotificationGenerationWorkManager.kt index 0e93dbc185..741b644fbb 100644 --- a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/generation/impl/NotificationGenerationWorkManager.kt +++ b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/generation/impl/NotificationGenerationWorkManager.kt @@ -5,12 +5,12 @@ import androidx.work.CoroutineWorker import androidx.work.Data import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequest -import androidx.work.WorkManager import androidx.work.WorkerParameters import com.onesignal.OneSignal import com.onesignal.common.AndroidUtils import com.onesignal.debug.internal.logging.Logging import com.onesignal.notifications.internal.common.NotificationFormatHelper +import com.onesignal.notifications.internal.common.OSWorkManagerHelper import com.onesignal.notifications.internal.generation.INotificationGenerationProcessor import com.onesignal.notifications.internal.generation.INotificationGenerationWorkManager import org.json.JSONException @@ -55,7 +55,7 @@ internal class NotificationGenerationWorkManager : INotificationGenerationWorkMa Logging.debug( "NotificationWorkManager enqueueing notification work with notificationId: $osNotificationId and jsonPayload: $jsonPayload", ) - WorkManager.getInstance(context) + OSWorkManagerHelper.getInstance(context) .enqueueUniqueWork(osNotificationId, ExistingWorkPolicy.KEEP, workRequest) return true diff --git a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/receivereceipt/impl/ReceiveReceiptWorkManager.kt b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/receivereceipt/impl/ReceiveReceiptWorkManager.kt index 494519ed1f..eb421f0ed8 100644 --- a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/receivereceipt/impl/ReceiveReceiptWorkManager.kt +++ b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/receivereceipt/impl/ReceiveReceiptWorkManager.kt @@ -7,13 +7,13 @@ import androidx.work.Data import androidx.work.ExistingWorkPolicy import androidx.work.NetworkType import androidx.work.OneTimeWorkRequest -import androidx.work.WorkManager import androidx.work.WorkerParameters import com.onesignal.OneSignal import com.onesignal.common.AndroidUtils import com.onesignal.core.internal.application.IApplicationService import com.onesignal.core.internal.config.ConfigModelStore import com.onesignal.debug.internal.logging.Logging +import com.onesignal.notifications.internal.common.OSWorkManagerHelper import com.onesignal.notifications.internal.receivereceipt.IReceiveReceiptProcessor import com.onesignal.notifications.internal.receivereceipt.IReceiveReceiptWorkManager import com.onesignal.user.internal.subscriptions.ISubscriptionManager @@ -57,7 +57,7 @@ internal class ReceiveReceiptWorkManager( Logging.debug( "OSReceiveReceiptController enqueueing send receive receipt work with notificationId: $notificationId and delay: $delay seconds", ) - WorkManager.getInstance(_applicationService.appContext) + OSWorkManagerHelper.getInstance(_applicationService.appContext) .enqueueUniqueWork( notificationId + "_receive_receipt", ExistingWorkPolicy.KEEP, diff --git a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/restoration/impl/NotificationRestoreWorkManager.kt b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/restoration/impl/NotificationRestoreWorkManager.kt index b97d123daf..bb2b567850 100644 --- a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/restoration/impl/NotificationRestoreWorkManager.kt +++ b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/restoration/impl/NotificationRestoreWorkManager.kt @@ -4,10 +4,10 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequest -import androidx.work.WorkManager import androidx.work.WorkerParameters import com.onesignal.OneSignal import com.onesignal.notifications.internal.common.NotificationHelper +import com.onesignal.notifications.internal.common.OSWorkManagerHelper import com.onesignal.notifications.internal.restoration.INotificationRestoreProcessor import com.onesignal.notifications.internal.restoration.INotificationRestoreWorkManager import java.util.concurrent.TimeUnit @@ -36,7 +36,7 @@ internal class NotificationRestoreWorkManager : INotificationRestoreWorkManager OneTimeWorkRequest.Builder(NotificationRestoreWorker::class.java) .setInitialDelay(restoreDelayInSeconds.toLong(), TimeUnit.SECONDS) .build() - WorkManager.getInstance(context!!) + OSWorkManagerHelper.getInstance(context!!) .enqueueUniqueWork( NOTIFICATION_RESTORE_WORKER_IDENTIFIER, ExistingWorkPolicy.KEEP, From 329fb237ed6b27b1c2d26afddd080aa5e0094b1f Mon Sep 17 00:00:00 2001 From: Nan Date: Fri, 12 Apr 2024 10:42:23 -0700 Subject: [PATCH 2/2] add error log and clarify comments --- .../internal/common/OSWorkManagerHelper.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt index d0e259f4f3..55403cf8e7 100644 --- a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt +++ b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/common/OSWorkManagerHelper.kt @@ -5,6 +5,7 @@ import android.content.Context import androidx.work.Configuration import androidx.work.WorkManager import androidx.work.impl.WorkManagerImpl +import com.onesignal.debug.internal.logging.Logging object OSWorkManagerHelper { /** @@ -34,11 +35,15 @@ object OSWorkManagerHelper { fun getInstance(context: Context): WorkManager { if (!isInitialized()) { try { - // Initialization can fail if another thread initializes in the small time gap - // https://android.googlesource.com/platform/frameworks/support/+/60ae0eec2a32396c22ad92502cde952c80d514a0/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java#177 WorkManager.initialize(context, Configuration.Builder().build()) } catch (e: IllegalStateException) { - // Admittedly starting to get hacky + /* + This catch is meant for the exception - + https://android.googlesource.com/platform/frameworks/support/+/60ae0eec2a32396c22ad92502cde952c80d514a0/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java#177 + 1. We lost the race with another call to WorkManager.initialize outside of OneSignal. + 2. It is possible for some other unexpected error is thrown from WorkManager. + */ + Logging.error("OSWorkManagerHelper initializing WorkManager failed: ", e) } } return WorkManager.getInstance(context)