Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
94bf1b4
Remove the logging of GaugeMetadata to allow using AQS (#6678)
tejasd Feb 7, 2025
6624a4e
Implement a SessionSubscriber for Firebase Performance (#6683)
tejasd Feb 11, 2025
7914326
convert perf session to use aqs support session id (#6832)
themiswang Apr 7, 2025
c95d813
Update PerfSession and SessionManager to identify Legacy sessions. (#…
tejasd Apr 14, 2025
cbc5130
Add process name to application info (#6890)
themiswang Apr 17, 2025
b9e5efb
Update GaugeManager for AQS (#6938)
tejasd May 7, 2025
e4dc71d
Add unit tests for new logging in GaugeManager using GaugeCounter. (#…
tejasd May 15, 2025
35767db
Refactor usage of ProcessName in Fireperf. (#6963)
tejasd May 16, 2025
591db10
Add missing `GaugeCounter` increment. (#6966)
tejasd May 16, 2025
5ee6df3
Add gauge counter to collector tests (#6991)
tejasd May 26, 2025
3d97050
Update Gauge logging to better match the implementation on main (#6992)
tejasd May 27, 2025
d8cab85
Make the AQS Test App behave more typically for Perf start up (#7158)
mrober Jul 16, 2025
76c322e
Update the behaviour of the FirebasePerformanceSessionSubcriber to be…
tejasd Jul 25, 2025
db794e1
Add more legacy session verification, move up gauge collection, and u…
tejasd Jul 30, 2025
22ec9a5
Update the version dependency of firebase sessions (#7201)
tejasd Jul 30, 2025
917fa58
Update legacy session enforcement and debugging logic (#7244)
tejasd Aug 13, 2025
91fea97
Update legacy session logs to include trace names. (#7258)
tejasd Aug 14, 2025
cc777ce
Merge branch 'main' into fireperf-aqs
tejasd Jan 8, 2026
6cda4d7
SessionManager refactor phase1 (#7675)
jrodiz Mar 4, 2026
239b725
Fireperf aqs sessionmanager phase2 (#7997)
jrodiz Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion firebase-perf/firebase-perf.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ android {
buildConfigField("String", "TRANSPORT_LOG_SRC", "String.valueOf(\"FIREPERF\")")
buildConfigField("Boolean", "ENFORCE_DEFAULT_LOG_SRC", "Boolean.valueOf(false)")
buildConfigField("String", "FIREPERF_VERSION_NAME", "String.valueOf(\"" + property("version") + "\")")
buildConfigField("Boolean", "ENFORCE_LEGACY_SESSIONS", "Boolean.valueOf(false)")

if (project.hasProperty("fireperfBuildForAutopush")) {
// This allows the SDK to be built for "Autopush" env when the mentioned flag
// (-PfireperfBuildForAutopush) is passed in the gradle build command (of either the
// SDK or the Test App).
buildConfigField("String", "TRANSPORT_LOG_SRC", "String.valueOf(\"FIREPERF_AUTOPUSH\")")
buildConfigField("Boolean", "ENFORCE_DEFAULT_LOG_SRC", "Boolean.valueOf(true)")
buildConfigField("Boolean", "ENFORCE_LEGACY_SESSIONS", "Boolean.valueOf(true)")
}

minSdkVersion project.minSdkVersion
Expand Down Expand Up @@ -124,7 +126,7 @@ dependencies {
api("com.google.firebase:firebase-installations:18.0.0") {
exclude group: 'com.google.firebase', module: 'firebase-common-ktx'
}
api("com.google.firebase:firebase-sessions:2.0.7") {
api("com.google.firebase:firebase-sessions:3.0.0") {
exclude group: 'com.google.firebase', module: 'firebase-common'
exclude group: 'com.google.firebase', module: 'firebase-common-ktx'
exclude group: 'com.google.firebase', module: 'firebase-components'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
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.FirebasePerformanceSessionSubscriber;
import com.google.firebase.perf.session.SessionManager;
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
import java.util.concurrent.Executor;

/**
Expand All @@ -34,29 +36,29 @@
public class FirebasePerfEarly {

public FirebasePerfEarly(
FirebaseApp app, @Nullable StartupTime startupTime, Executor uiExecutor) {
FirebaseApp app,
@Nullable StartupTime startupTime,
Executor uiExecutor,
SessionManager sessionManager) {
Context context = app.getApplicationContext();

// Initialize ConfigResolver early for accessing device caching layer.
ConfigResolver configResolver = ConfigResolver.getInstance();
configResolver.setApplicationContext(context);

AppStateMonitor appStateMonitor = AppStateMonitor.getInstance();
// Register FirebasePerformance as a subscriber ASAP - which will start collecting gauges if the
// FirebaseSession is verbose.
FirebaseSessionsDependencies.register(
new FirebasePerformanceSessionSubscriber(configResolver, sessionManager));

AppStateMonitor appStateMonitor = AppStateMonitor.getInstance(sessionManager);
appStateMonitor.registerActivityLifecycleCallbacks(context);
appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer());

if (startupTime != null) {
AppStartTrace appStartTrace = AppStartTrace.getInstance();
AppStartTrace appStartTrace = AppStartTrace.getInstance(sessionManager);
appStartTrace.registerActivityLifecycleCallbacks(context);
uiExecutor.execute(new AppStartTrace.StartFromBackgroundRunnable(appStartTrace));
}

// TODO: Bring back Firebase Sessions dependency to watch for updates to sessions.

// 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@
import com.google.firebase.perf.injection.components.DaggerFirebasePerformanceComponent;
import com.google.firebase.perf.injection.components.FirebasePerformanceComponent;
import com.google.firebase.perf.injection.modules.FirebasePerformanceModule;
import com.google.firebase.perf.session.PerfSession;
import com.google.firebase.perf.session.SessionManager;
import com.google.firebase.perf.session.gauges.GaugeManager;
import com.google.firebase.platforminfo.LibraryVersionComponent;
import com.google.firebase.remoteconfig.RemoteConfigComponent;
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
import com.google.firebase.sessions.api.SessionSubscriber;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
Expand All @@ -47,6 +52,11 @@ public class FirebasePerfRegistrar implements ComponentRegistrar {
private static final String LIBRARY_NAME = "fire-perf";
private static final String EARLY_LIBRARY_NAME = "fire-perf-early";

static {
// Add Firebase Performance as a dependency of Sessions when this class is loaded into memory.
FirebaseSessionsDependencies.addDependency(SessionSubscriber.Name.PERFORMANCE);
}

@Override
@Keep
public List<Component<?>> getComponents() {
Expand All @@ -59,20 +69,28 @@ public List<Component<?>> getComponents() {
.add(Dependency.required(FirebaseInstallationsApi.class))
.add(Dependency.requiredProvider(TransportFactory.class))
.add(Dependency.required(FirebasePerfEarly.class))
.add(Dependency.required(SessionManager.class))
.factory(FirebasePerfRegistrar::providesFirebasePerformance)
.build(),
Component.builder(FirebasePerfEarly.class)
.name(EARLY_LIBRARY_NAME)
.add(Dependency.required(FirebaseApp.class))
.add(Dependency.optionalProvider(StartupTime.class))
.add(Dependency.required(uiExecutor))
.add(Dependency.required(SessionManager.class))
.eagerInDefaultApp()
.factory(
container ->
new FirebasePerfEarly(
container.get(FirebaseApp.class),
container.getProvider(StartupTime.class).get(),
container.get(uiExecutor)))
container.get(uiExecutor),
container.get(SessionManager.class)))
.build(),
Component.builder(SessionManager.class)
.factory(
container ->
new SessionManager(GaugeManager.getInstance(), PerfSession.createWithId(null)))
.build(),
/**
* Fireperf SDK is lazily by {@link FirebasePerformanceInitializer} during {@link
Expand All @@ -94,7 +112,8 @@ private static FirebasePerformance providesFirebasePerformance(ComponentContaine
container.get(FirebaseApp.class),
container.get(FirebaseInstallationsApi.class),
container.getProvider(RemoteConfigComponent.class),
container.getProvider(TransportFactory.class)))
container.getProvider(TransportFactory.class),
container.get(SessionManager.class)))
.build();

return component.getFirebasePerformance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.google.firebase.perf.config.RemoteConfigManager;
import com.google.firebase.perf.logging.AndroidLogger;
import com.google.firebase.perf.logging.ConsoleUrlGenerator;
import com.google.firebase.perf.logging.FirebaseSessionsEnforcementCheck;
import com.google.firebase.perf.metrics.HttpMetric;
import com.google.firebase.perf.metrics.Trace;
import com.google.firebase.perf.session.SessionManager;
Expand Down Expand Up @@ -136,11 +137,6 @@ public static FirebasePerformance getInstance() {
// to false if it's been force disabled or it is set to null if neither.
@Nullable private Boolean mPerformanceCollectionForceEnabledState = null;

private final FirebaseApp firebaseApp;
private final Provider<RemoteConfigComponent> firebaseRemoteConfigProvider;
private final FirebaseInstallationsApi firebaseInstallationsApi;
private final Provider<TransportFactory> transportFactoryProvider;

/**
* Constructs the FirebasePerformance class and allows injecting dependencies.
*
Expand All @@ -166,23 +162,19 @@ public static FirebasePerformance getInstance() {
ConfigResolver configResolver,
SessionManager sessionManager) {

this.firebaseApp = firebaseApp;
this.firebaseRemoteConfigProvider = firebaseRemoteConfigProvider;
this.firebaseInstallationsApi = firebaseInstallationsApi;
this.transportFactoryProvider = transportFactoryProvider;

if (firebaseApp == null) {
this.mPerformanceCollectionForceEnabledState = false;
this.configResolver = configResolver;
this.mMetadataBundle = new ImmutableBundle(new Bundle());
return;
}
FirebaseSessionsEnforcementCheck.setEnforcement(BuildConfig.ENFORCE_LEGACY_SESSIONS);

TransportManager.getInstance()
.initialize(firebaseApp, firebaseInstallationsApi, transportFactoryProvider);
.initialize(
firebaseApp, firebaseInstallationsApi, transportFactoryProvider, sessionManager);

Context appContext = firebaseApp.getApplicationContext();
// TODO(b/110178816): Explore moving off of main thread.
mMetadataBundle = extractMetadata(appContext);

remoteConfigManager.setFirebaseRemoteConfigProvider(firebaseRemoteConfigProvider);
Expand All @@ -192,6 +184,7 @@ public static FirebasePerformance getInstance() {
sessionManager.setApplicationContext(appContext);

mPerformanceCollectionForceEnabledState = configResolver.getIsPerformanceCollectionEnabled();

if (logger.isLogcatEnabled() && isPerformanceCollectionEnabled()) {
logger.info(
String.format(
Expand Down Expand Up @@ -282,7 +275,7 @@ public synchronized void setPerformanceCollectionEnabled(@Nullable Boolean enabl
return;
}

if (configResolver.getIsPerformanceCollectionDeactivated()) {
if (Boolean.TRUE.equals(configResolver.getIsPerformanceCollectionDeactivated())) {
logger.info("Firebase Performance is permanently disabled");
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
import com.google.firebase.perf.logging.AndroidLogger;
import com.google.firebase.perf.metrics.FrameMetricsCalculator.PerfFrameMetrics;
import com.google.firebase.perf.metrics.Trace;
import com.google.firebase.perf.session.PerfSession;
import com.google.firebase.perf.session.SessionManager;
import com.google.firebase.perf.session.gauges.GaugeManager;
import com.google.firebase.perf.transport.TransportManager;
import com.google.firebase.perf.util.Clock;
import com.google.firebase.perf.util.Constants;
Expand Down Expand Up @@ -70,6 +72,7 @@ public class AppStateMonitor implements ActivityLifecycleCallbacks {
private final TransportManager transportManager;
private final ConfigResolver configResolver;
private final Clock clock;
private final SessionManager sessionManager;
private final boolean screenPerformanceRecordingSupported;

private Timer resumeTime; // The time app comes to foreground
Expand All @@ -80,22 +83,46 @@ public class AppStateMonitor implements ActivityLifecycleCallbacks {
private boolean isRegisteredForLifecycleCallbacks = false;
private boolean isColdStart = true;

public static AppStateMonitor getInstance(SessionManager sessionManager) {
if (instance == null) {
synchronized (AppStateMonitor.class) {
if (instance == null) {
instance =
new AppStateMonitor(TransportManager.getInstance(), new Clock(), sessionManager);
}
}
}
return instance;
}

/**
* Returns the singleton instance, creating it with a default {@link SessionManager} if not
* already initialized. In production, {@link #getInstance(SessionManager)} is always called
* first by {@link com.google.firebase.perf.FirebasePerfEarly}, so the pre-seeded instance is
* returned. This overload exists for call sites that run after early initialization (e.g.
* {@link com.google.firebase.perf.application.AppStateUpdateHandler}) and for test environments.
*/
public static AppStateMonitor getInstance() {
if (instance == null) {
synchronized (AppStateMonitor.class) {
if (instance == null) {
instance = new AppStateMonitor(TransportManager.getInstance(), new Clock());
instance =
new AppStateMonitor(
TransportManager.getInstance(),
new Clock(),
new SessionManager(GaugeManager.getInstance(), PerfSession.createWithId(null)));
}
}
}
return instance;
}

AppStateMonitor(TransportManager transportManager, Clock clock) {
AppStateMonitor(TransportManager transportManager, Clock clock, SessionManager sessionManager) {
this(
transportManager,
clock,
ConfigResolver.getInstance(),
sessionManager,
isScreenPerformanceRecordingSupported());
}

Expand All @@ -104,13 +131,24 @@ public static AppStateMonitor getInstance() {
TransportManager transportManager,
Clock clock,
ConfigResolver configResolver,
SessionManager sessionManager,
boolean screenPerformanceRecordingSupported) {
this.transportManager = transportManager;
this.clock = clock;
this.configResolver = configResolver;
this.sessionManager = sessionManager;
this.screenPerformanceRecordingSupported = screenPerformanceRecordingSupported;
}

public SessionManager getSessionManager() {
return sessionManager;
}

@VisibleForTesting
public static void resetInstance() {
instance = null;
}

public synchronized void registerActivityLifecycleCallbacks(Context context) {
// Make sure the callback is registered only once.
if (isRegisteredForLifecycleCallbacks) {
Expand Down Expand Up @@ -379,7 +417,7 @@ private void sendSessionLog(String name, Timer startTime, Timer endTime) {
.setName(name)
.setClientStartTimeUs(startTime.getMicros())
.setDurationUs(startTime.getDurationMicros(endTime))
.addPerfSessions(SessionManager.getInstance().perfSession().build());
.addPerfSessions(sessionManager.perfSession().build());
// Atomically get mTsnsCount and set it to zero.
int tsnsCount = this.tsnsCount.getAndSet(0);
synchronized (metricToCountMap) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public ConfigResolver(
@Nullable ImmutableBundle metadataBundle,
@Nullable DeviceCacheManager deviceCacheManager) {
this.remoteConfigManager =
remoteConfigManager == null ? RemoteConfigManager.getInstance() : remoteConfigManager;
remoteConfigManager == null ? new RemoteConfigManager() : remoteConfigManager;
this.metadataBundle = metadataBundle == null ? new ImmutableBundle() : metadataBundle;
this.deviceCacheManager =
deviceCacheManager == null ? DeviceCacheManager.getInstance() : deviceCacheManager;
Expand Down Expand Up @@ -916,4 +916,8 @@ private boolean isGaugeCaptureFrequencyMsValid(long frequencyMilliseconds) {
private boolean isSessionsMaxDurationMinutesValid(long maxDurationMin) {
return maxDurationMin > 0;
}

public RemoteConfigManager getRemoteConfigManager() {
return remoteConfigManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
public class RemoteConfigManager {

private static final AndroidLogger logger = AndroidLogger.getInstance();
private static final RemoteConfigManager instance = new RemoteConfigManager();
private static final String FIREPERF_FRC_NAMESPACE_NAME = "fireperf";
private static final long TIME_AFTER_WHICH_A_FETCH_IS_CONSIDERED_STALE_MS =
TimeUnit.HOURS.toMillis(12);
Expand All @@ -67,7 +66,7 @@ public class RemoteConfigManager {

// TODO(b/258263016): Migrate to go/firebase-android-executors
@SuppressLint("ThreadPoolCreation")
private RemoteConfigManager() {
public RemoteConfigManager() {
this(
DeviceCacheManager.getInstance(),
new ThreadPoolExecutor(
Expand Down Expand Up @@ -96,11 +95,6 @@ private RemoteConfigManager() {
this.remoteConfigFetchDelayInMs = remoteConfigFetchDelayInMs;
}

/** Gets the singleton instance. */
public static RemoteConfigManager getInstance() {
return instance;
}

/**
* Sets the {@link Provider} for {@link RemoteConfigComponent} from which we can extract the
* {@link FirebaseRemoteConfig} instance whenever it gets available.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,19 @@ public class FirebasePerformanceModule {
private final FirebaseInstallationsApi firebaseInstallations;
private final Provider<RemoteConfigComponent> remoteConfigComponentProvider;
private final Provider<TransportFactory> transportFactoryProvider;
private final SessionManager sessionManager;

public FirebasePerformanceModule(
@NonNull FirebaseApp firebaseApp,
@NonNull FirebaseInstallationsApi firebaseInstallations,
@NonNull Provider<RemoteConfigComponent> remoteConfigComponentProvider,
@NonNull Provider<TransportFactory> transportFactoryProvider) {
@NonNull Provider<TransportFactory> transportFactoryProvider,
@NonNull SessionManager sessionManager) {
this.firebaseApp = firebaseApp;
this.firebaseInstallations = firebaseInstallations;
this.remoteConfigComponentProvider = remoteConfigComponentProvider;
this.transportFactoryProvider = transportFactoryProvider;
this.sessionManager = sessionManager;
}

@Provides
Expand All @@ -68,7 +71,7 @@ Provider<TransportFactory> providesTransportFactoryProvider() {

@Provides
RemoteConfigManager providesRemoteConfigManager() {
return RemoteConfigManager.getInstance();
return ConfigResolver.getInstance().getRemoteConfigManager();
}

@Provides
Expand All @@ -78,6 +81,6 @@ ConfigResolver providesConfigResolver() {

@Provides
SessionManager providesSessionManager() {
return SessionManager.getInstance();
return sessionManager;
}
}
Loading
Loading