diff --git a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java index 8fc7767388e..ee16ff412e6 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -50,6 +50,7 @@ import com.google.firebase.heartbeatinfo.DefaultHeartBeatController; import com.google.firebase.inject.Provider; import com.google.firebase.internal.DataCollectionConfigStorage; +import com.google.firebase.provider.FirebaseInitProvider; import com.google.firebase.tracing.ComponentMonitor; import com.google.firebase.tracing.FirebaseTrace; import java.nio.charset.Charset; @@ -408,6 +409,7 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o this.applicationContext = Preconditions.checkNotNull(applicationContext); this.name = Preconditions.checkNotEmpty(name); this.options = Preconditions.checkNotNull(options); + StartupTime startupTime = FirebaseInitProvider.getStartupTime(); FirebaseTrace.pushTrace("Firebase"); @@ -418,7 +420,7 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o FirebaseTrace.popTrace(); // ComponentDiscovery FirebaseTrace.pushTrace("Runtime"); - componentRuntime = + ComponentRuntime.Builder builder = ComponentRuntime.builder(com.google.firebase.concurrent.UiExecutor.INSTANCE) .addLazyComponentRegistrars(registrars) .addComponentRegistrar(new FirebaseCommonRegistrar()) @@ -426,8 +428,15 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o .addComponent(Component.of(applicationContext, Context.class)) .addComponent(Component.of(this, FirebaseApp.class)) .addComponent(Component.of(options, FirebaseOptions.class)) - .setProcessor(new ComponentMonitor()) - .build(); + .setProcessor(new ComponentMonitor()); + + // Don't provide StartupTime in direct boot mode or if Firebase was manually started + if (UserManagerCompat.isUserUnlocked(applicationContext) + && FirebaseInitProvider.isCurrentlyInitializing()) { + builder.addComponent(Component.of(startupTime, StartupTime.class)); + } + + componentRuntime = builder.build(); FirebaseTrace.popTrace(); // Runtime dataCollectionConfigStorage = diff --git a/firebase-common/src/main/java/com/google/firebase/StartupTime.java b/firebase-common/src/main/java/com/google/firebase/StartupTime.java new file mode 100644 index 00000000000..972b0ed3c2c --- /dev/null +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -0,0 +1,56 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase; + +import android.os.SystemClock; +import androidx.annotation.NonNull; +import com.google.auto.value.AutoValue; + +/** + * Represents Firebase's startup time in several timing methods. Represented in unix time/epoch + * milliseconds milliseconds since boot, and uptime milliseconds. The absence of a StartupTime + * indicates an unreliable or misleading time, such as a launch in direct boot mode. Because of + * this, StartupTime cannot be guaranteed to be present, and instead should be optionally depended + * on, and its absence handled. + * + * @hide + */ +@AutoValue +public abstract class StartupTime { + + /** @return The epoch time that Firebase began initializing, in milliseconds */ + public abstract long getEpochMillis(); + + /** @return The number of milliseconds from boot to when Firebase began initializing */ + public abstract long getElapsedRealtime(); + + /** @return The number of milliseconds of uptime measured by SystemClock.uptimeMillis() */ + public abstract long getUptimeMillis(); + + /** + * @param epochMillis Time in milliseconds since epoch + * @param elapsedRealtime Time in milliseconds since boot + */ + public static @NonNull StartupTime create( + long epochMillis, long elapsedRealtime, long uptimeMillis) { + return new AutoValue_StartupTime(epochMillis, elapsedRealtime, uptimeMillis); + } + + /** @return A StartupTime represented by the current epoch time and JVM nano time */ + public static @NonNull StartupTime now() { + return create( + System.currentTimeMillis(), SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); + } +} diff --git a/firebase-common/src/main/java/com/google/firebase/provider/FirebaseInitProvider.java b/firebase-common/src/main/java/com/google/firebase/provider/FirebaseInitProvider.java index e4db850fa63..a43c884ed1d 100644 --- a/firebase-common/src/main/java/com/google/firebase/provider/FirebaseInitProvider.java +++ b/firebase-common/src/main/java/com/google/firebase/provider/FirebaseInitProvider.java @@ -27,11 +27,24 @@ import androidx.annotation.VisibleForTesting; import com.google.android.gms.common.internal.Preconditions; import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; +import java.util.concurrent.atomic.AtomicBoolean; /** Initializes Firebase APIs at app startup time. */ public class FirebaseInitProvider extends ContentProvider { - private static final String TAG = "FirebaseInitProvider"; + @Nullable private static StartupTime startupTime = StartupTime.now(); + @NonNull private static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); + + /** @hide */ + public static @Nullable StartupTime getStartupTime() { + return startupTime; + } + + /** @hide */ + public static boolean isCurrentlyInitializing() { + return currentlyInitializing.get(); + } /** Should match the {@link FirebaseInitProvider} authority if $androidId is empty. */ @VisibleForTesting @@ -48,12 +61,17 @@ public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) { /** Called before {@link Application#onCreate()}. */ @Override public boolean onCreate() { - if (FirebaseApp.initializeApp(getContext()) == null) { - Log.i(TAG, "FirebaseApp initialization unsuccessful"); - } else { - Log.i(TAG, "FirebaseApp initialization successful"); + try { + currentlyInitializing.set(true); + if (FirebaseApp.initializeApp(getContext()) == null) { + Log.i(TAG, "FirebaseApp initialization unsuccessful"); + } else { + Log.i(TAG, "FirebaseApp initialization successful"); + } + return false; + } finally { + currentlyInitializing.set(false); } - return false; } /** diff --git a/firebase-perf/src/main/AndroidManifest.xml b/firebase-perf/src/main/AndroidManifest.xml index 48ae28f8c6e..c40a7de8422 100644 --- a/firebase-perf/src/main/AndroidManifest.xml +++ b/firebase-perf/src/main/AndroidManifest.xml @@ -6,11 +6,6 @@ - Responsible for initializing the AppStartTrace, and early initialization of ConfigResolver + */ +public class FirebasePerfEarly { + @NonNull private final Handler mainHandler = new Handler(Looper.getMainLooper()); + + public FirebasePerfEarly(@NonNull FirebaseApp app, @Nullable StartupTime startupTime) { + Context context = app.getApplicationContext(); + + // Initialize ConfigResolver early for accessing device caching layer. + ConfigResolver configResolver = ConfigResolver.getInstance(); + configResolver.setApplicationContext(context); + + AppStateMonitor appStateMonitor = AppStateMonitor.getInstance(); + appStateMonitor.registerActivityLifecycleCallbacks(context); + appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer()); + + if (startupTime != null) { + AppStartTrace appStartTrace = AppStartTrace.getInstance(); + appStartTrace.registerActivityLifecycleCallbacks(context); + mainHandler.post(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace)); + } + + // In the case of cold start, we create a session and start collecting gauges as early as + // possible. + // There is code in SessionManager that prevents us from resetting the session twice in case + // of app cold start. + SessionManager.getInstance().initializeGaugeCollection(); + } +} diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java index 3dc56578866..335dc7b33f0 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java @@ -17,6 +17,7 @@ import androidx.annotation.Keep; import com.google.android.datatransport.TransportFactory; import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; import com.google.firebase.components.Component; import com.google.firebase.components.ComponentContainer; import com.google.firebase.components.ComponentRegistrar; @@ -41,6 +42,7 @@ @Keep public class FirebasePerfRegistrar implements ComponentRegistrar { private static final String LIBRARY_NAME = "fire-perf"; + private static final String EARLY_LIBRARY_NAME = "fire-perf-early"; @Override @Keep @@ -52,8 +54,16 @@ public List> getComponents() { .add(Dependency.requiredProvider(RemoteConfigComponent.class)) .add(Dependency.required(FirebaseInstallationsApi.class)) .add(Dependency.requiredProvider(TransportFactory.class)) + .add(Dependency.required(FirebasePerfEarly.class)) .factory(FirebasePerfRegistrar::providesFirebasePerformance) .build(), + Component.builder(FirebasePerfEarly.class) + .name(EARLY_LIBRARY_NAME) + .add(Dependency.required(FirebaseApp.class)) + .add(Dependency.optionalProvider(StartupTime.class)) + .eagerInDefaultApp() + .factory(FirebasePerfRegistrar::providesFirebasePerformanceEarly) + .build(), /** * Fireperf SDK is lazily by {@link FirebasePerformanceInitializer} during {@link * com.google.firebase.perf.application.AppStateMonitor#onActivityResumed(Activity)}. we use @@ -65,6 +75,8 @@ public List> getComponents() { } private static FirebasePerformance providesFirebasePerformance(ComponentContainer container) { + // Ensure FirebasePerfEarly was initialized + container.get(FirebasePerfEarly.class); FirebasePerformanceComponent component = DaggerFirebasePerformanceComponent.builder() .firebasePerformanceModule( @@ -77,4 +89,9 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine return component.getFirebasePerformance(); } + + private static FirebasePerfEarly providesFirebasePerformanceEarly(ComponentContainer container) { + return new FirebasePerfEarly( + container.get(FirebaseApp.class), container.getProvider(StartupTime.class).get()); + } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/config/RemoteConfigManager.java b/firebase-perf/src/main/java/com/google/firebase/perf/config/RemoteConfigManager.java index 06c505ddd51..d4166dd1c70 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/config/RemoteConfigManager.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/config/RemoteConfigManager.java @@ -23,9 +23,10 @@ import androidx.annotation.Keep; import androidx.annotation.Nullable; import com.google.android.gms.common.util.VisibleForTesting; +import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; import com.google.firebase.inject.Provider; import com.google.firebase.perf.logging.AndroidLogger; -import com.google.firebase.perf.provider.FirebasePerfProvider; import com.google.firebase.perf.util.Optional; import com.google.firebase.remoteconfig.FirebaseRemoteConfig; import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue; @@ -80,7 +81,19 @@ private RemoteConfigManager() { new LinkedBlockingQueue()), /* firebaseRemoteConfig= */ null, // set once FirebaseRemoteConfig is initialized MIN_APP_START_CONFIG_FETCH_DELAY_MS - + new Random().nextInt(RANDOM_APP_START_CONFIG_FETCH_DELAY_MS)); + + new Random().nextInt(RANDOM_APP_START_CONFIG_FETCH_DELAY_MS), + getInitialStartupMillis()); + } + + @VisibleForTesting + @SuppressWarnings("FirebaseUseExplicitDependencies") + static long getInitialStartupMillis() { + StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); + if (startupTime != null) { + return startupTime.getEpochMillis(); + } else { + return System.currentTimeMillis(); + } } @VisibleForTesting @@ -88,7 +101,8 @@ private RemoteConfigManager() { DeviceCacheManager cache, Executor executor, FirebaseRemoteConfig firebaseRemoteConfig, - long appStartConfigFetchDelayInMs) { + long appStartConfigFetchDelayInMs, + long appStartTimeInMs) { this.cache = cache; this.executor = executor; this.firebaseRemoteConfig = firebaseRemoteConfig; @@ -96,8 +110,7 @@ private RemoteConfigManager() { firebaseRemoteConfig == null ? new ConcurrentHashMap<>() : new ConcurrentHashMap<>(firebaseRemoteConfig.getAll()); - this.appStartTimeInMs = - TimeUnit.MICROSECONDS.toMillis(FirebasePerfProvider.getAppStartTime().getMicros()); + this.appStartTimeInMs = appStartTimeInMs; this.appStartConfigFetchDelayInMs = appStartConfigFetchDelayInMs; } 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 5463ed04dd9..2d91f2e2262 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 @@ -27,9 +27,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.gms.common.util.VisibleForTesting; +import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; import com.google.firebase.perf.config.ConfigResolver; import com.google.firebase.perf.logging.AndroidLogger; -import com.google.firebase.perf.provider.FirebasePerfProvider; import com.google.firebase.perf.session.PerfSession; import com.google.firebase.perf.session.SessionManager; import com.google.firebase.perf.transport.TransportManager; @@ -94,6 +95,8 @@ public class AppStartTrace implements ActivityLifecycleCallbacks { */ private boolean isTooLateToInitUI = false; + private static Timer firebaseStartupTime = null; + private Timer appStartTime = null; private Timer onCreateTime = null; private Timer onStartTime = null; @@ -160,6 +163,7 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) return instance; } + @SuppressWarnings("FirebaseUseExplicitDependencies") AppStartTrace( @NonNull TransportManager transportManager, @NonNull Clock clock, @@ -169,10 +173,18 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) this.clock = clock; this.configResolver = configResolver; this.executorService = executorService; + + StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); + if (startupTime == null) { + firebaseStartupTime = new Timer(); + } else { + firebaseStartupTime = + Timer.ofElapsedRealtime(startupTime.getElapsedRealtime(), startupTime.getUptimeMillis()); + } this.experimentTtid = TraceMetric.newBuilder().setName("_experiment_app_start_ttid"); } - /** Called from FirebasePerfProvider to register this callback. */ + /** Called from FirebasePerfEarly to register this callback. */ public synchronized void registerActivityLifecycleCallbacks(@NonNull Context context) { // Make sure the callback is registered only once. if (isRegisteredForLifecycleCallbacks) { @@ -197,7 +209,7 @@ public synchronized void unregisterActivityLifecycleCallbacks() { /** * Gets the timetamp that marks the beginning of app start, currently defined as the beginning of - * BIND_APPLICATION. Fallback to class-load time of {@link FirebasePerfProvider} when API < 24. + * BIND_APPLICATION. Fallback to class-load time of {@link StartupTime} when API < 24. * * @return {@link Timer} at the beginning of app start by Fireperf definition. */ @@ -206,7 +218,7 @@ private static Timer getStartTimer() { return Timer.ofElapsedRealtime( Process.getStartElapsedRealtime(), Process.getStartUptimeMillis()); } - return FirebasePerfProvider.getAppStartTime(); + return firebaseStartupTime; } private void recordFirstDrawDone() { @@ -222,9 +234,8 @@ private void recordFirstDrawDone() { TraceMetric.Builder subtrace = TraceMetric.newBuilder() .setName("_experiment_classLoadTime") - .setClientStartTimeUs(FirebasePerfProvider.getAppStartTime().getMicros()) - .setDurationUs( - FirebasePerfProvider.getAppStartTime().getDurationMicros(this.firstDrawDone)); + .setClientStartTimeUs(firebaseStartupTime.getMicros()) + .setDurationUs(firebaseStartupTime.getDurationMicros(this.firstDrawDone)); this.experimentTtid.addSubtraces(subtrace.build()); subtrace = TraceMetric.newBuilder(); @@ -290,8 +301,7 @@ public synchronized void onActivityCreated(Activity activity, Bundle savedInstan launchActivity = new WeakReference(activity); onCreateTime = clock.getTime(); - if (FirebasePerfProvider.getAppStartTime().getDurationMicros(onCreateTime) - > MAX_LATENCY_BEFORE_UI_INIT) { + if (firebaseStartupTime.getDurationMicros(onCreateTime) > MAX_LATENCY_BEFORE_UI_INIT) { isTooLateToInitUI = true; } } @@ -327,7 +337,7 @@ public synchronized void onActivityResumed(Activity activity) { appStartActivity = new WeakReference(activity); onResumeTime = clock.getTime(); - this.appStartTime = FirebasePerfProvider.getAppStartTime(); + this.appStartTime = firebaseStartupTime; this.startSession = SessionManager.getInstance().perfSession(); AndroidLogger.getInstance() .debug( @@ -421,8 +431,8 @@ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} /** * 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 FirebasePerfProvider. If app is started from background, this runnable - * will be executed before any activity's onCreate() method. If app is started from foreground, + * 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. */ public static class StartFromBackgroundRunnable implements Runnable { diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/provider/FirebasePerfProvider.java b/firebase-perf/src/main/java/com/google/firebase/perf/provider/FirebasePerfProvider.java deleted file mode 100644 index cd3df8158fb..00000000000 --- a/firebase-perf/src/main/java/com/google/firebase/perf/provider/FirebasePerfProvider.java +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.firebase.perf.provider; - -import android.app.Application; -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.Context; -import android.content.pm.ProviderInfo; -import android.database.Cursor; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.Keep; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import com.google.android.gms.common.internal.Preconditions; -import com.google.android.gms.common.util.VisibleForTesting; -import com.google.firebase.perf.FirebasePerformanceInitializer; -import com.google.firebase.perf.application.AppStateMonitor; -import com.google.firebase.perf.config.ConfigResolver; -import com.google.firebase.perf.metrics.AppStartTrace; -import com.google.firebase.perf.session.SessionManager; -import com.google.firebase.perf.util.Clock; -import com.google.firebase.perf.util.Timer; - -/** Initializes app start time at app startup time. */ -@Keep -public class FirebasePerfProvider extends ContentProvider { - - private static final Timer APP_START_TIME = new Clock().getTime(); - /** Should match the {@link FirebasePerfProvider} authority if $androidId is empty. */ - @VisibleForTesting - static final String EMPTY_APPLICATION_ID_PROVIDER_AUTHORITY = - "com.google.firebase.firebaseperfprovider"; - - private final Handler mainHandler = new Handler(Looper.getMainLooper()); - - public static Timer getAppStartTime() { - return APP_START_TIME; - } - - @Override - public void attachInfo(Context context, ProviderInfo info) { - // super.attachInfo calls onCreate(). Fail as early as possible. - checkContentProviderAuthority(info); - super.attachInfo(context, info); - - // Initialize ConfigResolver early for accessing device caching layer. - ConfigResolver configResolver = ConfigResolver.getInstance(); - configResolver.setContentProviderContext(getContext()); - - AppStateMonitor appStateMonitor = AppStateMonitor.getInstance(); - appStateMonitor.registerActivityLifecycleCallbacks(getContext()); - appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer()); - - AppStartTrace appStartTrace = AppStartTrace.getInstance(); - appStartTrace.registerActivityLifecycleCallbacks(getContext()); - - mainHandler.post(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace)); - - // In the case of cold start, we create a session and start collecting gauges as early as - // possible. - // There is code in SessionManager that prevents us from resetting the session twice in case - // of app cold start. - SessionManager.getInstance().initializeGaugeCollection(); - } - - /** Called before {@link Application#onCreate()}. */ - @Override - public boolean onCreate() { - return false; - } - - /** - * Check that the content provider's authority does not use firebase-common's package name. If it - * does, crash in order to alert the developer of the problem before they distribute the app. - */ - private static void checkContentProviderAuthority(@NonNull ProviderInfo info) { - Preconditions.checkNotNull(info, "FirebasePerfProvider ProviderInfo cannot be null."); - if (EMPTY_APPLICATION_ID_PROVIDER_AUTHORITY.equals(info.authority)) { - throw new IllegalStateException( - "Incorrect provider authority in manifest. Most likely due to a missing " - + "applicationId variable in application's build.gradle."); - } - } - - @Nullable - @Override - public Cursor query( - Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - return null; - } - - @Nullable - @Override - public String getType(Uri uri) { - return null; - } - - @Nullable - @Override - public Uri insert(Uri uri, ContentValues values) { - return null; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - return 0; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - return 0; - } -} diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/provider/package-info.java b/firebase-perf/src/main/java/com/google/firebase/perf/provider/package-info.java deleted file mode 100644 index d3aff47a348..00000000000 --- a/firebase-perf/src/main/java/com/google/firebase/perf/provider/package-info.java +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** @hide */ -package com.google.firebase.perf.provider; diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerfRegistrarTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerfRegistrarTest.java index 8fd5c68d354..f76f1bb9679 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerfRegistrarTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/FirebasePerfRegistrarTest.java @@ -18,6 +18,7 @@ import com.google.android.datatransport.TransportFactory; import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; import com.google.firebase.components.Component; import com.google.firebase.components.Dependency; import com.google.firebase.installations.FirebaseInstallationsApi; @@ -35,9 +36,7 @@ public void testGetComponents() { FirebasePerfRegistrar firebasePerfRegistrar = new FirebasePerfRegistrar(); List> components = firebasePerfRegistrar.getComponents(); - // Note: Although we have 3 deps but looks like size doesn't count deps towards interface like - // FirebaseInstallationsApi - assertThat(components).hasSize(2); + assertThat(components).hasSize(3); Component firebasePerfComponent = components.get(0); @@ -46,8 +45,17 @@ public void testGetComponents() { Dependency.required(FirebaseApp.class), Dependency.requiredProvider(RemoteConfigComponent.class), Dependency.required(FirebaseInstallationsApi.class), - Dependency.requiredProvider(TransportFactory.class)); + Dependency.requiredProvider(TransportFactory.class), + Dependency.required(FirebasePerfEarly.class)); assertThat(firebasePerfComponent.isLazy()).isTrue(); + + Component firebasePerfEarlyComponent = components.get(1); + + assertThat(firebasePerfEarlyComponent.getDependencies()) + .containsExactly( + Dependency.required(FirebaseApp.class), Dependency.optionalProvider(StartupTime.class)); + + assertThat(firebasePerfEarlyComponent.isLazy()).isFalse(); } } diff --git a/firebase-perf/src/test/java/com/google/firebase/perf/config/RemoteConfigManagerTest.java b/firebase-perf/src/test/java/com/google/firebase/perf/config/RemoteConfigManagerTest.java index af1b971d9a1..91db4227536 100644 --- a/firebase-perf/src/test/java/com/google/firebase/perf/config/RemoteConfigManagerTest.java +++ b/firebase-perf/src/test/java/com/google/firebase/perf/config/RemoteConfigManagerTest.java @@ -28,7 +28,6 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.firebase.inject.Provider; import com.google.firebase.perf.FirebasePerformanceTestBase; -import com.google.firebase.perf.provider.FirebasePerfProvider; import com.google.firebase.remoteconfig.FirebaseRemoteConfig; import com.google.firebase.remoteconfig.FirebaseRemoteConfigInfo; import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings; @@ -840,8 +839,7 @@ public void triggerRemoteConfigFetchIfNecessary_doesNotFetchBeforeAppStartRandom appStartConfigFetchDelay)); // Simulate time fast forward to some time before fetch time is up - long appStartTimeInMs = - TimeUnit.MICROSECONDS.toMillis(FirebasePerfProvider.getAppStartTime().getMicros()); + long appStartTimeInMs = System.currentTimeMillis(); when(remoteConfigManagerPartialMock.getCurrentSystemTimeMillis()) .thenReturn(appStartTimeInMs + appStartConfigFetchDelay - 2000); @@ -867,8 +865,7 @@ public void triggerRemoteConfigFetchIfNecessary_fetchesAfterAppStartRandomDelay( appStartConfigFetchDelay)); // Simulate time fast forward to 2s after fetch delay time is up - long appStartTimeInMs = - TimeUnit.MICROSECONDS.toMillis(FirebasePerfProvider.getAppStartTime().getMicros()); + long appStartTimeInMs = System.currentTimeMillis(); when(remoteConfigManagerPartialMock.getCurrentSystemTimeMillis()) .thenReturn(appStartTimeInMs + appStartConfigFetchDelay + 2000); @@ -920,13 +917,18 @@ private RemoteConfigManager setupTestRemoteConfigManager( when(mockFirebaseRemoteConfig.getAll()).thenReturn(configs); if (initializeFrc) { return new RemoteConfigManager( - cacheManager, fakeExecutor, mockFirebaseRemoteConfig, appStartConfigFetchDelayInMs); + cacheManager, + fakeExecutor, + mockFirebaseRemoteConfig, + appStartConfigFetchDelayInMs, + RemoteConfigManager.getInitialStartupMillis()); } else { return new RemoteConfigManager( cacheManager, fakeExecutor, /* firebaseRemoteConfig= */ null, - appStartConfigFetchDelayInMs); + appStartConfigFetchDelayInMs, + RemoteConfigManager.getInitialStartupMillis()); } } 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 550accd2b92..65f4071ea81 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 @@ -15,7 +15,6 @@ package com.google.firebase.perf.metrics; import static com.google.common.truth.Truth.assertThat; -import static com.google.firebase.perf.util.TimerTest.getElapsedRealtimeMicros; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -26,7 +25,6 @@ import static org.robolectric.Shadows.shadowOf; import android.app.Activity; -import android.content.pm.ProviderInfo; import android.os.Bundle; import android.os.Looper; import android.os.Process; @@ -35,7 +33,6 @@ import androidx.test.core.app.ApplicationProvider; import com.google.firebase.perf.FirebasePerformanceTestBase; import com.google.firebase.perf.config.ConfigResolver; -import com.google.firebase.perf.provider.FirebasePerfProvider; import com.google.firebase.perf.session.SessionManager; import com.google.firebase.perf.transport.TransportManager; import com.google.firebase.perf.util.Clock; @@ -78,9 +75,6 @@ public class AppStartTraceTest extends FirebasePerformanceTestBase { // a mocked current wall-clock time in microseconds. private long currentTime = 0; - // Timer at the beginning of app startup - private Timer appStart; - @Before public void setUp() { initMocks(this); @@ -95,7 +89,6 @@ public Timer answer(InvocationOnMock invocationOnMock) throws Throwable { .getTime(); transportManager = mock(TransportManager.class); traceArgumentCaptor = ArgumentCaptor.forClass(TraceMetric.class); - appStart = FirebasePerfProvider.getAppStartTime(); } @After @@ -156,15 +149,10 @@ private void verifyFinalState( TraceMetric metric = traceArgumentCaptor.getValue(); Assert.assertEquals(Constants.TraceNames.APP_START_TRACE_NAME.toString(), metric.getName()); - Assert.assertEquals(appStart.getMicros(), metric.getClientStartTimeUs()); - Assert.assertEquals(resumeTime - getElapsedRealtimeMicros(appStart), metric.getDurationUs()); Assert.assertEquals(3, metric.getSubtracesCount()); Assert.assertEquals( Constants.TraceNames.ON_CREATE_TRACE_NAME.toString(), metric.getSubtraces(0).getName()); - Assert.assertEquals(appStart.getMicros(), metric.getSubtraces(0).getClientStartTimeUs()); - Assert.assertEquals( - createTime - getElapsedRealtimeMicros(appStart), metric.getSubtraces(0).getDurationUs()); Assert.assertEquals( Constants.TraceNames.ON_START_TRACE_NAME.toString(), metric.getSubtraces(1).getName()); @@ -225,7 +213,10 @@ public void testDelayedAppStart() { AppStartTrace trace = new AppStartTrace(transportManager, clock, configResolver, fakeExecutorService); // Delays activity creation after 1 minute from app start time. - currentTime = appStart.getMicros() + TimeUnit.MINUTES.toMicros(1) + 1; + currentTime = + TimeUnit.MILLISECONDS.toMicros(SystemClock.elapsedRealtime()) + + TimeUnit.MINUTES.toMicros(1) + + 1; trace.onActivityCreated(activity1, bundle); Assert.assertEquals(currentTime, trace.getOnCreateTime().getMicros()); ++currentTime; @@ -262,24 +253,6 @@ public void testStartFromBackground() { ArgumentMatchers.nullable(ApplicationProcessState.class)); } - @Test - public void testFirebasePerfProviderOnAttachInfo_initializesGaugeCollection() { - com.google.firebase.perf.session.PerfSession mockPerfSession = - mock(com.google.firebase.perf.session.PerfSession.class); - when(mockPerfSession.sessionId()).thenReturn("sessionId"); - when(mockPerfSession.isGaugeAndEventCollectionEnabled()).thenReturn(true); - - SessionManager.getInstance().setPerfSession(mockPerfSession); - String oldSessionId = SessionManager.getInstance().perfSession().sessionId(); - Assert.assertEquals(oldSessionId, SessionManager.getInstance().perfSession().sessionId()); - - FirebasePerfProvider provider = new FirebasePerfProvider(); - provider.attachInfo(ApplicationProvider.getApplicationContext(), new ProviderInfo()); - - Assert.assertEquals(oldSessionId, SessionManager.getInstance().perfSession().sessionId()); - verify(mockPerfSession, times(2)).isGaugeAndEventCollectionEnabled(); - } - @Test @Config(sdk = 26) public void timeToInitialDisplay_isLogged() {