@@ -75,6 +75,10 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser
75
75
private static final @ NonNull Timer PERF_CLASS_LOAD_TIME = new Clock ().getTime ();
76
76
private static final long MAX_LATENCY_BEFORE_UI_INIT = TimeUnit .MINUTES .toMicros (1 );
77
77
78
+ // If the `mainThreadRunnableTime` was set within this duration, the assumption
79
+ // is that it was called immediately before `onActivityCreated` in foreground starts on API 34+.
80
+ private static final long MAX_BACKGROUND_RUNNABLE_DELAY = TimeUnit .MILLISECONDS .toMicros (100 );
81
+
78
82
// Core pool size 0 allows threads to shut down if they're idle
79
83
private static final int CORE_POOL_SIZE = 0 ;
80
84
private static final int MAX_POOL_SIZE = 1 ; // Only need single thread
@@ -111,6 +115,8 @@ public class AppStartTrace implements ActivityLifecycleCallbacks, LifecycleObser
111
115
private final @ Nullable Timer processStartTime ;
112
116
private final @ Nullable Timer firebaseClassLoadTime ;
113
117
private Timer onCreateTime = null ;
118
+
119
+ private Timer mainThreadRunnableTime = null ;
114
120
private Timer onStartTime = null ;
115
121
private Timer onResumeTime = null ;
116
122
private Timer firstForegroundTime = null ;
@@ -319,8 +325,44 @@ private void recordOnDrawFrontOfQueue() {
319
325
logExperimentTrace (this .experimentTtid );
320
326
}
321
327
328
+ /**
329
+ * Sets the `isStartedFromBackground` flag to `true` if the `mainThreadRunnableTime` was set
330
+ * from the `StartFromBackgroundRunnable`.
331
+ * <p>
332
+ * If it's prior to API 34, it's always set to true if `mainThreadRunnableTime` was set.
333
+ * <p>
334
+ * If it's on or after API 34, and it was called less than `MAX_BACKGROUND_RUNNABLE_DELAY`
335
+ * before `onActivityCreated`, the
336
+ * assumption is that it was called immediately before the activity lifecycle callbacks in a
337
+ * foreground start.
338
+ * See https://github.com/firebase/firebase-android-sdk/issues/5920.
339
+ */
340
+ private void resolveIsStartedFromBackground () {
341
+ // If the mainThreadRunnableTime is null, either the runnable hasn't run, or this check has
342
+ // already been made.
343
+ if (mainThreadRunnableTime == null ) {
344
+ return ;
345
+ }
346
+
347
+ // If the `mainThreadRunnableTime` was set prior to API 34, it's always assumed that's it's
348
+ // a background start.
349
+ // Otherwise it's assumed to be a background start if the runnable was set more than
350
+ // `MAX_BACKGROUND_RUNNABLE_DELAY`
351
+ // before the first `onActivityCreated` call.
352
+ // TODO(b/339891952): Investigate removing the API check, and setting a more precise delay.
353
+ if ((Build .VERSION .SDK_INT < 34 )
354
+ || (mainThreadRunnableTime .getDurationMicros () > MAX_BACKGROUND_RUNNABLE_DELAY )) {
355
+ isStartedFromBackground = true ;
356
+ }
357
+
358
+ // Set this to null to prevent additional checks.
359
+ mainThreadRunnableTime = null ;
360
+ }
361
+
322
362
@ Override
323
363
public synchronized void onActivityCreated (Activity activity , Bundle savedInstanceState ) {
364
+ resolveIsStartedFromBackground ();
365
+
324
366
if (isStartedFromBackground || onCreateTime != null // An activity already called onCreate()
325
367
) {
326
368
return ;
@@ -559,9 +601,9 @@ public static boolean isScreenOn(Context appContext) {
559
601
/**
560
602
* We use StartFromBackgroundRunnable to detect if app is started from background or foreground.
561
603
* If app is started from background, we do not generate AppStart trace. This runnable is posted
562
- * to main UI thread from FirebasePerfEarly. If app is started from background, this runnable will
563
- * be executed before any activity's onCreate() method. If app is started from foreground,
564
- * activity's onCreate() method is executed before this runnable .
604
+ * to main UI thread from FirebasePerfEarly. If `onActivityCreate` has never been called, we
605
+ * record the timestamp - which allows `onActivityCreate` to determine whether it was a background
606
+ * app start or not .
565
607
*/
566
608
public static class StartFromBackgroundRunnable implements Runnable {
567
609
private final AppStartTrace trace ;
@@ -572,9 +614,9 @@ public StartFromBackgroundRunnable(final AppStartTrace trace) {
572
614
573
615
@ Override
574
616
public void run () {
575
- // if no activity has ever been created .
617
+ // Only set the `mainThreadRunnableTime` if `onActivityCreate` has never been called .
576
618
if (trace .onCreateTime == null ) {
577
- trace .isStartedFromBackground = true ;
619
+ trace .mainThreadRunnableTime = new Timer () ;
578
620
}
579
621
}
580
622
}
@@ -614,7 +656,7 @@ Timer getOnResumeTime() {
614
656
}
615
657
616
658
@ VisibleForTesting
617
- void setIsStartFromBackground ( ) {
618
- isStartedFromBackground = true ;
659
+ void setMainThreadRunnableTime ( Timer timer ) {
660
+ mainThreadRunnableTime = timer ;
619
661
}
620
662
}
0 commit comments