From 237280c7881b163b8c2b8469357ace7e72ccb6b2 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 11:25:17 -0400 Subject: [PATCH 01/14] =?UTF-8?q?Revert=20"Revert=20"Make=20a=20change=20t?= =?UTF-8?q?o=20prevent=20false=20positive=20background=20app=20starts?= =?UTF-8?q?=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3c6bed3e768712ead6ea7c556f68dd9dc4017e0c. --- firebase-perf/CHANGELOG.md | 1 + .../firebase/perf/metrics/AppStartTrace.java | 34 +++++++++++++---- .../perf/metrics/AppStartTraceTest.java | 37 ++++++++++++++++++- 3 files changed, 62 insertions(+), 10 deletions(-) diff --git a/firebase-perf/CHANGELOG.md b/firebase-perf/CHANGELOG.md index b4db8bb6bc4..4954be336b5 100644 --- a/firebase-perf/CHANGELOG.md +++ b/firebase-perf/CHANGELOG.md @@ -1,5 +1,6 @@ # Unreleased * [fixed] Fixed an ANR on app launch. [#4831] +* [fixed] Fixed app start traces on API 34+. [#5920] # 22.0.0 * [changed] **Breaking Change**: Updated minSdkVersion to API level 23 or higher. diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 7574f989d92..ceac4b39335 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -75,6 +75,8 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser private static final @NonNull Timer PERF_CLASS_LOAD_TIME = new Clock().getTime(); private static final long MAX_LATENCY_BEFORE_UI_INIT = TimeUnit.MINUTES.toMicros(1); + private static final long MAX_BACKGROUND_RUNNABLE_DELAY = TimeUnit.MILLISECONDS.toMicros(100); + // Core pool size 0 allows threads to shut down if they're idle private static final int CORE_POOL_SIZE = 0; private static final int MAX_POOL_SIZE = 1; // Only need single thread @@ -111,6 +113,8 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser private final @Nullable Timer processStartTime; private final @Nullable Timer firebaseClassLoadTime; private Timer onCreateTime = null; + + private Timer mainThreadRunnableTime = null; private Timer onStartTime = null; private Timer onResumeTime = null; private Timer firstForegroundTime = null; @@ -319,8 +323,26 @@ private void recordOnDrawFrontOfQueue() { logExperimentTrace(this.experimentTtid); } + private void resolveIsStartedFromBackground() { + // If the mainThreadRunnableTime is null, either the runnable hasn't run, or this check has + // already been made. + if (mainThreadRunnableTime == null) { + return; + } + + // Set it to true if the runnable ran more than 100ms prior to onActivityCreated() + if (mainThreadRunnableTime.getDurationMicros() > MAX_BACKGROUND_RUNNABLE_DELAY) { + isStartedFromBackground = true; + } + + // Set this to null to prevent additional checks if `onActivityCreated()` is called again. + mainThreadRunnableTime = null; + } + @Override public synchronized void onActivityCreated(Activity activity, Bundle savedInstanceState) { + resolveIsStartedFromBackground(); + if (isStartedFromBackground || onCreateTime != null // An activity already called onCreate() ) { return; @@ -560,8 +582,7 @@ public static boolean isScreenOn(Context appContext) { * We use StartFromBackgroundRunnable to detect if app is started from background or foreground. * If app is started from background, we do not generate AppStart trace. This runnable is posted * to main UI thread from FirebasePerfEarly. If app is started from background, this runnable will - * be executed before any activity's onCreate() method. If app is started from foreground, - * activity's onCreate() method is executed before this runnable. + * be executed earlier than 100ms of any activity's onCreate() method. */ public static class StartFromBackgroundRunnable implements Runnable { private final AppStartTrace trace; @@ -572,10 +593,7 @@ public StartFromBackgroundRunnable(final AppStartTrace trace) { @Override public void run() { - // if no activity has ever been created. - if (trace.onCreateTime == null) { - trace.isStartedFromBackground = true; - } + trace.mainThreadRunnableTime = new Timer(); } } @@ -614,7 +632,7 @@ Timer getOnResumeTime() { } @VisibleForTesting - void setIsStartFromBackground() { - isStartedFromBackground = true; + void setMainThreadRunnableTime(Timer timer) { + mainThreadRunnableTime = timer; } } diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java index 36ae3d10116..25ad845231a 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java @@ -18,6 +18,7 @@ import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -238,11 +239,42 @@ public void testDelayedAppStart() { } @Test - public void testStartFromBackground() { + public void testStartFromBackground_within100ms() { FakeScheduledExecutorService fakeExecutorService = new FakeScheduledExecutorService(); + Timer fakeTimer = spy(new Timer(currentTime)); AppStartTrace trace = new AppStartTrace(transportManager, clock, configResolver, fakeExecutorService); - trace.setIsStartFromBackground(); + trace.registerActivityLifecycleCallbacks(appContext); + trace.setMainThreadRunnableTime(fakeTimer); + + when(fakeTimer.getDurationMicros()).thenReturn(99L); + trace.onActivityCreated(activity1, bundle); + Assert.assertNotNull(trace.getOnCreateTime()); + ++currentTime; + trace.onActivityStarted(activity1); + Assert.assertNotNull(trace.getOnStartTime()); + ++currentTime; + trace.onActivityResumed(activity1); + Assert.assertNotNull(trace.getOnResumeTime()); + fakeExecutorService.runAll(); + // There should be a trace sent since the delay between the main thread and onActivityCreated + // is limited. + verify(transportManager, times(1)) + .log( + traceArgumentCaptor.capture(), + ArgumentMatchers.nullable(ApplicationProcessState.class)); + } + + @Test + public void testStartFromBackground_moreThan100ms() { + FakeScheduledExecutorService fakeExecutorService = new FakeScheduledExecutorService(); + Timer fakeTimer = spy(new Timer(currentTime)); + AppStartTrace trace = + new AppStartTrace(transportManager, clock, configResolver, fakeExecutorService); + trace.registerActivityLifecycleCallbacks(appContext); + trace.setMainThreadRunnableTime(fakeTimer); + + when(fakeTimer.getDurationMicros()).thenReturn(TimeUnit.MILLISECONDS.toMicros(100) + 1); trace.onActivityCreated(activity1, bundle); Assert.assertNull(trace.getOnCreateTime()); ++currentTime; @@ -252,6 +284,7 @@ public void testStartFromBackground() { trace.onActivityResumed(activity1); Assert.assertNull(trace.getOnResumeTime()); // There should be no trace sent. + fakeExecutorService.runAll(); verify(transportManager, times(0)) .log( traceArgumentCaptor.capture(), From 1e1d6d34787eb679c3a5dcf104527a61a9eed99f Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 11:31:27 -0400 Subject: [PATCH 02/14] Add back the onCreateTime check in the background runnable --- .../java/com/google/firebase/perf/metrics/AppStartTrace.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index ceac4b39335..936e3f77968 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -593,7 +593,9 @@ public StartFromBackgroundRunnable(final AppStartTrace trace) { @Override public void run() { - trace.mainThreadRunnableTime = new Timer(); + if (trace.onCreateTime == null) { + trace.mainThreadRunnableTime = new Timer(); + } } } From 6861c40e9cc460c47fae1d209473d4ebd23b64b3 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 11:49:58 -0400 Subject: [PATCH 03/14] Add better documentation. --- .../google/firebase/perf/metrics/AppStartTrace.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 936e3f77968..1cfcfe545f8 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -323,6 +323,16 @@ private void recordOnDrawFrontOfQueue() { logExperimentTrace(this.experimentTtid); } + /** + * Sets the `isStartedFromBackground` flag to `true` if the `mainThreadRunnable` time was set + * from the `StartFromBackgroundRunnable` more than 100ms prior to the first time + * `onActivityCreated` was called. + *

+ * If it was called less than 100ms before `onActivityCreated` the assumption is that it was a + * change in order on API 34+ devices where the runnable executes before the activity + * lifecycle callbacks. + * See https://github.com/firebase/firebase-android-sdk/issues/5920. + */ private void resolveIsStartedFromBackground() { // If the mainThreadRunnableTime is null, either the runnable hasn't run, or this check has // already been made. @@ -593,6 +603,7 @@ public StartFromBackgroundRunnable(final AppStartTrace trace) { @Override public void run() { + // Only set the `mainThreadRunnableTime` if `onActivityCreate` has never been called. if (trace.onCreateTime == null) { trace.mainThreadRunnableTime = new Timer(); } From 07bca3753e23b22037afdd9e5ff073ae437c5ce5 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 12:06:32 -0400 Subject: [PATCH 04/14] More documentation --- .../java/com/google/firebase/perf/metrics/AppStartTrace.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 1cfcfe545f8..4e32d292ba0 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -591,8 +591,9 @@ public static boolean isScreenOn(Context appContext) { /** * We use StartFromBackgroundRunnable to detect if app is started from background or foreground. * If app is started from background, we do not generate AppStart trace. This runnable is posted - * to main UI thread from FirebasePerfEarly. If app is started from background, this runnable will - * be executed earlier than 100ms of any activity's onCreate() method. + * to main UI thread from FirebasePerfEarly. If `onActivityCreate` has never been called, we + * record the timestamp - which allows `onActivityCreate` to determine whether it was a background + * app start or not. */ public static class StartFromBackgroundRunnable implements Runnable { private final AppStartTrace trace; From 177bc5dd8f7383c59d1355c6fb06d61881a0f567 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 12:16:12 -0400 Subject: [PATCH 05/14] Add API check and more documentation --- .../firebase/perf/metrics/AppStartTrace.java | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 4e32d292ba0..4b8d67d243d 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -324,13 +324,14 @@ private void recordOnDrawFrontOfQueue() { } /** - * Sets the `isStartedFromBackground` flag to `true` if the `mainThreadRunnable` time was set - * from the `StartFromBackgroundRunnable` more than 100ms prior to the first time - * `onActivityCreated` was called. + * Sets the `isStartedFromBackground` flag to `true` if the `mainThreadRunnableTime` was set + * from the `StartFromBackgroundRunnable`. *

- * If it was called less than 100ms before `onActivityCreated` the assumption is that it was a - * change in order on API 34+ devices where the runnable executes before the activity - * lifecycle callbacks. + * If it's prior to API 34, it's always set to true if `mainThreadRunnableTime` was set. + *

+ * If it's on or after API 34, and it was called less than 100ms before `onActivityCreated`, the + * assumption is that it was called immediately before the activity lifecycle callbacks in a + * foreground start. * See https://github.com/firebase/firebase-android-sdk/issues/5920. */ private void resolveIsStartedFromBackground() { @@ -340,12 +341,17 @@ private void resolveIsStartedFromBackground() { return; } - // Set it to true if the runnable ran more than 100ms prior to onActivityCreated() - if (mainThreadRunnableTime.getDurationMicros() > MAX_BACKGROUND_RUNNABLE_DELAY) { + // If the `minaThreadRunnableTime` was set prior to API 34, it's always assumed that's it's + // a background start. + // Otherwise it's assumed to be a background start if the runnable was set more than 100ms + // before the first `onActivityCreated` call. + // TODO(b/339891952): Investigate removing the API check, and setting a more precise delay. + if ((Build.VERSION.SDK_INT < 34) + || (mainThreadRunnableTime.getDurationMicros() > MAX_BACKGROUND_RUNNABLE_DELAY)) { isStartedFromBackground = true; } - // Set this to null to prevent additional checks if `onActivityCreated()` is called again. + // Set this to null to prevent additional checks. mainThreadRunnableTime = null; } From 5b6ce5df03ccccea112df753a8522d7c6affcc52 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 12:22:18 -0400 Subject: [PATCH 06/14] Update comments --- .../com/google/firebase/perf/metrics/AppStartTrace.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 4b8d67d243d..ac7b9d1253e 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -75,6 +75,8 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser private static final @NonNull Timer PERF_CLASS_LOAD_TIME = new Clock().getTime(); private static final long MAX_LATENCY_BEFORE_UI_INIT = TimeUnit.MINUTES.toMicros(1); + // If the `mainThreadRunnableTime` was set earlier than this this duration, the assumption + // is that it was called immediately before `onActivityCreated` in foreground starts on API 34+. private static final long MAX_BACKGROUND_RUNNABLE_DELAY = TimeUnit.MILLISECONDS.toMicros(100); // Core pool size 0 allows threads to shut down if they're idle @@ -329,7 +331,8 @@ private void recordOnDrawFrontOfQueue() { *

* If it's prior to API 34, it's always set to true if `mainThreadRunnableTime` was set. *

- * If it's on or after API 34, and it was called less than 100ms before `onActivityCreated`, the + * If it's on or after API 34, and it was called less than `MAX_BACKGROUND_RUNNABLE_DELAY` + * before `onActivityCreated`, the * assumption is that it was called immediately before the activity lifecycle callbacks in a * foreground start. * See https://github.com/firebase/firebase-android-sdk/issues/5920. @@ -343,7 +346,8 @@ private void resolveIsStartedFromBackground() { // If the `minaThreadRunnableTime` was set prior to API 34, it's always assumed that's it's // a background start. - // Otherwise it's assumed to be a background start if the runnable was set more than 100ms + // Otherwise it's assumed to be a background start if the runnable was set more than + // `MAX_BACKGROUND_RUNNABLE_DELAY` // before the first `onActivityCreated` call. // TODO(b/339891952): Investigate removing the API check, and setting a more precise delay. if ((Build.VERSION.SDK_INT < 34) From b6b7e2dc71316bb933e57bc9969ad2a68740401a Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 12:28:24 -0400 Subject: [PATCH 07/14] typo --- .../java/com/google/firebase/perf/metrics/AppStartTrace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index ac7b9d1253e..a884331c705 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -344,7 +344,7 @@ private void resolveIsStartedFromBackground() { return; } - // If the `minaThreadRunnableTime` was set prior to API 34, it's always assumed that's it's + // If the `mainThreadRunnableTime` was set prior to API 34, it's always assumed that's it's // a background start. // Otherwise it's assumed to be a background start if the runnable was set more than // `MAX_BACKGROUND_RUNNABLE_DELAY` From a1fe4112a7a6d8cf448d5e29db6b29334cb64a00 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Thu, 21 Aug 2025 12:41:46 -0400 Subject: [PATCH 08/14] clarify --- .../java/com/google/firebase/perf/metrics/AppStartTrace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index a884331c705..1c22e74e900 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -75,7 +75,7 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser private static final @NonNull Timer PERF_CLASS_LOAD_TIME = new Clock().getTime(); private static final long MAX_LATENCY_BEFORE_UI_INIT = TimeUnit.MINUTES.toMicros(1); - // If the `mainThreadRunnableTime` was set earlier than this this duration, the assumption + // If the `mainThreadRunnableTime` was set within this duration, the assumption // is that it was called immediately before `onActivityCreated` in foreground starts on API 34+. private static final long MAX_BACKGROUND_RUNNABLE_DELAY = TimeUnit.MILLISECONDS.toMicros(100); From 8e747297f6c4e333936f91d02da00f11f2a11119 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Tue, 2 Sep 2025 16:48:00 -0400 Subject: [PATCH 09/14] Update threshold to 50ms and update tests. --- .../google/firebase/perf/metrics/AppStartTrace.java | 2 +- .../firebase/perf/metrics/AppStartTraceTest.java | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 1c22e74e900..83b9ff1ed64 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -77,7 +77,7 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser // If the `mainThreadRunnableTime` was set within this duration, the assumption // is that it was called immediately before `onActivityCreated` in foreground starts on API 34+. - private static final long MAX_BACKGROUND_RUNNABLE_DELAY = TimeUnit.MILLISECONDS.toMicros(100); + private static final long MAX_BACKGROUND_RUNNABLE_DELAY = TimeUnit.MILLISECONDS.toMicros(50); // Core pool size 0 allows threads to shut down if they're idle private static final int CORE_POOL_SIZE = 0; diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java index 25ad845231a..daa80ad29a2 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/metrics/AppStartTraceTest.java @@ -239,7 +239,7 @@ public void testDelayedAppStart() { } @Test - public void testStartFromBackground_within100ms() { + public void testStartFromBackground_within50ms() { FakeScheduledExecutorService fakeExecutorService = new FakeScheduledExecutorService(); Timer fakeTimer = spy(new Timer(currentTime)); AppStartTrace trace = @@ -247,7 +247,8 @@ public void testStartFromBackground_within100ms() { trace.registerActivityLifecycleCallbacks(appContext); trace.setMainThreadRunnableTime(fakeTimer); - when(fakeTimer.getDurationMicros()).thenReturn(99L); + // See AppStartTrace.MAX_BACKGROUND_RUNNABLE_DELAY. + when(fakeTimer.getDurationMicros()).thenReturn(TimeUnit.MILLISECONDS.toMicros(50) - 1); trace.onActivityCreated(activity1, bundle); Assert.assertNotNull(trace.getOnCreateTime()); ++currentTime; @@ -266,7 +267,7 @@ public void testStartFromBackground_within100ms() { } @Test - public void testStartFromBackground_moreThan100ms() { + public void testStartFromBackground_moreThan50ms() { FakeScheduledExecutorService fakeExecutorService = new FakeScheduledExecutorService(); Timer fakeTimer = spy(new Timer(currentTime)); AppStartTrace trace = @@ -274,7 +275,8 @@ public void testStartFromBackground_moreThan100ms() { trace.registerActivityLifecycleCallbacks(appContext); trace.setMainThreadRunnableTime(fakeTimer); - when(fakeTimer.getDurationMicros()).thenReturn(TimeUnit.MILLISECONDS.toMicros(100) + 1); + // See AppStartTrace.MAX_BACKGROUND_RUNNABLE_DELAY. + when(fakeTimer.getDurationMicros()).thenReturn(TimeUnit.MILLISECONDS.toMicros(50) + 1); trace.onActivityCreated(activity1, bundle); Assert.assertNull(trace.getOnCreateTime()); ++currentTime; From 817c8b6d5c8b408c9ff5cdfd6be215347ba0f254 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Tue, 2 Sep 2025 16:50:59 -0400 Subject: [PATCH 10/14] Update TODO --- .../java/com/google/firebase/perf/metrics/AppStartTrace.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 83b9ff1ed64..1095bb18edd 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -77,6 +77,7 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser // If the `mainThreadRunnableTime` was set within this duration, the assumption // is that it was called immediately before `onActivityCreated` in foreground starts on API 34+. + // See b/339891952. private static final long MAX_BACKGROUND_RUNNABLE_DELAY = TimeUnit.MILLISECONDS.toMicros(50); // Core pool size 0 allows threads to shut down if they're idle @@ -335,7 +336,7 @@ private void recordOnDrawFrontOfQueue() { * before `onActivityCreated`, the * assumption is that it was called immediately before the activity lifecycle callbacks in a * foreground start. - * See https://github.com/firebase/firebase-android-sdk/issues/5920. + * See b/339891952. */ private void resolveIsStartedFromBackground() { // If the mainThreadRunnableTime is null, either the runnable hasn't run, or this check has @@ -349,7 +350,7 @@ private void resolveIsStartedFromBackground() { // Otherwise it's assumed to be a background start if the runnable was set more than // `MAX_BACKGROUND_RUNNABLE_DELAY` // before the first `onActivityCreated` call. - // TODO(b/339891952): Investigate removing the API check, and setting a more precise delay. + // TODO(b/339891952): Investigate removing the API check. if ((Build.VERSION.SDK_INT < 34) || (mainThreadRunnableTime.getDurationMicros() > MAX_BACKGROUND_RUNNABLE_DELAY)) { isStartedFromBackground = true; From 1e83d89db57c383aedb295fa91c7b6b8110068b6 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Tue, 2 Sep 2025 17:19:57 -0400 Subject: [PATCH 11/14] Add TODO --- .../java/com/google/firebase/perf/metrics/AppStartTrace.java | 1 + 1 file changed, 1 insertion(+) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 1095bb18edd..571011e7ef4 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -117,6 +117,7 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser private final @Nullable Timer firebaseClassLoadTime; private Timer onCreateTime = null; + // TODO(b/339891952): Investigate simplifying Timers in app start trace to use timestamps. private Timer mainThreadRunnableTime = null; private Timer onStartTime = null; private Timer onResumeTime = null; From 458ad871062475d524a47e2526653aa3eeaeba0d Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Tue, 2 Sep 2025 17:29:48 -0400 Subject: [PATCH 12/14] Add changelog --- firebase-perf/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/firebase-perf/CHANGELOG.md b/firebase-perf/CHANGELOG.md index fcbd3dd15b6..f7734040c5a 100644 --- a/firebase-perf/CHANGELOG.md +++ b/firebase-perf/CHANGELOG.md @@ -1,4 +1,5 @@ # Unreleased +- [fixed] Fixed the behavior of app start traces on API 34+ devices. # 22.0.1 From 1b385bcf353ef70e66c27f2ca3ce8c1207bd08c2 Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Tue, 2 Sep 2025 17:30:52 -0400 Subject: [PATCH 13/14] nit --- .../java/com/google/firebase/perf/metrics/AppStartTrace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java index 571011e7ef4..450e6c6e9c8 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/metrics/AppStartTrace.java @@ -117,7 +117,7 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser private final @Nullable Timer firebaseClassLoadTime; private Timer onCreateTime = null; - // TODO(b/339891952): Investigate simplifying Timers in app start trace to use timestamps. + // TODO(b/339891952): Explore simplifying Timers in app start trace to use timestamps. private Timer mainThreadRunnableTime = null; private Timer onStartTime = null; private Timer onResumeTime = null; From 6535a7505848f3a88f82a84a57bee8f179d322cb Mon Sep 17 00:00:00 2001 From: Tejas Deshpande Date: Wed, 3 Sep 2025 11:40:19 -0400 Subject: [PATCH 14/14] style --- firebase-perf/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/firebase-perf/CHANGELOG.md b/firebase-perf/CHANGELOG.md index f7734040c5a..ef26b80a67c 100644 --- a/firebase-perf/CHANGELOG.md +++ b/firebase-perf/CHANGELOG.md @@ -1,4 +1,5 @@ # Unreleased + - [fixed] Fixed the behavior of app start traces on API 34+ devices. # 22.0.1