From fa332172d1f1fd50a8a45e641418b31dbb620c4a Mon Sep 17 00:00:00 2001 From: Josh Kasten Date: Fri, 8 Mar 2024 18:27:41 -0500 Subject: [PATCH] make timeFocusedAtMs thread safe Fixes rare cases where timeFocusedAtMs can become null right after the null check in getTimeFocusedElapsed(). We have seen crashes in the wild so it not just theoretical. --- .../com/onesignal/FocusTimeController.java | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java b/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java index c181d5f0f8..9a160b607c 100644 --- a/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java +++ b/OneSignalSDK/onesignal/src/main/java/com/onesignal/FocusTimeController.java @@ -28,6 +28,8 @@ class FocusTimeController { // Only present if app is currently in focus. private Long timeFocusedAtMs; + private Object timeFocusedAtMsLock = new Object(); + private OSFocusTimeProcessorFactory processorFactory; private OSLogger logger; @@ -42,13 +44,17 @@ private enum FocusEventType { } void appForegrounded() { - timeFocusedAtMs = OneSignal.getTime().getElapsedRealtime(); - logger.debug("Application foregrounded focus time: " + timeFocusedAtMs); + synchronized (timeFocusedAtMsLock) { + timeFocusedAtMs = OneSignal.getTime().getElapsedRealtime(); + logger.debug("Application foregrounded focus time: " + timeFocusedAtMs); + } } void appStopped() { Long timeElapsed = getTimeFocusedElapsed(); - logger.debug("Application stopped focus time: " + timeFocusedAtMs + " timeElapsed: " + timeElapsed); + synchronized (timeFocusedAtMsLock) { + logger.debug("Application stopped focus time: " + timeFocusedAtMs + " timeElapsed: " + timeElapsed); + } if (timeElapsed == null) return; @@ -58,9 +64,11 @@ void appStopped() { } void appBackgrounded() { - logger.debug("Application backgrounded focus time: " + timeFocusedAtMs); - processorFactory.getTimeProcessorSaved().sendUnsentTimeNow(); - timeFocusedAtMs = null; + synchronized (timeFocusedAtMsLock) { + logger.debug("Application backgrounded focus time: " + timeFocusedAtMs); + processorFactory.getTimeProcessorSaved().sendUnsentTimeNow(); + timeFocusedAtMs = null; + } } void doBlockingBackgroundSyncOfUnsentTime() { @@ -91,16 +99,19 @@ private boolean giveProcessorsValidFocusTime(@NonNull List influenc // Get time past since app was put into focus. // Will be null if time is invalid or 0 private @Nullable Long getTimeFocusedElapsed() { - // timeFocusedAtMs is cleared when the app goes into the background so we don't have a focus time - if (timeFocusedAtMs == null) - return null; + synchronized (timeFocusedAtMsLock) { + // timeFocusedAtMs is cleared when the app goes into the background so we don't have a focus time + if (timeFocusedAtMs == null) + return null; + + long timeElapsed = (long) (((OneSignal.getTime().getElapsedRealtime() - timeFocusedAtMs) / 1_000d) + 0.5d); - long timeElapsed = (long)(((OneSignal.getTime().getElapsedRealtime() - timeFocusedAtMs) / 1_000d) + 0.5d); - // Time is invalid if below 1 or over a day - if (timeElapsed < 1 || timeElapsed > 86_400) - return null; - return timeElapsed; + // Time is invalid if below 1 or over a day + if (timeElapsed < 1 || timeElapsed > 86_400) + return null; + return timeElapsed; + } } static class FocusTimeProcessorUnattributed extends FocusTimeProcessorBase {