Skip to content

Commit 6eee549

Browse files
emilypgoogleDavid Motsonashvili
authored and
David Motsonashvili
committed
Migrate fireperf content provider to component (#4242)
* Migrate fireperf content provider to component * Resolve comments * Collect startup time from init provider * Limit startup time usage to during init provider initialization * Pass in startup time in private constructor * Component dependencies and names * Ensure FirebasePerfEarly gets initialized before FirebasePerf * Actually fix merge * Adjust tests * Resolve cli tests * Resolve nullability * Actually resolving nullability lints * Formatting * Adjust privacy and docs * Hide startup time in the init provider * Fix error with getting optional component from ComponentContainer * Fix error with getting optional component from ComponentContainer * Hide one more method
1 parent 7d2de6e commit 6eee549

File tree

13 files changed

+234
-217
lines changed

13 files changed

+234
-217
lines changed

firebase-common/src/main/java/com/google/firebase/FirebaseApp.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import com.google.firebase.heartbeatinfo.DefaultHeartBeatController;
5151
import com.google.firebase.inject.Provider;
5252
import com.google.firebase.internal.DataCollectionConfigStorage;
53+
import com.google.firebase.provider.FirebaseInitProvider;
5354
import com.google.firebase.tracing.ComponentMonitor;
5455
import com.google.firebase.tracing.FirebaseTrace;
5556
import java.nio.charset.Charset;
@@ -408,6 +409,7 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o
408409
this.applicationContext = Preconditions.checkNotNull(applicationContext);
409410
this.name = Preconditions.checkNotEmpty(name);
410411
this.options = Preconditions.checkNotNull(options);
412+
StartupTime startupTime = FirebaseInitProvider.getStartupTime();
411413

412414
FirebaseTrace.pushTrace("Firebase");
413415

@@ -418,16 +420,23 @@ protected FirebaseApp(Context applicationContext, String name, FirebaseOptions o
418420
FirebaseTrace.popTrace(); // ComponentDiscovery
419421

420422
FirebaseTrace.pushTrace("Runtime");
421-
componentRuntime =
423+
ComponentRuntime.Builder builder =
422424
ComponentRuntime.builder(com.google.firebase.concurrent.UiExecutor.INSTANCE)
423425
.addLazyComponentRegistrars(registrars)
424426
.addComponentRegistrar(new FirebaseCommonRegistrar())
425427
.addComponentRegistrar(new ExecutorsRegistrar())
426428
.addComponent(Component.of(applicationContext, Context.class))
427429
.addComponent(Component.of(this, FirebaseApp.class))
428430
.addComponent(Component.of(options, FirebaseOptions.class))
429-
.setProcessor(new ComponentMonitor())
430-
.build();
431+
.setProcessor(new ComponentMonitor());
432+
433+
// Don't provide StartupTime in direct boot mode or if Firebase was manually started
434+
if (UserManagerCompat.isUserUnlocked(applicationContext)
435+
&& FirebaseInitProvider.isCurrentlyInitializing()) {
436+
builder.addComponent(Component.of(startupTime, StartupTime.class));
437+
}
438+
439+
componentRuntime = builder.build();
431440
FirebaseTrace.popTrace(); // Runtime
432441

433442
dataCollectionConfigStorage =
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase;
16+
17+
import android.os.SystemClock;
18+
import androidx.annotation.NonNull;
19+
import com.google.auto.value.AutoValue;
20+
21+
/**
22+
* Represents Firebase's startup time in several timing methods. Represented in unix time/epoch
23+
* milliseconds milliseconds since boot, and uptime milliseconds. The absence of a StartupTime
24+
* indicates an unreliable or misleading time, such as a launch in direct boot mode. Because of
25+
* this, StartupTime cannot be guaranteed to be present, and instead should be optionally depended
26+
* on, and its absence handled.
27+
*
28+
* @hide
29+
*/
30+
@AutoValue
31+
public abstract class StartupTime {
32+
33+
/** @return The epoch time that Firebase began initializing, in milliseconds */
34+
public abstract long getEpochMillis();
35+
36+
/** @return The number of milliseconds from boot to when Firebase began initializing */
37+
public abstract long getElapsedRealtime();
38+
39+
/** @return The number of milliseconds of uptime measured by SystemClock.uptimeMillis() */
40+
public abstract long getUptimeMillis();
41+
42+
/**
43+
* @param epochMillis Time in milliseconds since epoch
44+
* @param elapsedRealtime Time in milliseconds since boot
45+
*/
46+
public static @NonNull StartupTime create(
47+
long epochMillis, long elapsedRealtime, long uptimeMillis) {
48+
return new AutoValue_StartupTime(epochMillis, elapsedRealtime, uptimeMillis);
49+
}
50+
51+
/** @return A StartupTime represented by the current epoch time and JVM nano time */
52+
public static @NonNull StartupTime now() {
53+
return create(
54+
System.currentTimeMillis(), SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
55+
}
56+
}

firebase-common/src/main/java/com/google/firebase/provider/FirebaseInitProvider.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,24 @@
2727
import androidx.annotation.VisibleForTesting;
2828
import com.google.android.gms.common.internal.Preconditions;
2929
import com.google.firebase.FirebaseApp;
30+
import com.google.firebase.StartupTime;
31+
import java.util.concurrent.atomic.AtomicBoolean;
3032

3133
/** Initializes Firebase APIs at app startup time. */
3234
public class FirebaseInitProvider extends ContentProvider {
33-
3435
private static final String TAG = "FirebaseInitProvider";
36+
@Nullable private static StartupTime startupTime = StartupTime.now();
37+
@NonNull private static AtomicBoolean currentlyInitializing = new AtomicBoolean(false);
38+
39+
/** @hide */
40+
public static @Nullable StartupTime getStartupTime() {
41+
return startupTime;
42+
}
43+
44+
/** @hide */
45+
public static boolean isCurrentlyInitializing() {
46+
return currentlyInitializing.get();
47+
}
3548

3649
/** Should match the {@link FirebaseInitProvider} authority if $androidId is empty. */
3750
@VisibleForTesting
@@ -48,12 +61,17 @@ public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) {
4861
/** Called before {@link Application#onCreate()}. */
4962
@Override
5063
public boolean onCreate() {
51-
if (FirebaseApp.initializeApp(getContext()) == null) {
52-
Log.i(TAG, "FirebaseApp initialization unsuccessful");
53-
} else {
54-
Log.i(TAG, "FirebaseApp initialization successful");
64+
try {
65+
currentlyInitializing.set(true);
66+
if (FirebaseApp.initializeApp(getContext()) == null) {
67+
Log.i(TAG, "FirebaseApp initialization unsuccessful");
68+
} else {
69+
Log.i(TAG, "FirebaseApp initialization successful");
70+
}
71+
return false;
72+
} finally {
73+
currentlyInitializing.set(false);
5574
}
56-
return false;
5775
}
5876

5977
/**

firebase-perf/src/main/AndroidManifest.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,6 @@
66
<uses-permission android:name="android.permission.INTERNET"/>
77

88
<application>
9-
<provider
10-
android:authorities="${applicationId}.firebaseperfprovider"
11-
android:exported="false"
12-
android:initOrder="101"
13-
android:name="com.google.firebase.perf.provider.FirebasePerfProvider"/>
149

1510
<service
1611
android:exported="false"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2022 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
//
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.perf;
16+
17+
import android.content.Context;
18+
import android.os.Handler;
19+
import android.os.Looper;
20+
import androidx.annotation.NonNull;
21+
import androidx.annotation.Nullable;
22+
import com.google.firebase.FirebaseApp;
23+
import com.google.firebase.StartupTime;
24+
import com.google.firebase.perf.application.AppStateMonitor;
25+
import com.google.firebase.perf.config.ConfigResolver;
26+
import com.google.firebase.perf.metrics.AppStartTrace;
27+
import com.google.firebase.perf.session.SessionManager;
28+
29+
/**
30+
* The Firebase Performance early initialization.
31+
*
32+
* <p>Responsible for initializing the AppStartTrace, and early initialization of ConfigResolver
33+
*/
34+
public class FirebasePerfEarly {
35+
@NonNull private final Handler mainHandler = new Handler(Looper.getMainLooper());
36+
37+
public FirebasePerfEarly(@NonNull FirebaseApp app, @Nullable StartupTime startupTime) {
38+
Context context = app.getApplicationContext();
39+
40+
// Initialize ConfigResolver early for accessing device caching layer.
41+
ConfigResolver configResolver = ConfigResolver.getInstance();
42+
configResolver.setApplicationContext(context);
43+
44+
AppStateMonitor appStateMonitor = AppStateMonitor.getInstance();
45+
appStateMonitor.registerActivityLifecycleCallbacks(context);
46+
appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer());
47+
48+
if (startupTime != null) {
49+
AppStartTrace appStartTrace = AppStartTrace.getInstance();
50+
appStartTrace.registerActivityLifecycleCallbacks(context);
51+
mainHandler.post(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace));
52+
}
53+
54+
// In the case of cold start, we create a session and start collecting gauges as early as
55+
// possible.
56+
// There is code in SessionManager that prevents us from resetting the session twice in case
57+
// of app cold start.
58+
SessionManager.getInstance().initializeGaugeCollection();
59+
}
60+
}

firebase-perf/src/main/java/com/google/firebase/perf/FirebasePerfRegistrar.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import androidx.annotation.Keep;
1818
import com.google.android.datatransport.TransportFactory;
1919
import com.google.firebase.FirebaseApp;
20+
import com.google.firebase.StartupTime;
2021
import com.google.firebase.components.Component;
2122
import com.google.firebase.components.ComponentContainer;
2223
import com.google.firebase.components.ComponentRegistrar;
@@ -41,6 +42,7 @@
4142
@Keep
4243
public class FirebasePerfRegistrar implements ComponentRegistrar {
4344
private static final String LIBRARY_NAME = "fire-perf";
45+
private static final String EARLY_LIBRARY_NAME = "fire-perf-early";
4446

4547
@Override
4648
@Keep
@@ -52,8 +54,16 @@ public List<Component<?>> getComponents() {
5254
.add(Dependency.requiredProvider(RemoteConfigComponent.class))
5355
.add(Dependency.required(FirebaseInstallationsApi.class))
5456
.add(Dependency.requiredProvider(TransportFactory.class))
57+
.add(Dependency.required(FirebasePerfEarly.class))
5558
.factory(FirebasePerfRegistrar::providesFirebasePerformance)
5659
.build(),
60+
Component.builder(FirebasePerfEarly.class)
61+
.name(EARLY_LIBRARY_NAME)
62+
.add(Dependency.required(FirebaseApp.class))
63+
.add(Dependency.optionalProvider(StartupTime.class))
64+
.eagerInDefaultApp()
65+
.factory(FirebasePerfRegistrar::providesFirebasePerformanceEarly)
66+
.build(),
5767
/**
5868
* Fireperf SDK is lazily by {@link FirebasePerformanceInitializer} during {@link
5969
* com.google.firebase.perf.application.AppStateMonitor#onActivityResumed(Activity)}. we use
@@ -65,6 +75,8 @@ public List<Component<?>> getComponents() {
6575
}
6676

6777
private static FirebasePerformance providesFirebasePerformance(ComponentContainer container) {
78+
// Ensure FirebasePerfEarly was initialized
79+
container.get(FirebasePerfEarly.class);
6880
FirebasePerformanceComponent component =
6981
DaggerFirebasePerformanceComponent.builder()
7082
.firebasePerformanceModule(
@@ -77,4 +89,9 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine
7789

7890
return component.getFirebasePerformance();
7991
}
92+
93+
private static FirebasePerfEarly providesFirebasePerformanceEarly(ComponentContainer container) {
94+
return new FirebasePerfEarly(
95+
container.get(FirebaseApp.class), container.getProvider(StartupTime.class).get());
96+
}
8097
}

firebase-perf/src/main/java/com/google/firebase/perf/config/RemoteConfigManager.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@
2323
import androidx.annotation.Keep;
2424
import androidx.annotation.Nullable;
2525
import com.google.android.gms.common.util.VisibleForTesting;
26+
import com.google.firebase.FirebaseApp;
27+
import com.google.firebase.StartupTime;
2628
import com.google.firebase.inject.Provider;
2729
import com.google.firebase.perf.logging.AndroidLogger;
28-
import com.google.firebase.perf.provider.FirebasePerfProvider;
2930
import com.google.firebase.perf.util.Optional;
3031
import com.google.firebase.remoteconfig.FirebaseRemoteConfig;
3132
import com.google.firebase.remoteconfig.FirebaseRemoteConfigValue;
@@ -80,24 +81,36 @@ private RemoteConfigManager() {
8081
new LinkedBlockingQueue<Runnable>()),
8182
/* firebaseRemoteConfig= */ null, // set once FirebaseRemoteConfig is initialized
8283
MIN_APP_START_CONFIG_FETCH_DELAY_MS
83-
+ new Random().nextInt(RANDOM_APP_START_CONFIG_FETCH_DELAY_MS));
84+
+ new Random().nextInt(RANDOM_APP_START_CONFIG_FETCH_DELAY_MS),
85+
getInitialStartupMillis());
86+
}
87+
88+
@VisibleForTesting
89+
@SuppressWarnings("FirebaseUseExplicitDependencies")
90+
static long getInitialStartupMillis() {
91+
StartupTime startupTime = FirebaseApp.getInstance().get(StartupTime.class);
92+
if (startupTime != null) {
93+
return startupTime.getEpochMillis();
94+
} else {
95+
return System.currentTimeMillis();
96+
}
8497
}
8598

8699
@VisibleForTesting
87100
RemoteConfigManager(
88101
DeviceCacheManager cache,
89102
Executor executor,
90103
FirebaseRemoteConfig firebaseRemoteConfig,
91-
long appStartConfigFetchDelayInMs) {
104+
long appStartConfigFetchDelayInMs,
105+
long appStartTimeInMs) {
92106
this.cache = cache;
93107
this.executor = executor;
94108
this.firebaseRemoteConfig = firebaseRemoteConfig;
95109
this.allRcConfigMap =
96110
firebaseRemoteConfig == null
97111
? new ConcurrentHashMap<>()
98112
: new ConcurrentHashMap<>(firebaseRemoteConfig.getAll());
99-
this.appStartTimeInMs =
100-
TimeUnit.MICROSECONDS.toMillis(FirebasePerfProvider.getAppStartTime().getMicros());
113+
this.appStartTimeInMs = appStartTimeInMs;
101114
this.appStartConfigFetchDelayInMs = appStartConfigFetchDelayInMs;
102115
}
103116

0 commit comments

Comments
 (0)