From f2c4fdb01284ee2ed06d9dda846151a8d37a3e96 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Tue, 25 Oct 2022 16:12:55 -0500 Subject: [PATCH 01/18] Migrate fireperf content provider to component --- .../java/com/google/firebase/FirebaseApp.java | 12 +- .../java/com/google/firebase/StartupTime.java | 50 +++++++ firebase-perf/src/main/AndroidManifest.xml | 5 - .../firebase/perf/FirebasePerfEarly.java | 64 +++++++++ .../firebase/perf/FirebasePerfRegistrar.java | 12 ++ .../perf/config/RemoteConfigManager.java | 11 +- .../firebase/perf/metrics/AppStartTrace.java | 31 +++-- .../perf/provider/FirebasePerfProvider.java | 128 ------------------ .../firebase/perf/provider/package-info.java | 16 --- .../perf/FirebasePerfRegistrarTest.java | 13 +- .../perf/config/RemoteConfigManagerTest.java | 9 +- .../perf/metrics/AppStartTraceTest.java | 27 +--- 12 files changed, 183 insertions(+), 195 deletions(-) create mode 100644 firebase-common/src/main/java/com/google/firebase/StartupTime.java create mode 100644 firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java delete mode 100644 firebase-perf/src/main/java/com/google/firebase/perf/provider/FirebasePerfProvider.java delete mode 100644 firebase-perf/src/main/java/com/google/firebase/perf/provider/package-info.java 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 efc6c01d8ce..47e2ca5e981 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -426,15 +426,21 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o FirebaseTrace.popTrace(); // ComponentDiscovery FirebaseTrace.pushTrace("Runtime"); - componentRuntime = + ComponentRuntime.Builder builder = ComponentRuntime.builder(UI_EXECUTOR) .addLazyComponentRegistrars(registrars) .addComponentRegistrar(new FirebaseCommonRegistrar()) .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 + if (UserManagerCompat.isUserUnlocked(applicationContext)) { + builder.addComponent(Component.of(StartupTime.now(), 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..b4d48095129 --- /dev/null +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -0,0 +1,50 @@ +// 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 com.google.auto.value.AutoValue; + +/** + * Represents the time at which Firebase began initialization, both in unix time/epoch milliseconds + * and in nanoseconds since the startup of the JVM. 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 guarenteed to be present, and instead should be optionally depended on, and its absence + * handled. + */ +@AutoValue +public abstract class StartupTime { + + /** @return The epoch time that Firebase began initializing, in milliseconds */ + public abstract long getEpochMillis(); + + /** + * @return The number of nanoseconds from the start of the program to when Firebase began + * initializing, measured by the JVM + */ + public abstract long getStartupNanos(); + + /** + * @param epochMillis Time in milliseconds since epoch + * @param startupNanos Time in nanoseconds since JVM start + */ + public static StartupTime create(long epochMillis, long startupNanos) { + return new AutoValue_StartupTime(epochMillis, startupNanos); + } + + /** @return A StartupTime represented by the current epoch time and JVM nano time */ + public static StartupTime now() { + return create(System.currentTimeMillis(), System.nanoTime()); + } +} 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 { + private final Handler mainHandler = new Handler(Looper.getMainLooper()); + + @NonNull + public static FirebasePerfEarly getInstance() { + return FirebaseApp.getInstance().get(FirebasePerfEarly.class); + } + + public FirebasePerfEarly(FirebaseApp app) { + 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 (app.get(StartupTime.class) != 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..bb883f83d83 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; @@ -54,6 +55,13 @@ public List> getComponents() { .add(Dependency.requiredProvider(TransportFactory.class)) .factory(FirebasePerfRegistrar::providesFirebasePerformance) .build(), + Component.builder(FirebasePerfEarly.class) + .name(LIBRARY_NAME) + .add(Dependency.required(FirebaseApp.class)) + .add(Dependency.optionalProvider(StartupTime.class)) + .alwaysEager() + .factory(FirebasePerfRegistrar::providesFirebasePerformanceEarly) + .build(), /** * Fireperf SDK is lazily by {@link FirebasePerformanceInitializer} during {@link * com.google.firebase.perf.application.AppStateMonitor#onActivityResumed(Activity)}. we use @@ -77,4 +85,8 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine return component.getFirebasePerformance(); } + + private static FirebasePerfEarly providesFirebasePerformanceEarly(ComponentContainer container) { + return new FirebasePerfEarly(container.get(FirebaseApp.class)); + } } 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 7c27b59c3d9..49fb97c40f1 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 @@ -22,9 +22,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; @@ -93,8 +94,12 @@ private RemoteConfigManager() { firebaseRemoteConfig == null ? new ConcurrentHashMap<>() : new ConcurrentHashMap<>(firebaseRemoteConfig.getAll()); - this.appStartTimeInMs = - TimeUnit.MICROSECONDS.toMillis(FirebasePerfProvider.getAppStartTime().getMicros()); + StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); + if (startupTime != null) { + this.appStartTimeInMs = startupTime.getEpochMillis(); + } else { + this.appStartTimeInMs = System.currentTimeMillis(); + } 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 2e1bb5cd8d3..4924ec6d5b0 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 @@ -26,9 +26,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; @@ -91,6 +92,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; @@ -163,9 +166,16 @@ 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(); + } + firebaseStartupTime = + Timer.ofElapsedRealtime(TimeUnit.NANOSECONDS.toMillis(startupTime.getStartupNanos())); } - /** 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) { @@ -190,7 +200,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. */ @@ -198,7 +208,7 @@ private static Timer getStartTimer() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { return Timer.ofElapsedRealtime(Process.getStartElapsedRealtime()); } - return FirebasePerfProvider.getAppStartTime(); + return firebaseStartupTime; } private void recordFirstDrawDone() { @@ -225,8 +235,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; } } @@ -261,7 +270,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( @@ -290,8 +299,8 @@ private void logColdStart(Timer start, Timer end, PerfSession session) { TraceMetric.Builder subtrace = TraceMetric.newBuilder() .setName("_experiment_classLoadTime") - .setClientStartTimeUs(FirebasePerfProvider.getAppStartTime().getMicros()) - .setDurationUs(FirebasePerfProvider.getAppStartTime().getDurationMicros(end)); + .setClientStartTimeUs(firebaseStartupTime.getMicros()) + .setDurationUs(firebaseStartupTime.getDurationMicros(end)); metric.addSubtraces(subtrace).addPerfSessions(this.startSession.build()); @@ -347,8 +356,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..cac3c49add9 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); @@ -49,5 +48,13 @@ public void testGetComponents() { Dependency.requiredProvider(TransportFactory.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..afb2f1b1ad8 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 @@ -26,9 +26,10 @@ import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.TaskCompletionSource; import com.google.common.util.concurrent.MoreExecutors; +import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; 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 +841,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 = FirebaseApp.getInstance().get(StartupTime.class).getEpochMillis(); when(remoteConfigManagerPartialMock.getCurrentSystemTimeMillis()) .thenReturn(appStartTimeInMs + appStartConfigFetchDelay - 2000); @@ -867,8 +867,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 = FirebaseApp.getInstance().get(StartupTime.class).getEpochMillis(); when(remoteConfigManagerPartialMock.getCurrentSystemTimeMillis()) .thenReturn(appStartTimeInMs + appStartConfigFetchDelay + 2000); 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 e705a145aed..6442aee47ce 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 @@ -26,16 +26,16 @@ 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; import android.os.SystemClock; import android.view.View; import androidx.test.core.app.ApplicationProvider; +import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; 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; @@ -95,7 +95,10 @@ public Timer answer(InvocationOnMock invocationOnMock) throws Throwable { .getTime(); transportManager = mock(TransportManager.class); traceArgumentCaptor = ArgumentCaptor.forClass(TraceMetric.class); - appStart = FirebasePerfProvider.getAppStartTime(); + appStart = + Timer.ofElapsedRealtime( + TimeUnit.NANOSECONDS.toMillis( + FirebaseApp.getInstance().get(StartupTime.class).getStartupNanos())); } @After @@ -262,24 +265,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() { From a495cb70fbbde7fe79ccf5c43ece68357db411dd Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Fri, 28 Oct 2022 11:17:12 -0500 Subject: [PATCH 02/18] Resolve comments --- .../java/com/google/firebase/FirebaseApp.java | 3 ++- .../java/com/google/firebase/StartupTime.java | 23 ++++++++----------- .../firebase/perf/FirebasePerfEarly.java | 5 ---- .../firebase/perf/FirebasePerfRegistrar.java | 2 +- .../firebase/perf/metrics/AppStartTrace.java | 3 +-- .../perf/metrics/AppStartTraceTest.java | 3 +-- 6 files changed, 15 insertions(+), 24 deletions(-) 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 47e2ca5e981..a7de3953beb 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -416,6 +416,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 = StartupTime.now(); FirebaseTrace.pushTrace("Firebase"); @@ -437,7 +438,7 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o // Don't provide StartupTime in direct boot mode if (UserManagerCompat.isUserUnlocked(applicationContext)) { - builder.addComponent(Component.of(StartupTime.now(), StartupTime.class)); + builder.addComponent(Component.of(startupTime, StartupTime.class)); } componentRuntime = builder.build(); diff --git a/firebase-common/src/main/java/com/google/firebase/StartupTime.java b/firebase-common/src/main/java/com/google/firebase/StartupTime.java index b4d48095129..2d82a96b662 100644 --- a/firebase-common/src/main/java/com/google/firebase/StartupTime.java +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -14,14 +14,14 @@ package com.google.firebase; +import android.os.SystemClock; import com.google.auto.value.AutoValue; /** * Represents the time at which Firebase began initialization, both in unix time/epoch milliseconds - * and in nanoseconds since the startup of the JVM. 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 guarenteed to be present, and instead should be optionally depended on, and its absence - * handled. + * and in milliseconds since boot. 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. */ @AutoValue public abstract class StartupTime { @@ -29,22 +29,19 @@ public abstract class StartupTime { /** @return The epoch time that Firebase began initializing, in milliseconds */ public abstract long getEpochMillis(); - /** - * @return The number of nanoseconds from the start of the program to when Firebase began - * initializing, measured by the JVM - */ - public abstract long getStartupNanos(); + /** @return The number of milliseconds from boot to when Firebase began initializing */ + public abstract long getElapsedRealtime(); /** * @param epochMillis Time in milliseconds since epoch - * @param startupNanos Time in nanoseconds since JVM start + * @param elapsedRealtime Time in milliseconds since boot */ - public static StartupTime create(long epochMillis, long startupNanos) { - return new AutoValue_StartupTime(epochMillis, startupNanos); + public static StartupTime create(long epochMillis, long elapsedRealtime) { + return new AutoValue_StartupTime(epochMillis, elapsedRealtime); } /** @return A StartupTime represented by the current epoch time and JVM nano time */ public static StartupTime now() { - return create(System.currentTimeMillis(), System.nanoTime()); + return create(System.currentTimeMillis(), SystemClock.elapsedRealtime()); } } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java index 99644ea673e..661aad4f0a2 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java @@ -33,11 +33,6 @@ public class FirebasePerfEarly { private final Handler mainHandler = new Handler(Looper.getMainLooper()); - @NonNull - public static FirebasePerfEarly getInstance() { - return FirebaseApp.getInstance().get(FirebasePerfEarly.class); - } - public FirebasePerfEarly(FirebaseApp app) { Context context = app.getApplicationContext(); 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 bb883f83d83..2041fc5d8b2 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 @@ -59,7 +59,7 @@ public List> getComponents() { .name(LIBRARY_NAME) .add(Dependency.required(FirebaseApp.class)) .add(Dependency.optionalProvider(StartupTime.class)) - .alwaysEager() + .eagerInDefaultApp() .factory(FirebasePerfRegistrar::providesFirebasePerformanceEarly) .build(), /** 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 4924ec6d5b0..cb9ba7a4418 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 @@ -171,8 +171,7 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) if (startupTime == null) { firebaseStartupTime = new Timer(); } - firebaseStartupTime = - Timer.ofElapsedRealtime(TimeUnit.NANOSECONDS.toMillis(startupTime.getStartupNanos())); + firebaseStartupTime = Timer.ofElapsedRealtime(startupTime.getElapsedRealtime()); } /** Called from FirebasePerfEarly to register this callback. */ 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 6442aee47ce..ce594911f8b 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 @@ -97,8 +97,7 @@ public Timer answer(InvocationOnMock invocationOnMock) throws Throwable { traceArgumentCaptor = ArgumentCaptor.forClass(TraceMetric.class); appStart = Timer.ofElapsedRealtime( - TimeUnit.NANOSECONDS.toMillis( - FirebaseApp.getInstance().get(StartupTime.class).getStartupNanos())); + FirebaseApp.getInstance().get(StartupTime.class).getElapsedRealtime()); } @After From c2d74765a7ac3a2225f0179de6a2a5e66f1be3bf Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Tue, 8 Nov 2022 13:27:44 -0600 Subject: [PATCH 03/18] Collect startup time from init provider --- .../src/main/java/com/google/firebase/FirebaseApp.java | 7 ++++--- .../com/google/firebase/provider/FirebaseInitProvider.java | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) 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 a7de3953beb..2db2a460e89 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -51,6 +51,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; @@ -416,7 +417,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 = StartupTime.now(); + StartupTime startupTime = FirebaseInitProvider.startupTime; FirebaseTrace.pushTrace("Firebase"); @@ -436,8 +437,8 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o .addComponent(Component.of(options, FirebaseOptions.class)) .setProcessor(new ComponentMonitor()); - // Don't provide StartupTime in direct boot mode - if (UserManagerCompat.isUserUnlocked(applicationContext)) { + // Don't provide StartupTime in direct boot mode or if Firebase was manually started + if (UserManagerCompat.isUserUnlocked(applicationContext) && startupTime != null) { builder.addComponent(Component.of(startupTime, StartupTime.class)); } 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..dadfe8cec3f 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,12 @@ import androidx.annotation.VisibleForTesting; import com.google.android.gms.common.internal.Preconditions; import com.google.firebase.FirebaseApp; +import com.google.firebase.StartupTime; /** Initializes Firebase APIs at app startup time. */ public class FirebaseInitProvider extends ContentProvider { - private static final String TAG = "FirebaseInitProvider"; + public static StartupTime startupTime; /** Should match the {@link FirebaseInitProvider} authority if $androidId is empty. */ @VisibleForTesting @@ -48,6 +49,7 @@ public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) { /** Called before {@link Application#onCreate()}. */ @Override public boolean onCreate() { + startupTime = StartupTime.now(); if (FirebaseApp.initializeApp(getContext()) == null) { Log.i(TAG, "FirebaseApp initialization unsuccessful"); } else { From b4f9cccef93df57d9a9edd6f7be4884ba3947818 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Wed, 9 Nov 2022 14:52:29 -0600 Subject: [PATCH 04/18] Limit startup time usage to during init provider initialization --- .../java/com/google/firebase/FirebaseApp.java | 3 ++- .../provider/FirebaseInitProvider.java | 20 ++++++++++++------- 2 files changed, 15 insertions(+), 8 deletions(-) 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 2db2a460e89..491689307e7 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -438,7 +438,8 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o .setProcessor(new ComponentMonitor()); // Don't provide StartupTime in direct boot mode or if Firebase was manually started - if (UserManagerCompat.isUserUnlocked(applicationContext) && startupTime != null) { + if (UserManagerCompat.isUserUnlocked(applicationContext) + && FirebaseInitProvider.currentlyInitializing.get()) { builder.addComponent(Component.of(startupTime, StartupTime.class)); } 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 dadfe8cec3f..5578048a4f2 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 @@ -28,11 +28,13 @@ 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"; - public static StartupTime startupTime; + public static StartupTime startupTime = StartupTime.now(); + public static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); /** Should match the {@link FirebaseInitProvider} authority if $androidId is empty. */ @VisibleForTesting @@ -49,13 +51,17 @@ public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) { /** Called before {@link Application#onCreate()}. */ @Override public boolean onCreate() { - startupTime = StartupTime.now(); - 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; } /** From 8071823327cb2ccc4ceb659adc45e950cfc5a334 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Wed, 9 Nov 2022 15:05:49 -0600 Subject: [PATCH 05/18] Pass in startup time in private constructor --- .../firebase/perf/FirebasePerfEarly.java | 1 - .../perf/config/RemoteConfigManager.java | 23 ++++++++++++------- .../perf/config/RemoteConfigManagerTest.java | 9 ++++++-- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java index 661aad4f0a2..fec2120de2e 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java @@ -17,7 +17,6 @@ import android.content.Context; import android.os.Handler; import android.os.Looper; -import androidx.annotation.NonNull; import com.google.firebase.FirebaseApp; import com.google.firebase.StartupTime; import com.google.firebase.perf.application.AppStateMonitor; 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 49fb97c40f1..a2eb8d9ac5c 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 @@ -78,7 +78,18 @@ 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 + static long getInitialStartupMillis() { + StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); + if (startupTime != null) { + return startupTime.getEpochMillis(); + } else { + return System.currentTimeMillis(); + } } @VisibleForTesting @@ -86,7 +97,8 @@ private RemoteConfigManager() { DeviceCacheManager cache, Executor executor, FirebaseRemoteConfig firebaseRemoteConfig, - long appStartConfigFetchDelayInMs) { + long appStartConfigFetchDelayInMs, + long appStartTimeInMs) { this.cache = cache; this.executor = executor; this.firebaseRemoteConfig = firebaseRemoteConfig; @@ -94,12 +106,7 @@ private RemoteConfigManager() { firebaseRemoteConfig == null ? new ConcurrentHashMap<>() : new ConcurrentHashMap<>(firebaseRemoteConfig.getAll()); - StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); - if (startupTime != null) { - this.appStartTimeInMs = startupTime.getEpochMillis(); - } else { - this.appStartTimeInMs = System.currentTimeMillis(); - } + this.appStartTimeInMs = appStartTimeInMs; this.appStartConfigFetchDelayInMs = appStartConfigFetchDelayInMs; } 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 afb2f1b1ad8..85126a8a170 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 @@ -919,13 +919,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()); } } From fd7c0e3741d41923a3df5cb41c3264275f3496b5 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Thu, 10 Nov 2022 10:29:50 -0600 Subject: [PATCH 06/18] Component dependencies and names --- .../java/com/google/firebase/perf/FirebasePerfRegistrar.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 2041fc5d8b2..06a405efaf3 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 @@ -42,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 @@ -53,10 +54,11 @@ 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(LIBRARY_NAME) + .name(EARLY_LIBRARY_NAME) .add(Dependency.required(FirebaseApp.class)) .add(Dependency.optionalProvider(StartupTime.class)) .eagerInDefaultApp() From 18b98fbf041c8a1366def8e84ff7b187563acc55 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Thu, 10 Nov 2022 11:29:16 -0600 Subject: [PATCH 07/18] Ensure FirebasePerfEarly gets initialized before FirebasePerf --- .../java/com/google/firebase/perf/FirebasePerfRegistrar.java | 2 ++ 1 file changed, 2 insertions(+) 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 06a405efaf3..26a8ebd773f 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 @@ -75,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( From 14235e2caaeebc0d0a214cd8df625748aee4917b Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Thu, 17 Nov 2022 11:18:15 -0600 Subject: [PATCH 08/18] Actually fix merge --- .../src/main/java/com/google/firebase/FirebaseApp.java | 2 +- .../src/main/java/com/google/firebase/StartupTime.java | 10 +++++++--- .../google/firebase/perf/metrics/AppStartTrace.java | 8 ++++---- .../firebase/perf/metrics/AppStartTraceTest.java | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) 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 f59886ff4ab..a91a9164207 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -420,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()) diff --git a/firebase-common/src/main/java/com/google/firebase/StartupTime.java b/firebase-common/src/main/java/com/google/firebase/StartupTime.java index 2d82a96b662..cd01f338e33 100644 --- a/firebase-common/src/main/java/com/google/firebase/StartupTime.java +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -32,16 +32,20 @@ public abstract class StartupTime { /** @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 StartupTime create(long epochMillis, long elapsedRealtime) { - return new AutoValue_StartupTime(epochMillis, elapsedRealtime); + public static 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 StartupTime now() { - return create(System.currentTimeMillis(), SystemClock.elapsedRealtime()); + return create( + System.currentTimeMillis(), SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); } } 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 bccf23e61bf..c19616d1469 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 @@ -177,7 +177,8 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) if (startupTime == null) { firebaseStartupTime = new Timer(); } - firebaseStartupTime = Timer.ofElapsedRealtime(startupTime.getElapsedRealtime()); + firebaseStartupTime = + Timer.ofElapsedRealtime(startupTime.getElapsedRealtime(), startupTime.getUptimeMillis()); this.experimentTtid = TraceMetric.newBuilder().setName("_experiment_app_start_ttid"); } @@ -231,9 +232,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(); 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 0b442d4299b..e3385add100 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 @@ -95,9 +95,9 @@ public Timer answer(InvocationOnMock invocationOnMock) throws Throwable { .getTime(); transportManager = mock(TransportManager.class); traceArgumentCaptor = ArgumentCaptor.forClass(TraceMetric.class); + StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); appStart = - Timer.ofElapsedRealtime( - FirebaseApp.getInstance().get(StartupTime.class).getElapsedRealtime()); + Timer.ofElapsedRealtime(startupTime.getElapsedRealtime(), startupTime.getUptimeMillis()); } @After From f074d74c36137389131c5d2045d1d4181e0bd822 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Fri, 18 Nov 2022 11:58:05 -0600 Subject: [PATCH 09/18] Adjust tests --- .../firebase/perf/metrics/AppStartTrace.java | 5 +++-- .../perf/FirebasePerfRegistrarTest.java | 3 ++- .../perf/config/RemoteConfigManagerTest.java | 6 ++---- .../perf/metrics/AppStartTraceTest.java | 19 ++++--------------- 4 files changed, 11 insertions(+), 22 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 c19616d1469..b804a2fc34c 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 @@ -176,9 +176,10 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); if (startupTime == null) { firebaseStartupTime = new Timer(); + } else { + firebaseStartupTime = + Timer.ofElapsedRealtime(startupTime.getElapsedRealtime(), startupTime.getUptimeMillis()); } - firebaseStartupTime = - Timer.ofElapsedRealtime(startupTime.getElapsedRealtime(), startupTime.getUptimeMillis()); this.experimentTtid = TraceMetric.newBuilder().setName("_experiment_app_start_ttid"); } 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 cac3c49add9..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 @@ -45,7 +45,8 @@ 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(); 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 85126a8a170..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 @@ -26,8 +26,6 @@ import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.TaskCompletionSource; import com.google.common.util.concurrent.MoreExecutors; -import com.google.firebase.FirebaseApp; -import com.google.firebase.StartupTime; import com.google.firebase.inject.Provider; import com.google.firebase.perf.FirebasePerformanceTestBase; import com.google.firebase.remoteconfig.FirebaseRemoteConfig; @@ -841,7 +839,7 @@ public void triggerRemoteConfigFetchIfNecessary_doesNotFetchBeforeAppStartRandom appStartConfigFetchDelay)); // Simulate time fast forward to some time before fetch time is up - long appStartTimeInMs = FirebaseApp.getInstance().get(StartupTime.class).getEpochMillis(); + long appStartTimeInMs = System.currentTimeMillis(); when(remoteConfigManagerPartialMock.getCurrentSystemTimeMillis()) .thenReturn(appStartTimeInMs + appStartConfigFetchDelay - 2000); @@ -867,7 +865,7 @@ public void triggerRemoteConfigFetchIfNecessary_fetchesAfterAppStartRandomDelay( appStartConfigFetchDelay)); // Simulate time fast forward to 2s after fetch delay time is up - long appStartTimeInMs = FirebaseApp.getInstance().get(StartupTime.class).getEpochMillis(); + long appStartTimeInMs = System.currentTimeMillis(); when(remoteConfigManagerPartialMock.getCurrentSystemTimeMillis()) .thenReturn(appStartTimeInMs + appStartConfigFetchDelay + 2000); 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 e3385add100..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; @@ -32,8 +31,6 @@ import android.os.SystemClock; import android.view.View; import androidx.test.core.app.ApplicationProvider; -import com.google.firebase.FirebaseApp; -import com.google.firebase.StartupTime; import com.google.firebase.perf.FirebasePerformanceTestBase; import com.google.firebase.perf.config.ConfigResolver; import com.google.firebase.perf.session.SessionManager; @@ -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,9 +89,6 @@ public Timer answer(InvocationOnMock invocationOnMock) throws Throwable { .getTime(); transportManager = mock(TransportManager.class); traceArgumentCaptor = ArgumentCaptor.forClass(TraceMetric.class); - StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); - appStart = - Timer.ofElapsedRealtime(startupTime.getElapsedRealtime(), startupTime.getUptimeMillis()); } @After @@ -158,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()); @@ -227,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; From 23b36d46b9f3ad67fa86dae9c978ca4fa054c6f1 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Fri, 18 Nov 2022 14:43:32 -0600 Subject: [PATCH 10/18] Resolve cli tests --- .../src/main/java/com/google/firebase/StartupTime.java | 6 ++++++ .../com/google/firebase/provider/FirebaseInitProvider.java | 1 + .../java/com/google/firebase/perf/FirebasePerfEarly.java | 2 +- .../google/firebase/perf/config/RemoteConfigManager.java | 2 +- .../com/google/firebase/perf/metrics/AppStartTrace.java | 2 +- 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/StartupTime.java b/firebase-common/src/main/java/com/google/firebase/StartupTime.java index cd01f338e33..5c3185a22ef 100644 --- a/firebase-common/src/main/java/com/google/firebase/StartupTime.java +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -17,6 +17,8 @@ import android.os.SystemClock; import com.google.auto.value.AutoValue; +import javax.annotation.Nullable; + /** * Represents the time at which Firebase began initialization, both in unix time/epoch milliseconds * and in milliseconds since boot. The absence of a StartupTime indicates an unreliable or @@ -26,6 +28,10 @@ @AutoValue public abstract class StartupTime { + public static @Nullable StartupTime getInstance() { + return FirebaseApp.getInstance().get(StartupTime.class); + } + /** @return The epoch time that Firebase began initializing, in milliseconds */ public abstract long getEpochMillis(); 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 5578048a4f2..267940dbe31 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 @@ -33,6 +33,7 @@ /** Initializes Firebase APIs at app startup time. */ public class FirebaseInitProvider extends ContentProvider { private static final String TAG = "FirebaseInitProvider"; + @Nullable public static StartupTime startupTime = StartupTime.now(); public static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java index fec2120de2e..e9bdac5a015 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java @@ -43,7 +43,7 @@ public FirebasePerfEarly(FirebaseApp app) { appStateMonitor.registerActivityLifecycleCallbacks(context); appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer()); - if (app.get(StartupTime.class) != null) { + if (StartupTime.getInstance() != null) { AppStartTrace appStartTrace = AppStartTrace.getInstance(); appStartTrace.registerActivityLifecycleCallbacks(context); mainHandler.post(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace)); 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 c90f791c093..8c15c7f14bc 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 @@ -87,7 +87,7 @@ private RemoteConfigManager() { @VisibleForTesting static long getInitialStartupMillis() { - StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); + StartupTime startupTime = StartupTime.getInstance(); if (startupTime != null) { return startupTime.getEpochMillis(); } else { 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 b804a2fc34c..b0ed722d8d3 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 @@ -173,7 +173,7 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) this.configResolver = configResolver; this.executorService = executorService; - StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); + StartupTime startupTime = StartupTime.getInstance(); if (startupTime == null) { firebaseStartupTime = new Timer(); } else { From 9e31558dac182f6e3e7e4743bdcb2a6e81634ba4 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Fri, 18 Nov 2022 15:11:11 -0600 Subject: [PATCH 11/18] Resolve nullability --- .../com/google/firebase/provider/FirebaseInitProvider.java | 1 + .../main/java/com/google/firebase/perf/FirebasePerfEarly.java | 4 ++++ 2 files changed, 5 insertions(+) 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 267940dbe31..3e46bd291c8 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 @@ -35,6 +35,7 @@ public class FirebaseInitProvider extends ContentProvider { private static final String TAG = "FirebaseInitProvider"; @Nullable public static StartupTime startupTime = StartupTime.now(); + @NonNull public static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); /** Should match the {@link FirebaseInitProvider} authority if $androidId is empty. */ diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java index e9bdac5a015..1e9b9784c1b 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java @@ -17,6 +17,9 @@ import android.content.Context; import android.os.Handler; import android.os.Looper; + +import androidx.annotation.NonNull; + import com.google.firebase.FirebaseApp; import com.google.firebase.StartupTime; import com.google.firebase.perf.application.AppStateMonitor; @@ -30,6 +33,7 @@ *

Responsible for initializing the AppStartTrace, and early initialization of ConfigResolver */ public class FirebasePerfEarly { + @NonNull private final Handler mainHandler = new Handler(Looper.getMainLooper()); public FirebasePerfEarly(FirebaseApp app) { From ea9339c01bb80f73609b23d93f5feffb2c50ae2c Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Fri, 18 Nov 2022 15:35:29 -0600 Subject: [PATCH 12/18] Actually resolving nullability lints --- .../src/main/java/com/google/firebase/StartupTime.java | 7 +++++-- .../java/com/google/firebase/perf/FirebasePerfEarly.java | 5 +++-- .../com/google/firebase/perf/FirebasePerfRegistrar.java | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/StartupTime.java b/firebase-common/src/main/java/com/google/firebase/StartupTime.java index 5c3185a22ef..18596f29c4c 100644 --- a/firebase-common/src/main/java/com/google/firebase/StartupTime.java +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -15,6 +15,9 @@ package com.google.firebase; import android.os.SystemClock; + +import androidx.annotation.NonNull; + import com.google.auto.value.AutoValue; import javax.annotation.Nullable; @@ -45,12 +48,12 @@ public abstract class StartupTime { * @param epochMillis Time in milliseconds since epoch * @param elapsedRealtime Time in milliseconds since boot */ - public static StartupTime create(long epochMillis, long elapsedRealtime, long uptimeMillis) { + 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 StartupTime now() { + public static @NonNull StartupTime now() { return create( System.currentTimeMillis(), SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); } diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java index 1e9b9784c1b..ff634d6ed1d 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java @@ -19,6 +19,7 @@ import android.os.Looper; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.firebase.FirebaseApp; import com.google.firebase.StartupTime; @@ -36,7 +37,7 @@ public class FirebasePerfEarly { @NonNull private final Handler mainHandler = new Handler(Looper.getMainLooper()); - public FirebasePerfEarly(FirebaseApp app) { + public FirebasePerfEarly(@NonNull FirebaseApp app, @Nullable StartupTime startupTime) { Context context = app.getApplicationContext(); // Initialize ConfigResolver early for accessing device caching layer. @@ -47,7 +48,7 @@ public FirebasePerfEarly(FirebaseApp app) { appStateMonitor.registerActivityLifecycleCallbacks(context); appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer()); - if (StartupTime.getInstance() != null) { + if (startupTime != null) { AppStartTrace appStartTrace = AppStartTrace.getInstance(); appStartTrace.registerActivityLifecycleCallbacks(context); mainHandler.post(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace)); 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 26a8ebd773f..c92df5fff58 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 @@ -91,6 +91,6 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine } private static FirebasePerfEarly providesFirebasePerformanceEarly(ComponentContainer container) { - return new FirebasePerfEarly(container.get(FirebaseApp.class)); + return new FirebasePerfEarly(container.get(FirebaseApp.class), container.get(StartupTime.class)); } } From 5e10d8f0f08b316f592a13b61e6f084d878e7e82 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Fri, 18 Nov 2022 15:58:01 -0600 Subject: [PATCH 13/18] Formatting --- .../src/main/java/com/google/firebase/StartupTime.java | 6 ++---- .../com/google/firebase/provider/FirebaseInitProvider.java | 6 ++---- .../java/com/google/firebase/perf/FirebasePerfEarly.java | 5 +---- .../com/google/firebase/perf/FirebasePerfRegistrar.java | 3 ++- .../google/firebase/perf/config/RemoteConfigManager.java | 1 - .../com/google/firebase/perf/metrics/AppStartTrace.java | 1 - 6 files changed, 7 insertions(+), 15 deletions(-) diff --git a/firebase-common/src/main/java/com/google/firebase/StartupTime.java b/firebase-common/src/main/java/com/google/firebase/StartupTime.java index 18596f29c4c..6e2c8dac10d 100644 --- a/firebase-common/src/main/java/com/google/firebase/StartupTime.java +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -15,11 +15,8 @@ package com.google.firebase; import android.os.SystemClock; - import androidx.annotation.NonNull; - import com.google.auto.value.AutoValue; - import javax.annotation.Nullable; /** @@ -48,7 +45,8 @@ public abstract class StartupTime { * @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) { + public static @NonNull StartupTime create( + long epochMillis, long elapsedRealtime, long uptimeMillis) { return new AutoValue_StartupTime(epochMillis, elapsedRealtime, 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 3e46bd291c8..f5052075fbc 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 @@ -33,10 +33,8 @@ /** Initializes Firebase APIs at app startup time. */ public class FirebaseInitProvider extends ContentProvider { private static final String TAG = "FirebaseInitProvider"; - @Nullable - public static StartupTime startupTime = StartupTime.now(); - @NonNull - public static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); + @Nullable public static StartupTime startupTime = StartupTime.now(); + @NonNull public static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); /** Should match the {@link FirebaseInitProvider} authority if $androidId is empty. */ @VisibleForTesting diff --git a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java index ff634d6ed1d..c858c00fc23 100644 --- a/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java +++ b/firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfEarly.java @@ -17,10 +17,8 @@ import android.content.Context; import android.os.Handler; import android.os.Looper; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.firebase.FirebaseApp; import com.google.firebase.StartupTime; import com.google.firebase.perf.application.AppStateMonitor; @@ -34,8 +32,7 @@ *

Responsible for initializing the AppStartTrace, and early initialization of ConfigResolver */ public class FirebasePerfEarly { - @NonNull - private final Handler mainHandler = new Handler(Looper.getMainLooper()); + @NonNull private final Handler mainHandler = new Handler(Looper.getMainLooper()); public FirebasePerfEarly(@NonNull FirebaseApp app, @Nullable StartupTime startupTime) { Context context = app.getApplicationContext(); 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 c92df5fff58..adb081bf05e 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 @@ -91,6 +91,7 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine } private static FirebasePerfEarly providesFirebasePerformanceEarly(ComponentContainer container) { - return new FirebasePerfEarly(container.get(FirebaseApp.class), container.get(StartupTime.class)); + return new FirebasePerfEarly( + container.get(FirebaseApp.class), container.get(StartupTime.class)); } } 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 8c15c7f14bc..71842066ccd 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,7 +23,6 @@ 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; 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 b0ed722d8d3..e5cd2499ae4 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,7 +27,6 @@ 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; From e7c4f34b23037468a471a85a5c20eaa47338b59a Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Mon, 21 Nov 2022 11:13:47 -0600 Subject: [PATCH 14/18] Adjust privacy and docs --- .../java/com/google/firebase/FirebaseApp.java | 2 +- .../java/com/google/firebase/StartupTime.java | 16 +++++++--------- .../firebase/provider/FirebaseInitProvider.java | 6 +++++- .../perf/config/RemoteConfigManager.java | 4 +++- .../firebase/perf/metrics/AppStartTrace.java | 4 +++- 5 files changed, 19 insertions(+), 13 deletions(-) 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 a91a9164207..317886816c3 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -432,7 +432,7 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o // Don't provide StartupTime in direct boot mode or if Firebase was manually started if (UserManagerCompat.isUserUnlocked(applicationContext) - && FirebaseInitProvider.currentlyInitializing.get()) { + && FirebaseInitProvider.isCurrentlyInitializing()) { builder.addComponent(Component.of(startupTime, StartupTime.class)); } diff --git a/firebase-common/src/main/java/com/google/firebase/StartupTime.java b/firebase-common/src/main/java/com/google/firebase/StartupTime.java index 6e2c8dac10d..972b0ed3c2c 100644 --- a/firebase-common/src/main/java/com/google/firebase/StartupTime.java +++ b/firebase-common/src/main/java/com/google/firebase/StartupTime.java @@ -17,21 +17,19 @@ import android.os.SystemClock; import androidx.annotation.NonNull; import com.google.auto.value.AutoValue; -import javax.annotation.Nullable; /** - * Represents the time at which Firebase began initialization, both in unix time/epoch milliseconds - * and in milliseconds since boot. 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. + * 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 { - public static @Nullable StartupTime getInstance() { - return FirebaseApp.getInstance().get(StartupTime.class); - } - /** @return The epoch time that Firebase began initializing, in milliseconds */ public abstract long getEpochMillis(); 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 f5052075fbc..0afac22f809 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 @@ -34,7 +34,11 @@ public class FirebaseInitProvider extends ContentProvider { private static final String TAG = "FirebaseInitProvider"; @Nullable public static StartupTime startupTime = StartupTime.now(); - @NonNull public static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); + @NonNull private static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); + + public static boolean isCurrentlyInitializing() { + return currentlyInitializing.get(); + } /** Should match the {@link FirebaseInitProvider} authority if $androidId is empty. */ @VisibleForTesting 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 71842066ccd..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,6 +23,7 @@ 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; @@ -85,8 +86,9 @@ private RemoteConfigManager() { } @VisibleForTesting + @SuppressWarnings("FirebaseUseExplicitDependencies") static long getInitialStartupMillis() { - StartupTime startupTime = StartupTime.getInstance(); + StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); if (startupTime != null) { return startupTime.getEpochMillis(); } else { 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 e5cd2499ae4..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,6 +27,7 @@ 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; @@ -162,6 +163,7 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) return instance; } + @SuppressWarnings("FirebaseUseExplicitDependencies") AppStartTrace( @NonNull TransportManager transportManager, @NonNull Clock clock, @@ -172,7 +174,7 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock) this.configResolver = configResolver; this.executorService = executorService; - StartupTime startupTime = StartupTime.getInstance(); + StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class); if (startupTime == null) { firebaseStartupTime = new Timer(); } else { From 7d8062ba1778c639ee4444d7fb1c17b2c4c836f0 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Mon, 21 Nov 2022 12:20:49 -0600 Subject: [PATCH 15/18] Hide startup time in the init provider --- .../src/main/java/com/google/firebase/FirebaseApp.java | 2 +- .../com/google/firebase/provider/FirebaseInitProvider.java | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) 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 317886816c3..ee16ff412e6 100644 --- a/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java +++ b/firebase-common/src/main/java/com/google/firebase/FirebaseApp.java @@ -409,7 +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.startupTime; + StartupTime startupTime = FirebaseInitProvider.getStartupTime(); FirebaseTrace.pushTrace("Firebase"); 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 0afac22f809..98f63ed5667 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 @@ -33,9 +33,14 @@ /** Initializes Firebase APIs at app startup time. */ public class FirebaseInitProvider extends ContentProvider { private static final String TAG = "FirebaseInitProvider"; - @Nullable public static StartupTime startupTime = StartupTime.now(); + @Nullable private static StartupTime startupTime = StartupTime.now(); @NonNull private static AtomicBoolean currentlyInitializing = new AtomicBoolean(false); + /** @hide */ + public static @Nullable StartupTime getStartupTime() { + return startupTime; + } + public static boolean isCurrentlyInitializing() { return currentlyInitializing.get(); } From 66a5f05bfb49d6dcb1734cd6977d43e71a318387 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Mon, 21 Nov 2022 12:36:13 -0600 Subject: [PATCH 16/18] Fix error with getting optional component from ComponentContainer --- .../java/com/google/firebase/perf/FirebasePerfRegistrar.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 adb081bf05e..83cd0c84128 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 @@ -90,8 +90,9 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine return component.getFirebasePerformance(); } + @SuppressWarnings("FirebaseUseExplicitDependencies") private static FirebasePerfEarly providesFirebasePerformanceEarly(ComponentContainer container) { return new FirebasePerfEarly( - container.get(FirebaseApp.class), container.get(StartupTime.class)); + container.get(FirebaseApp.class), container.get(FirebaseApp.class).get(StartupTime.class)); } } From 1f668dd6c12261e550426f9d733a183ff3e56942 Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Mon, 21 Nov 2022 12:57:21 -0600 Subject: [PATCH 17/18] Fix error with getting optional component from ComponentContainer --- .../java/com/google/firebase/perf/FirebasePerfRegistrar.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 83cd0c84128..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 @@ -90,9 +90,8 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine return component.getFirebasePerformance(); } - @SuppressWarnings("FirebaseUseExplicitDependencies") private static FirebasePerfEarly providesFirebasePerformanceEarly(ComponentContainer container) { return new FirebasePerfEarly( - container.get(FirebaseApp.class), container.get(FirebaseApp.class).get(StartupTime.class)); + container.get(FirebaseApp.class), container.getProvider(StartupTime.class).get()); } } From b5af211d30703889850845881a5d43ba36f1867c Mon Sep 17 00:00:00 2001 From: Emily Ploszaj Date: Tue, 22 Nov 2022 12:43:31 -0600 Subject: [PATCH 18/18] Hide one more method --- .../java/com/google/firebase/provider/FirebaseInitProvider.java | 1 + 1 file changed, 1 insertion(+) 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 98f63ed5667..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 @@ -41,6 +41,7 @@ public class FirebaseInitProvider extends ContentProvider { return startupTime; } + /** @hide */ public static boolean isCurrentlyInitializing() { return currentlyInitializing.get(); }