diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java index a0487253ed..cffbdb1dd0 100644 --- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java +++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/application/MainApplication.java @@ -131,6 +131,9 @@ public void onUserStateChange(@NonNull UserChangedState state) { OneSignal.addUserJwtInvalidatedListner(new IUserJwtInvalidatedListener() { @Override public void onUserJwtInvalidated(@NonNull UserJwtInvalidatedEvent event) { + // !!! For manual testing only + String jwt = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiIxNjg4ZDhmMi1kYTdmLTQ4MTUtOGVlMy05ZDEzNzg4NDgyYzgiLCJpYXQiOjE3MTgzMDk5NzIsImlkZW50aXR5Ijp7ImV4dGVybmFsX2lkIjoiYWxleC0wNjE0Iiwib25lc2lnbmFsX2lkIjoiYTViYjc4NDYtYzExNC00YzdkLTkzMWYtNGQ0NjhiMGE5OWJhIn0sInN1YnNjcmlwdGlvbnMiOlt7InR5cGUiOiJFbWFpbCIsInRva2VuIjoidGVzdEBkb21haW4uY29tIn0seyJpZCI6ImE2YzQxNmY3LTMxMGUtNDgzNi05Yjc4LWZiZmQ5NTgyNWNjNCJ9XX0.HsjsA2qNPwd9qov_8Px01km-dzRug-YKNNG85cMrGYI9Pdb2uoPQSdAN3Uqu7_o4pL8FRxXliYJrC52-9wH3FQ"; + OneSignal.updateUserJwt(event.getExternalId(), jwt); Log.v(Tag.LOG_TAG, "onUserJwtInvalidated fired with ID:" + event.getExternalId()); } }); diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt index 1f62cc556b..538d403d2c 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt @@ -1,6 +1,8 @@ package com.onesignal.core.internal.config +import com.onesignal.common.events.EventProducer import com.onesignal.common.modeling.Model +import com.onesignal.core.internal.backend.ParamsObject import org.json.JSONArray import org.json.JSONObject @@ -319,6 +321,20 @@ class ConfigModel : Model() { return null } + + var fetchParamsNotifier = EventProducer() + + fun addFetchParamsObserver(observer: FetchParamsObserver) { + fetchParamsNotifier.subscribe(observer) + } + + fun removeFetchParamsObserver(observer: FetchParamsObserver) { + fetchParamsNotifier.unsubscribe(observer) + } + + fun notifyFetchParams(params: ParamsObject) { + fetchParamsNotifier.fire { it.onParamsFetched(params) } + } } /** @@ -425,3 +441,7 @@ class FCMConfigModel(parentModel: Model, parentProperty: String) : Model(parentM setOptStringProperty(::apiKey.name, value) } } + +interface FetchParamsObserver { + fun onParamsFetched(params: ParamsObject) +} diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt index 87d7eae6b0..8622e05c90 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt @@ -72,7 +72,7 @@ internal class ConfigModelStoreListener( // copy current model into new model, then override with what comes down. val config = ConfigModel() config.initializeFromModel(null, _configModelStore.model) - + config.fetchParamsNotifier = _configModelStore.model.fetchParamsNotifier config.isInitializedWithRemote = true // these are always copied from the backend params @@ -105,6 +105,7 @@ internal class ConfigModelStoreListener( _configModelStore.replace(config, ModelChangeTags.HYDRATE) success = true + config.notifyFetchParams(params) } catch (ex: BackendException) { if (ex.statusCode == HttpURLConnection.HTTP_FORBIDDEN) { Logging.fatal("403 error getting OneSignal params, omitting further retries!") diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt index d2dceea5c3..3ab6059605 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/IOperationRepo.kt @@ -42,6 +42,8 @@ interface IOperationRepo { suspend fun awaitInitialized() fun forceExecuteOperations() + + fun setPaused(paused: Boolean) } // Extension function so the syntax containsInstanceOf() can be used over diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt index 7ed6336d4e..c033e56099 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt @@ -189,6 +189,10 @@ internal class OperationRepo( waiter.wake(LoopWaiterMessage(false)) } + override fun setPaused(paused: Boolean) { + this.paused = paused + } + /** * Waits until a new operation is enqueued, then wait an additional * amount of time afterwards, so operations can be grouped/batched. @@ -262,7 +266,15 @@ internal class OperationRepo( ops.forEach { _operationModelStore.remove(it.operation.id) } ops.forEach { it.waiter?.wake(true) } } - ExecutionResult.FAIL_UNAUTHORIZED, // TODO: Need to provide callback for app to reset JWT. For now, fail with no retry. + ExecutionResult.FAIL_UNAUTHORIZED -> { + Logging.error("Operation execution failed with invalid jwt, pausing the operation repo: $operations") + // keep the failed operation and pause the operation repo from executing + paused = true + // add back all operations to the front of the queue to be re-executed. + synchronized(queue) { + ops.reversed().forEach { queue.add(0, it) } + } + } ExecutionResult.FAIL_NORETRY, ExecutionResult.FAIL_CONFLICT, -> { 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 f35f198183..d81d8be50b 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 @@ -19,8 +19,10 @@ import com.onesignal.common.threading.suspendifyOnThread import com.onesignal.core.CoreModule import com.onesignal.core.internal.application.IApplicationService import com.onesignal.core.internal.application.impl.ApplicationService +import com.onesignal.core.internal.backend.ParamsObject import com.onesignal.core.internal.config.ConfigModel import com.onesignal.core.internal.config.ConfigModelStore +import com.onesignal.core.internal.config.FetchParamsObserver import com.onesignal.core.internal.operations.IOperationRepo import com.onesignal.core.internal.preferences.IPreferencesService import com.onesignal.core.internal.preferences.PreferenceOneSignalKeys @@ -74,7 +76,7 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { val oldValue = _consentGiven _consentGiven = value configModel?.consentGiven = value - if (oldValue != value && value) { + if (oldValue != value && value && !useIdentityVerification) { operationRepo?.forceExecuteOperations() } } @@ -141,7 +143,6 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { private var sessionModel: SessionModel? = null private var _consentRequired: Boolean? = null private var _consentGiven: Boolean? = null - private var _useIdentityVerification: Boolean? = false private var _disableGMSMissingPrompt: Boolean? = null private val initLock: Any = Any() private val loginLock: Any = Any() @@ -255,6 +256,7 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { startupService = services.getService() startupService!!.bootstrap() + resumeOperationRepoAfterFetchParams(configModel!!) if (forceCreateUser || !identityModelStore!!.model.hasProperty(IdentityConstants.ONESIGNAL_ID)) { val legacyPlayerId = preferencesService!!.getString( @@ -437,13 +439,18 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { externalId: String, token: String, ) { - if (!identityModelStore!!.model.externalId.equals(externalId)) { - Logging.log(LogLevel.DEBUG, "JWT $token is NOT updated for externalId $externalId") - return + // update the model with the given externalId + for (model in identityModelStore!!.store.list()) { + if (externalId == model.externalId) { + identityModelStore!!.model.jwtToken = token + operationRepo!!.setPaused(false) + operationRepo!!.forceExecuteOperations() + Logging.log(LogLevel.DEBUG, "JWT $token is updated for externalId $externalId") + return + } } - Logging.log(LogLevel.DEBUG, "JWT $token is updated for externalId $externalId") - identityModelStore!!.model.jwtToken = token + Logging.log(LogLevel.DEBUG, "No identity found for externalId $externalId") } override fun addUserJwtInvalidatedListener(listener: IUserJwtInvalidatedListener) { @@ -519,6 +526,23 @@ internal class OneSignalImp : IOneSignal, IServiceProvider { } } + private fun resumeOperationRepoAfterFetchParams(configModel: ConfigModel) { + // pause operation repo until useIdentityVerification is determined + operationRepo!!.setPaused(true) + configModel.addFetchParamsObserver( + object : FetchParamsObserver { + override fun onParamsFetched(params: ParamsObject) { + // resume operations if identity verification is turned off or a jwt is cached + if (params.useIdentityVerification == false || identityModelStore!!.model.jwtToken != null) { + operationRepo!!.setPaused(false) + } else { + Logging.log(LogLevel.ERROR, "A valid JWT is required for user ${identityModelStore!!.model.externalId}.") + } + } + }, + ) + } + override fun hasService(c: Class): Boolean = services.hasService(c) override fun getService(c: Class): T = services.getService(c) diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt index 7d5df5418e..70a5015f2e 100644 --- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt +++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/identity/IdentityModel.kt @@ -1,6 +1,7 @@ package com.onesignal.user.internal.identity import com.onesignal.common.modeling.MapModel +import com.onesignal.common.modeling.ModelChangeTags import com.onesignal.user.internal.backend.IdentityConstants /** @@ -37,6 +38,6 @@ class IdentityModel : MapModel() { var jwtToken: String? get() = getOptStringProperty(IdentityConstants.JWT_TOKEN) set(value) { - setOptStringProperty(IdentityConstants.JWT_TOKEN, value) + setOptStringProperty(IdentityConstants.JWT_TOKEN, value, ModelChangeTags.NO_PROPOGATE) } }