Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
805b678
Remove the logging of GaugeMetadata to allow using AQS (#6678)
tejasd Feb 7, 2025
257330a
Implement a SessionSubscriber for Firebase Performance (#6683)
tejasd Feb 11, 2025
d2e6ab2
convert perf session to use aqs support session id (#6832)
themiswang Apr 7, 2025
d6813e2
Update PerfSession and SessionManager to identify Legacy sessions. (#…
tejasd Apr 14, 2025
4fcf515
Add process name to application info (#6890)
themiswang Apr 17, 2025
6761cde
Update GaugeManager for AQS (#6938)
tejasd May 7, 2025
4c4a304
Add unit tests for new logging in GaugeManager using GaugeCounter. (#…
tejasd May 15, 2025
d181c2c
Refactor usage of ProcessName in Fireperf. (#6963)
tejasd May 16, 2025
1cad5d5
Add missing `GaugeCounter` increment. (#6966)
tejasd May 16, 2025
4ed0531
Add gauge counter to collector tests (#6991)
tejasd May 26, 2025
8b4adac
Update Gauge logging to better match the implementation on main (#6992)
tejasd May 27, 2025
5fdcaee
Use multi-process DataStore instead of Preferences DataStore (#6781)
mrober Mar 20, 2025
de86912
Share settings cache between running processes (#6788)
mrober Mar 24, 2025
32c9222
Fix unit tests showing warning about datastore_shared_counter (#6830)
mrober Apr 2, 2025
1656097
Create SharedSessionRepository and remove bound service (#6844)
mrober Apr 9, 2025
83d5f09
send session event based on data collection and setting config (#6852)
themiswang Apr 9, 2025
22438cd
Fix firelog send (#6855)
mrober Apr 9, 2025
149da18
Implement fake datastore for unit tests (#6874)
mrober Apr 15, 2025
eb567a7
Fallback try catch block (#6873)
themiswang Apr 15, 2025
0c36335
Add unit tests for session repo (#6878)
themiswang Apr 15, 2025
9dd77e8
background time nullable (#6886)
themiswang Apr 16, 2025
bae2d49
Disable the activity lifecycle callbacks on app delete (#6877)
mrober Apr 16, 2025
da6c776
Implement new cold app start detection heuristic (#6950)
mrober May 9, 2025
6106d06
Implement ProcessDataManager (#6961)
mrober May 15, 2025
e4d5edf
Implement cold start detection logic (#6975)
mrober May 20, 2025
92e5b41
Add benchmark to measure startup time with cleared app data (#6985)
mrober May 23, 2025
788be88
Do not notify subscribers when session didn't change (#6988)
mrober May 26, 2025
473050e
Fix the profile case with clear app data (#7000)
mrober May 30, 2025
ba27d56
Use the same tag for all logs in sessions (#7002)
mrober May 30, 2025
a8bd907
Add more trace buttons to sessions test app (#7014)
mrober Jun 6, 2025
ca9ff0f
Allow Perf to use this version of AQS (#7041)
mrober Jun 17, 2025
005a225
Address feedback on sessions-sharedrepo (#7045)
mrober Jun 18, 2025
d90ca97
Fix cold starts incorrectly using the previous firstSessionId (#7047)
mrober Jun 19, 2025
dc9e962
Make the Sessions test app more convenient to test (#7054)
mrober Jun 20, 2025
8e0f9b3
Notify Sessions when a fatal crash occurs (#7074)
mrober Jun 26, 2025
e2a8cde
Merge branch 'sessions-sharedrepo' into fireperf-aqs-bugbash
tejasd Jun 27, 2025
3990f3a
Merge branch 'fireperf-aqs' into fireperf-aqs-bugbash
tejasd Jun 27, 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
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import com.google.firebase.crashlytics.internal.persistence.FileStore;
import com.google.firebase.crashlytics.internal.settings.Settings;
import com.google.firebase.crashlytics.internal.settings.SettingsProvider;
import com.google.firebase.sessions.api.CrashEventReceiver;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
Expand Down Expand Up @@ -189,6 +190,11 @@ synchronized void handleUncaughtException(
Logger.getLogger()
.d("Handling uncaught " + "exception \"" + ex + "\" from thread " + thread.getName());

// Notify the Firebase Sessions SDK that a fatal crash has occurred.
if (!isOnDemand) {
CrashEventReceiver.notifyCrashOccurred();
}

// Capture the time that the crash occurs and close over it so that the time doesn't
// reflect when we get around to executing the task later.
final long timestampMillis = System.currentTimeMillis();
Expand Down
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 @@ -118,7 +120,7 @@ dependencies {
api("com.google.firebase:firebase-components:18.0.0")
api("com.google.firebase:firebase-config:21.5.0")
api("com.google.firebase:firebase-installations:17.2.0")
api("com.google.firebase:firebase-sessions:2.0.7") {
api(project(":firebase-sessions")) {
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 @@ -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,14 +34,18 @@
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.FirebasePerformanceSessionSubscriber;
import com.google.firebase.perf.session.SessionManager;
import com.google.firebase.perf.transport.TransportManager;
import com.google.firebase.perf.util.Constants;
import com.google.firebase.perf.util.ImmutableBundle;
import com.google.firebase.perf.util.Timer;
import com.google.firebase.remoteconfig.RemoteConfigComponent;
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
import com.google.firebase.sessions.api.SessionSubscriber;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URL;
Expand Down Expand Up @@ -92,6 +96,8 @@ public class FirebasePerformance implements FirebasePerformanceAttributable {
// once during initialization and cache it.
private final ImmutableBundle mMetadataBundle;

private final SessionSubscriber sessionSubscriber;

/** Valid HttpMethods for manual network APIs */
@StringDef({
HttpMethod.GET,
Expand Down Expand Up @@ -136,11 +142,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 +167,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());
this.sessionSubscriber = new FirebasePerformanceSessionSubscriber(false);
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 +189,9 @@ public static FirebasePerformance getInstance() {
sessionManager.setApplicationContext(appContext);

mPerformanceCollectionForceEnabledState = configResolver.getIsPerformanceCollectionEnabled();
sessionSubscriber = new FirebasePerformanceSessionSubscriber(isPerformanceCollectionEnabled());
FirebaseSessionsDependencies.register(sessionSubscriber);

if (logger.isLogcatEnabled() && isPerformanceCollectionEnabled()) {
logger.info(
String.format(
Expand Down Expand Up @@ -282,7 +282,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 Expand Up @@ -466,4 +466,9 @@ private static ImmutableBundle extractMetadata(Context appContext) {
Boolean getPerformanceCollectionForceEnabledState() {
return mPerformanceCollectionForceEnabledState;
}

@VisibleForTesting
SessionSubscriber getSessionSubscriber() {
return sessionSubscriber;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.PerfSession
import com.google.firebase.perf.session.isLegacy

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 checkSession(session: PerfSession, failureMessage: String) {
if (session.isLegacy()) {
logger.debug("legacy session ${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,35 @@
/*
* 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.logging.FirebaseSessionsEnforcementCheck
import com.google.firebase.sessions.api.SessionSubscriber

class FirebasePerformanceSessionSubscriber(override val isDataCollectionEnabled: Boolean) :
SessionSubscriber {

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

override fun onSessionChanged(sessionDetails: SessionSubscriber.SessionDetails) {
val currentPerfSession = SessionManager.getInstance().perfSession()
// TODO(b/394127311): Add logic to deal with app start gauges.
FirebaseSessionsEnforcementCheck.checkSession(currentPerfSession, "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