diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/IApplicationService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/IApplicationService.kt index ed27e918f6..14bc363a81 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/IApplicationService.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/IApplicationService.kt @@ -24,6 +24,13 @@ interface IApplicationService { */ val isInForeground: Boolean + /** + * Whether the device storage for the user data is accessible. + * + * Always return true for API 23 and below as the device booted into an unlocked state by default + */ + val isDeviceStorageUnlocked: Boolean + /** * How the application was entered. This is writeable to allow for the setting * to [AppEntryAction.NOTIFICATION_CLICK] when it is determined a notification diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/impl/ApplicationService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/impl/ApplicationService.kt index 55f2612324..922144c645 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/impl/ApplicationService.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/application/impl/ApplicationService.kt @@ -7,7 +7,9 @@ import android.content.ComponentCallbacks import android.content.Context import android.content.pm.ActivityInfo import android.content.res.Configuration +import android.os.Build import android.os.Bundle +import android.os.UserManager import android.view.ViewTreeObserver.OnGlobalLayoutListener import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment @@ -70,6 +72,25 @@ class ApplicationService() : IApplicationService, ActivityLifecycleCallbacks, On private val wasInBackground: Boolean get() = !isInForeground || nextResumeIsFirstActivity + /** + * Retrieve whether the user data is accessible. + * + * On Android 7.0+ (API 24+), encrypted user data is inaccessible until the user unlocks the device for the first time after boot. + * This includes: + * * getSharedPreferences() + * * Any file-based storage in the default credential-encrypted context + * + * Apps that auto-run on boot or background services triggered early may hit this issue. + */ + override val isDeviceStorageUnlocked: Boolean + get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val userManager = appContext.getSystemService(Context.USER_SERVICE) as UserManager + userManager.isUserUnlocked + } else { + // Prior to API 24, the device booted into an unlocked state by default + true + } + /** * Call to "start" this service, expected to be called during initialization of the SDK. * 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 d5a74cd648..5cfc5733c4 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 @@ -197,11 +197,16 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { PreferenceStoreFix.ensureNoObfuscatedPrefStore(context) + val applicationService = services.getService() + // start the application service. This is called explicitly first because we want // to make sure it has the context provided on input, for all other startable services // to depend on if needed. - val applicationService = services.getService() (applicationService as ApplicationService).start(context) + if (!applicationService.isDeviceStorageUnlocked) { + Logging.warn("initWithContext called when device storage is locked, no user data is accessible!") + return false + } // Give the logging singleton access to the application service to support visual logging. Logging.applicationService = applicationService