Skip to content
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cc39bb8
Remove the logging of GaugeMetadata to allow using AQS (#6678)
tejasd Feb 7, 2025
15e7bde
Implement a SessionSubscriber for Firebase Performance (#6683)
tejasd Feb 11, 2025
a963afc
convert perf session to use aqs support session id (#6832)
themiswang Apr 7, 2025
1c17c67
Update PerfSession and SessionManager to identify Legacy sessions. (#…
tejasd Apr 14, 2025
c4f90be
Add process name to application info (#6890)
themiswang Apr 17, 2025
f3a9fa8
Update GaugeManager for AQS (#6938)
tejasd May 7, 2025
173bf6b
Add unit tests for new logging in GaugeManager using GaugeCounter. (#…
tejasd May 15, 2025
ee36c0a
Refactor usage of ProcessName in Fireperf. (#6963)
tejasd May 16, 2025
dc97e23
Add missing `GaugeCounter` increment. (#6966)
tejasd May 16, 2025
ed1f917
Add gauge counter to collector tests (#6991)
tejasd May 26, 2025
5ca492f
Update Gauge logging to better match the implementation on main (#6992)
tejasd May 27, 2025
800894c
Make the AQS Test App behave more typically for Perf start up (#7158)
mrober Jul 16, 2025
d9e2cb2
Update the behaviour of the FirebasePerformanceSessionSubcriber to be…
tejasd Jul 25, 2025
6918904
Add more legacy session verification, move up gauge collection, and u…
tejasd Jul 30, 2025
8193be9
Update the version dependency of firebase sessions (#7201)
tejasd Jul 30, 2025
dc74ce3
Update legacy session enforcement and debugging logic (#7244)
tejasd Aug 13, 2025
ed6ca91
Add swapping logic to buildAndSort
tejasd Aug 13, 2025
2ca6ba1
Add logging
tejasd Aug 13, 2025
b6dcff2
Remove old set
tejasd Aug 13, 2025
25cedd4
Move comment
tejasd Aug 13, 2025
6398f6b
Update test
tejasd Aug 13, 2025
8a6f985
Update comment
tejasd Aug 13, 2025
bf94bf0
Review comments
tejasd Aug 13, 2025
6feac3a
Revert changes in sorting
tejasd Aug 13, 2025
2cef209
Merge branch 'fireperf-aqs' into td/session-sorting
tejasd Aug 14, 2025
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/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Unreleased
* [fixed] Fixed an ANR on app launch. [#4831]
* [changed] Updated `firebase-sessions` dependency to v3.0.0
* [fixed] Fixed the issues around unifying the sessions in `firebase-sessions`
and`firebase-performance`.

# 22.0.0
* [changed] **Breaking Change**: Updated minSdkVersion to API level 23 or higher.
Expand Down Expand Up @@ -442,4 +445,3 @@ updates.

# 16.1.0
* [fixed] Fixed a `SecurityException` crash on certain devices that do not have Google Play Services on them.

4 changes: 3 additions & 1 deletion firebase-perf/firebase-perf.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,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 @@ -121,7 +123,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
2 changes: 1 addition & 1 deletion firebase-perf/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
#
#

version=22.0.1
version=22.1.0
latestReleasedVersion=22.0.0
android.enableUnitTestBinaryResources=true

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
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.session.FirebasePerformanceSessionSubscriber;
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
import java.util.concurrent.Executor;

/**
Expand All @@ -41,6 +42,10 @@ public FirebasePerfEarly(
ConfigResolver configResolver = ConfigResolver.getInstance();
configResolver.setApplicationContext(context);

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

AppStateMonitor appStateMonitor = AppStateMonitor.getInstance();
appStateMonitor.registerActivityLifecycleCallbacks(context);
appStateMonitor.registerForAppColdStart(new FirebasePerformanceInitializer());
Expand All @@ -50,13 +55,5 @@ public FirebasePerfEarly(
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 @@ -30,6 +30,8 @@
import com.google.firebase.perf.injection.modules.FirebasePerformanceModule;
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 +49,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 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,18 @@ 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);

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

remoteConfigManager.setFirebaseRemoteConfigProvider(firebaseRemoteConfigProvider);
Expand All @@ -192,6 +183,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 +274,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
@@ -0,0 +1,43 @@
/*
* Copyright 2025 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.logging

import com.google.firebase.perf.session.isLegacy
import com.google.firebase.perf.v1.PerfSession as ProtoPerfSession

class FirebaseSessionsEnforcementCheck {
companion object {
/** When enabled, failed preconditions will cause assertion errors for debugging. */
@JvmStatic var enforcement: Boolean = false
private var logger: AndroidLogger = AndroidLogger.getInstance()

@JvmStatic
fun checkSessionsList(sessions: List<ProtoPerfSession>, failureMessage: String) {
if (sessions.count { it.sessionId.isLegacy() } == sessions.size) {
sessions.forEach { checkSession(it.sessionId, failureMessage) }
}
}

@JvmStatic
fun checkSession(sessionId: String, failureMessage: String) {
if (sessionId.isLegacy()) {
logger.verbose("Contains Legacy Session ${sessionId}: $failureMessage")
assert(!enforcement) { failureMessage }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

package com.google.firebase.perf.metrics;

import static com.google.firebase.perf.util.AppProcessesProvider.getAppProcesses;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
Expand Down Expand Up @@ -509,17 +511,10 @@ public static boolean isAnyAppProcessInForeground(Context appContext) {
if (activityManager == null) {
return true;
}
List<ActivityManager.RunningAppProcessInfo> appProcesses =
activityManager.getRunningAppProcesses();
if (appProcesses != null) {
String appProcessName = appContext.getPackageName();
String allowedAppProcessNamePrefix = appProcessName + ":";
List<ActivityManager.RunningAppProcessInfo> appProcesses = getAppProcesses(appContext);
if (!appProcesses.isEmpty()) {
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
continue;
}
if (appProcess.processName.equals(appProcessName)
|| appProcess.processName.startsWith(allowedAppProcessNamePrefix)) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
boolean isAppInForeground = true;

// For the case when the app is in foreground and the device transitions to sleep mode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public NetworkRequestMetricBuilder setCustomAttributes(Map<String, String> attri
* point depending upon the current {@link PerfSession} verbosity.
*
* @see GaugeManager#collectGaugeMetricOnce(Timer)
* @see PerfSession#isGaugeAndEventCollectionEnabled()
* @see PerfSession#isVerbose()
*/
public NetworkRequestMetricBuilder setRequestStartTimeMicros(long time) {
SessionManager sessionManager = SessionManager.getInstance();
Expand All @@ -234,7 +234,7 @@ public NetworkRequestMetricBuilder setRequestStartTimeMicros(long time) {
builder.setClientStartTimeUs(time);
updateSession(perfSession);

if (perfSession.isGaugeAndEventCollectionEnabled()) {
if (perfSession.isVerbose()) {
gaugeManager.collectGaugeMetricOnce(perfSession.getTimer());
}

Expand Down Expand Up @@ -265,12 +265,12 @@ public long getTimeToResponseInitiatedMicros() {
* point depending upon the current {@link PerfSession} Verbosity.
*
* @see GaugeManager#collectGaugeMetricOnce(Timer)
* @see PerfSession#isGaugeAndEventCollectionEnabled()
* @see PerfSession#isVerbose()
*/
public NetworkRequestMetricBuilder setTimeToResponseCompletedMicros(long time) {
builder.setTimeToResponseCompletedUs(time);

if (SessionManager.getInstance().perfSession().isGaugeAndEventCollectionEnabled()) {
if (SessionManager.getInstance().perfSession().isVerbose()) {
gaugeManager.collectGaugeMetricOnce(SessionManager.getInstance().perfSession().getTimer());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public void start() {

updateSession(perfSession);

if (perfSession.isGaugeAndEventCollectionEnabled()) {
if (perfSession.isVerbose()) {
gaugeManager.collectGaugeMetricOnce(perfSession.getTimer());
}
}
Expand All @@ -259,7 +259,7 @@ public void stop() {
if (!name.isEmpty()) {
transportManager.log(new TraceMetricBuilder(this).build(), getAppState());

if (SessionManager.getInstance().perfSession().isGaugeAndEventCollectionEnabled()) {
if (SessionManager.getInstance().perfSession().isVerbose()) {
gaugeManager.collectGaugeMetricOnce(
SessionManager.getInstance().perfSession().getTimer());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2025 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.session

import com.google.firebase.perf.config.ConfigResolver
import com.google.firebase.perf.logging.FirebaseSessionsEnforcementCheck.Companion.checkSession
import com.google.firebase.sessions.api.SessionSubscriber

class FirebasePerformanceSessionSubscriber(val configResolver: ConfigResolver) : SessionSubscriber {

override val sessionSubscriberName: SessionSubscriber.Name = SessionSubscriber.Name.PERFORMANCE

override val isDataCollectionEnabled: Boolean
get() = configResolver.isPerformanceCollectionEnabled ?: false

override fun onSessionChanged(sessionDetails: SessionSubscriber.SessionDetails) {
val currentPerfSession = SessionManager.getInstance().perfSession()
// TODO(b/394127311): Add logic to deal with app start gauges if necessary.
checkSession(currentPerfSession.sessionId(), "Existing session in onSessionChanged().")

val updatedSession = PerfSession.createWithId(sessionDetails.sessionId)
SessionManager.getInstance().updatePerfSession(updatedSession)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2025 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.session

import com.google.firebase.perf.util.Constants
import java.util.UUID

/** Identifies whether the [PerfSession] is legacy or not. */
fun PerfSession.isLegacy(): Boolean {
return this.sessionId().isLegacy()
}

/** Identifies whether the string is from a legacy [PerfSession]. */
fun String.isLegacy(): Boolean {
return this.startsWith(Constants.UNDEFINED_AQS_ID_PREFIX)
}

/** Creates a valid session ID for [PerfSession] that can be predictably identified as legacy. */
fun createLegacySessionId(): String {
val uuid = UUID.randomUUID().toString().replace("-", "")
return uuid.replaceRange(
0,
Constants.UNDEFINED_AQS_ID_PREFIX.length,
Constants.UNDEFINED_AQS_ID_PREFIX
)
}
Loading
Loading