Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public FirebasePerfEarly(
if (startupTime != null) {
AppStartTrace appStartTrace = AppStartTrace.getInstance();
appStartTrace.registerActivityLifecycleCallbacks(context);
// TODO(b/339891952): Investigate why this runs *before* the activity lifecycle callbacks.
uiExecutor.execute(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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_THREAD_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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -319,9 +323,25 @@ private void recordOnDrawFrontOfQueue() {
logExperimentTrace(this.experimentTtid);
}

private boolean isStartedFromBackground() {
// This a fix for b/339891952 where the runnable on the background thread can run before the
// activity lifecycle callbacks.
if (mainThreadRunnableTime == null) {
return false;
}

if (isStartedFromBackground
&& (mainThreadRunnableTime.getDurationMicros() < MAX_BACKGROUND_THREAD_DELAY)) {
// Reset it to false as it was executed pre-emptively.
isStartedFromBackground = false;
}

return isStartedFromBackground;
}

@Override
public synchronized void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (isStartedFromBackground || onCreateTime != null // An activity already called onCreate()
if (isStartedFromBackground() || onCreateTime != null // An activity already called onCreate()
) {
return;
}
Expand All @@ -337,7 +357,7 @@ public synchronized void onActivityCreated(Activity activity, Bundle savedInstan

@Override
public synchronized void onActivityStarted(Activity activity) {
if (isStartedFromBackground
if (isStartedFromBackground()
|| onStartTime != null // An activity already called onStart()
|| isTooLateToInitUI) {
return;
Expand All @@ -347,7 +367,7 @@ public synchronized void onActivityStarted(Activity activity) {

@Override
public synchronized void onActivityResumed(Activity activity) {
if (isStartedFromBackground || isTooLateToInitUI) {
if (isStartedFromBackground() || isTooLateToInitUI) {
return;
}

Expand Down Expand Up @@ -440,7 +460,7 @@ private void logAppStartTrace() {

@Override
public void onActivityPaused(Activity activity) {
if (isStartedFromBackground
if (isStartedFromBackground()
|| isTooLateToInitUI
|| !configResolver.getIsExperimentTTIDEnabled()) {
return;
Expand All @@ -458,7 +478,7 @@ public void onActivityStopped(Activity activity) {}
@Keep
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppEnteredForeground() {
if (isStartedFromBackground || isTooLateToInitUI || firstForegroundTime != null) {
if (isStartedFromBackground() || isTooLateToInitUI || firstForegroundTime != null) {
return;
}
// firstForeground is equivalent to the first Activity onStart. This marks the beginning of
Expand All @@ -476,7 +496,7 @@ public void onAppEnteredForeground() {
@Keep
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppEnteredBackground() {
if (isStartedFromBackground || isTooLateToInitUI || firstBackgroundTime != null) {
if (isStartedFromBackground() || isTooLateToInitUI || firstBackgroundTime != null) {
return;
}
firstBackgroundTime = clock.getTime();
Expand Down Expand Up @@ -574,6 +594,7 @@ public StartFromBackgroundRunnable(final AppStartTrace trace) {
public void run() {
// if no activity has ever been created.
if (trace.onCreateTime == null) {
trace.mainThreadRunnableTime = new Timer();
trace.isStartedFromBackground = true;
}
}
Expand Down
Loading