Merge "[Launcher] Fix launcher startup logging" into main
diff --git a/src/com/android/launcher3/logging/StartupLatencyLogger.kt b/src/com/android/launcher3/logging/StartupLatencyLogger.kt
index 93e9de5..7d7564b 100644
--- a/src/com/android/launcher3/logging/StartupLatencyLogger.kt
+++ b/src/com/android/launcher3/logging/StartupLatencyLogger.kt
@@ -30,6 +30,11 @@
     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     var workspaceLoadStartTime: Long = UNSET_LONG
 
+    // StartupLatencyLogger should only send launcher startup logs once in each launcher activity
+    // lifecycle. After launcher activity startup is completed, the logger should be torn down and
+    // reject all logging calls. This flag should be checked at all APIs to prevent logging invalid
+    // startup metrics (such as loading workspace in screen rotation).
+    var isTornDown = false
     private var isInTest = false
 
     /** Subclass can override this method to handle collected latency metrics. */
@@ -45,6 +50,9 @@
     @MainThread
     fun logWorkspaceLoadStartTime(startTimeMs: Long): StartupLatencyLogger {
         Preconditions.assertUIThread()
+        if (isTornDown) {
+            return this
+        }
         workspaceLoadStartTime = startTimeMs
         return this
     }
@@ -56,6 +64,9 @@
     @MainThread
     fun logCardinality(cardinality: Int): StartupLatencyLogger {
         Preconditions.assertUIThread()
+        if (isTornDown) {
+            return this
+        }
         this.cardinality = cardinality
         return this
     }
@@ -67,6 +78,9 @@
     fun logStart(event: LauncherLatencyEvent, startTimeMs: Long): StartupLatencyLogger {
         // In unit test no looper is attached to current thread
         Preconditions.assertUIThread()
+        if (isTornDown) {
+            return this
+        }
         if (validateLoggingEventAtStart(event)) {
             startTimeByEvent.put(event.id, startTimeMs)
         }
@@ -80,6 +94,9 @@
     fun logEnd(event: LauncherLatencyEvent, endTimeMs: Long): StartupLatencyLogger {
         // In unit test no looper is attached to current thread
         Preconditions.assertUIThread()
+        if (isTornDown) {
+            return this
+        }
         maybeLogStartOfWorkspaceLoadTime(event)
         if (validateLoggingEventAtEnd(event)) {
             endTimeByEvent.put(event.id, endTimeMs)
@@ -96,6 +113,7 @@
         endTimeByEvent.clear()
         cardinality = UNSET_INT
         workspaceLoadStartTime = UNSET_LONG
+        isTornDown = true
     }
 
     @MainThread
@@ -181,6 +199,15 @@
                 "Cannot end ${event.name} event after ${LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION.name}",
             )
             return false
+        } else if (
+            latencyType == LatencyType.COLD_DEVICE_REBOOTING &&
+                event == LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC
+        ) {
+            Log.e(
+                TAG,
+                "Cannot have ${LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_WORKSPACE_LOADER_SYNC.name} in ${LatencyType.COLD_DEVICE_REBOOTING.name} startup type"
+            )
+            return false
         }
         return true
     }
diff --git a/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt b/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt
index fffa6d7..a29218c 100644
--- a/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt
+++ b/tests/src/com/android/launcher3/logging/StartupLatencyLoggerTest.kt
@@ -357,6 +357,7 @@
         assertThat(underTest.startTimeByEvent.size()).isEqualTo(4)
         assertThat(underTest.endTimeByEvent.size()).isEqualTo(4)
         assertThat(underTest.cardinality).isEqualTo(235)
+        assertThat(underTest.isTornDown).isFalse()
 
         underTest.reset()
 
@@ -364,5 +365,26 @@
         assertThat(underTest.endTimeByEvent.isEmpty()).isTrue()
         assertThat(underTest.cardinality).isEqualTo(StartupLatencyLogger.UNSET_INT)
         assertThat(underTest.workspaceLoadStartTime).isEqualTo(StartupLatencyLogger.UNSET_LONG)
+        assertThat(underTest.isTornDown).isTrue()
+    }
+
+    @Test
+    @UiThreadTest
+    fun tornDown_rejectLogs() {
+        underTest.reset()
+
+        underTest
+            .logStart(
+                StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+                100
+            )
+            .logEnd(
+                StatsLogManager.LauncherLatencyEvent.LAUNCHER_LATENCY_STARTUP_TOTAL_DURATION,
+                200
+            )
+            .logCardinality(123)
+        assertThat(underTest.startTimeByEvent.isEmpty()).isTrue()
+        assertThat(underTest.endTimeByEvent.isEmpty()).isTrue()
+        assertThat(underTest.cardinality).isEqualTo(StartupLatencyLogger.UNSET_INT)
     }
 }