From fd0440c488208690dea137963a66f6ecad3fa6f7 Mon Sep 17 00:00:00 2001 From: mnardi Date: Thu, 25 May 2023 11:41:27 +0200 Subject: [PATCH] fix: avinsights heartbeats were miscalculated --- ATMobileAnalytics/Tracker/build.gradle | 4 +- .../Tracker/publish-mavencentral.gradle | 4 +- .../java/com/atinternet/tracker/Core.java | 2 +- .../atinternet/tracker/avinsights/Media.java | 65 ++++++++++++++----- 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/ATMobileAnalytics/Tracker/build.gradle b/ATMobileAnalytics/Tracker/build.gradle index 9e451b4d..3eb66b6b 100644 --- a/ATMobileAnalytics/Tracker/build.gradle +++ b/ATMobileAnalytics/Tracker/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'jacoco' apply from: './publish-mavencentral.gradle' group = 'com.atinternet' -version = '2.21.2' +version = '2.21.3' android { compileSdkVersion 30 @@ -182,4 +182,4 @@ task jacocoTestReport(type: JacocoReport, dependsOn: "testReleaseUnitTest") { xml.enabled true html.enabled false } -} \ No newline at end of file +} diff --git a/ATMobileAnalytics/Tracker/publish-mavencentral.gradle b/ATMobileAnalytics/Tracker/publish-mavencentral.gradle index af4b8913..449cf34a 100644 --- a/ATMobileAnalytics/Tracker/publish-mavencentral.gradle +++ b/ATMobileAnalytics/Tracker/publish-mavencentral.gradle @@ -15,7 +15,7 @@ publishing { release(MavenPublication) { groupId 'com.atinternet' artifactId 'Tracker' - version '2.21.2' + version '2.21.3' artifact(project.buildDir.absolutePath + "/outputs/aar/${project.getName()}-release.aar") artifact androidSourcesJar @@ -64,4 +64,4 @@ signing { nexusStaging { packageGroup = 'com.atinternet' -} \ No newline at end of file +} diff --git a/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/Core.java b/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/Core.java index a279c200..1bfd3c5f 100644 --- a/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/Core.java +++ b/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/Core.java @@ -1061,7 +1061,7 @@ class TechnicalContext { static final Closure VTAG = new Closure() { @Override public String execute() { - return "2.21.2"; + return "2.21.3"; } }; diff --git a/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/avinsights/Media.java b/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/avinsights/Media.java index 8d1081f7..6d9da308 100644 --- a/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/avinsights/Media.java +++ b/ATMobileAnalytics/Tracker/src/main/java/com/atinternet/tracker/avinsights/Media.java @@ -8,6 +8,7 @@ import com.atinternet.tracker.RequiredPropertiesDataObject; import com.atinternet.tracker.Utility; +import java.util.Calendar; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -31,6 +32,8 @@ public class Media extends RequiredPropertiesDataObject { private String sessionId; private String previousEvent = ""; + private int previousHeartbeatDelay = 0; + private int previousBufferHeartbeatDelay = 0; private int previousCursorPositionMillis = 0; private int currentCursorPositionMillis = 0; private long eventDurationMillis = 0; @@ -53,6 +56,8 @@ public Media(Events events) { heartbeatRunnable = new HeartbeatRunnable(this); bufferHeartbeatRunnable = new BufferHeartbeatRunnable(this); rebufferHeartbeatRunnable = new RebufferHeartbeatRunnable(this); + + this.sessionId = UUID.randomUUID().toString(); } public Media(Events events, int heartbeat, int bufferHeartbeat, String sessionId) { @@ -73,7 +78,9 @@ public Media(Events events, SparseIntArray heartbeat, SparseIntArray bufferHeart this(events); setHeartbeat(createHeartbeatStages(MIN_HEARTBEAT_DURATION)); setBufferHeartbeat(createHeartbeatStages(MIN_BUFFER_HEARTBEAT_DURATION)); - this.sessionId = TextUtils.isEmpty(sessionId) ? UUID.randomUUID().toString() : sessionId; + if (!TextUtils.isEmpty(sessionId)) { + this.sessionId = sessionId; + } } /*** @@ -201,8 +208,7 @@ public synchronized void setPlaybackSpeed(double playbackSpeed) { heartbeat(-1, null); if (autoHeartbeat) { - int diffMin = (int) ((Utility.currentTimeMillis() - startSessionTimeMillis) / 60_000); - heartbeatExecutor.schedule(heartbeatRunnable, heartbeatDurations.get(diffMin, MIN_HEARTBEAT_DURATION), TimeUnit.SECONDS); + this.previousHeartbeatDelay = updateHeartbeatRunnable(this.previousHeartbeatDelay, startSessionTimeMillis, MIN_HEARTBEAT_DURATION, heartbeatDurations, heartbeatRunnable); } this.playbackSpeed = playbackSpeed; } @@ -266,15 +272,13 @@ public synchronized void bufferStart(int cursorPosition, Map ext if (isPlaybackActivated) { if (autoBufferHeartbeat) { bufferTimeMillis = bufferTimeMillis == 0 ? Utility.currentTimeMillis() : bufferTimeMillis; - int diffMin = (int) ((Utility.currentTimeMillis() - bufferTimeMillis) / 60_000); - heartbeatExecutor.schedule(rebufferHeartbeatRunnable, bufferHeartbeatDurations.get(diffMin, MIN_BUFFER_HEARTBEAT_DURATION), TimeUnit.SECONDS); + this.previousBufferHeartbeatDelay = updateHeartbeatRunnable(this.previousBufferHeartbeatDelay, bufferTimeMillis, MIN_BUFFER_HEARTBEAT_DURATION, bufferHeartbeatDurations, rebufferHeartbeatRunnable); } sendEvents(createEvent("av.rebuffer.start", true, extraProps)); } else { if (autoBufferHeartbeat) { bufferTimeMillis = bufferTimeMillis == 0 ? Utility.currentTimeMillis() : bufferTimeMillis; - int diffMin = (int) ((Utility.currentTimeMillis() - bufferTimeMillis) / 60_000); - heartbeatExecutor.schedule(bufferHeartbeatRunnable, bufferHeartbeatDurations.get(diffMin, MIN_BUFFER_HEARTBEAT_DURATION), TimeUnit.SECONDS); + this.previousBufferHeartbeatDelay = updateHeartbeatRunnable(this.previousBufferHeartbeatDelay, bufferTimeMillis, MIN_BUFFER_HEARTBEAT_DURATION, bufferHeartbeatDurations, bufferHeartbeatRunnable); } sendEvents(createEvent("av.buffer.start", true, extraProps)); } @@ -298,8 +302,7 @@ public synchronized void playbackStart(int cursorPosition, Map e stopHeartbeatService(); if (autoHeartbeat) { - int diffMin = (int) ((Utility.currentTimeMillis() - startSessionTimeMillis) / 60_000); - heartbeatExecutor.schedule(heartbeatRunnable, heartbeatDurations.get(diffMin, MIN_HEARTBEAT_DURATION), TimeUnit.SECONDS); + this.previousHeartbeatDelay = updateHeartbeatRunnable(this.previousHeartbeatDelay, startSessionTimeMillis, MIN_HEARTBEAT_DURATION, heartbeatDurations, heartbeatRunnable); } sendEvents(createEvent("av.start", true, extraProps)); @@ -322,8 +325,7 @@ public synchronized void playbackResumed(int cursorPosition, Map stopHeartbeatService(); if (autoHeartbeat) { - int diffMin = (int) ((Utility.currentTimeMillis() - startSessionTimeMillis) / 60_000); - heartbeatExecutor.schedule(heartbeatRunnable, heartbeatDurations.get(diffMin, MIN_HEARTBEAT_DURATION), TimeUnit.SECONDS); + this.previousHeartbeatDelay = updateHeartbeatRunnable(this.previousHeartbeatDelay, startSessionTimeMillis, MIN_HEARTBEAT_DURATION, heartbeatDurations, heartbeatRunnable); } sendEvents(createEvent("av.resume", true, extraProps)); @@ -510,6 +512,13 @@ public void share(Map extraProps) { sendEvents(createEvent("av.share", false, extraProps)); } + private int updateHeartbeatRunnable(int previousHeartbeatDelay, long startTimerMillis, int MIN_HEARTBEAT_DURATION, SparseIntArray heartbeatDurations, AVRunnable heartbeatRunnable) { + int minutesDelay = (int) ((currentTimeMillis() - startTimerMillis) / 60_000); + int heartbeatDelay = Math.max(heartbeatDurations.get(minutesDelay, previousHeartbeatDelay), MIN_HEARTBEAT_DURATION); + heartbeatExecutor.schedule(heartbeatRunnable, heartbeatDelay, TimeUnit.SECONDS); + return heartbeatDelay; + } + synchronized void processHeartbeat(int cursorPosition, boolean fromAuto, Map extraProps) { startSessionTimeMillis = startSessionTimeMillis == 0 ? Utility.currentTimeMillis() : startSessionTimeMillis; @@ -523,8 +532,7 @@ synchronized void processHeartbeat(int cursorPosition, boolean fromAuto, Map e if (fromAuto) { bufferTimeMillis = bufferTimeMillis == 0 ? Utility.currentTimeMillis() : bufferTimeMillis; - int diffMin = (int) ((Utility.currentTimeMillis() - bufferTimeMillis) / 60_000); - heartbeatExecutor.schedule(bufferHeartbeatRunnable, bufferHeartbeatDurations.get(diffMin, MIN_BUFFER_HEARTBEAT_DURATION), TimeUnit.SECONDS); + this.previousBufferHeartbeatDelay = updateHeartbeatRunnable(this.previousBufferHeartbeatDelay, bufferTimeMillis, MIN_BUFFER_HEARTBEAT_DURATION, bufferHeartbeatDurations, bufferHeartbeatRunnable); } sendEvents(createEvent("av.buffer.heartbeat", true, extraProps)); } @@ -552,8 +559,7 @@ synchronized void processRebufferHeartbeat(boolean fromAuto, Map if (fromAuto) { bufferTimeMillis = bufferTimeMillis == 0 ? Utility.currentTimeMillis() : bufferTimeMillis; - int diffMin = (int) ((Utility.currentTimeMillis() - bufferTimeMillis) / 60_000); - heartbeatExecutor.schedule(rebufferHeartbeatRunnable, bufferHeartbeatDurations.get(diffMin, MIN_BUFFER_HEARTBEAT_DURATION), TimeUnit.SECONDS); + this.previousBufferHeartbeatDelay = updateHeartbeatRunnable(this.previousBufferHeartbeatDelay, bufferTimeMillis, MIN_BUFFER_HEARTBEAT_DURATION, bufferHeartbeatDurations, rebufferHeartbeatRunnable); } sendEvents(createEvent("av.rebuffer.heartbeat", true, extraProps)); @@ -581,6 +587,29 @@ private synchronized void processSeek(String seekDirection, int oldCursorPositio sendEvents(seekStart, createEvent("av." + seekDirection, true, extraProps)); } + private long currentTimeMillis() { + long timeMillis; + int year; + int retry = 3; + do { + retry--; + timeMillis = System.currentTimeMillis(); + + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(timeMillis); + year = cal.get(Calendar.YEAR); + if (year < 2000) { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } while (year < 2000 && retry > 0); + + return timeMillis; + } + private synchronized Event createEvent(String name, boolean withOptions, Map extraProps) { Map props = new HashMap<>(this.getProps()); if (withOptions) { @@ -627,4 +656,4 @@ private void resetState() { currentCursorPositionMillis = 0; eventDurationMillis = 0; } -} +} \ No newline at end of file