Skip to content

convert perf session to use aqs support session id #6832

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -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.DebugEnforcementCheck;
import com.google.firebase.perf.metrics.HttpMetric;
import com.google.firebase.perf.metrics.Trace;
import com.google.firebase.perf.session.FirebasePerformanceSessionSubscriber;
Expand All @@ -43,6 +44,7 @@
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.BuildConfig;
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
Expand Down Expand Up @@ -169,6 +171,7 @@ public static FirebasePerformance getInstance() {
this.mMetadataBundle = new ImmutableBundle(new Bundle());
return;
}
DebugEnforcementCheck.setEnforcement(BuildConfig.DEBUG);

TransportManager.getInstance()
.initialize(firebaseApp, firebaseInstallationsApi, transportFactoryProvider);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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

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

public fun checkSession(isAqsAvailable: Boolean, failureMessage: String) {
if (!isAqsAvailable) {
Companion.logger.debug(failureMessage)
assert(!enforcement) { failureMessage }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,15 @@ class FirebasePerformanceSessionSubscriber(override val isDataCollectionEnabled:
val currentPerfSession = SessionManager.getInstance().perfSession()

// A [PerfSession] was created before a session was started.
if (currentPerfSession.aqsSessionId() == null) {
currentPerfSession.setAQSId(sessionDetails)
if (!currentPerfSession.isAqsReady) {
GaugeManager.getInstance()
.logGaugeMetadata(currentPerfSession.aqsSessionId(), ApplicationProcessState.FOREGROUND)
.logGaugeMetadata(currentPerfSession.sessionId(), ApplicationProcessState.FOREGROUND)
return
}

val updatedSession = PerfSession.createWithId(UUID.randomUUID().toString())
updatedSession.setAQSId(sessionDetails)
SessionManager.getInstance().updatePerfSession(updatedSession)
GaugeManager.getInstance()
.logGaugeMetadata(updatedSession.aqsSessionId(), ApplicationProcessState.FOREGROUND)
.logGaugeMetadata(updatedSession.sessionId(), ApplicationProcessState.FOREGROUND)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,62 +23,56 @@
import com.google.firebase.perf.util.Clock;
import com.google.firebase.perf.util.Timer;
import com.google.firebase.perf.v1.SessionVerbosity;
import com.google.firebase.sessions.api.SessionSubscriber;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/** Details of a session including a unique Id and related information. */
public class PerfSession implements Parcelable {

private final String sessionId;
private final Timer creationTime;
@Nullable private String aqsSessionId;

private final String sessionId;
private boolean isGaugeAndEventCollectionEnabled = false;
public final boolean isAqsReady;

/*
* Creates a PerfSession object and decides what metrics to collect.
*/
public static PerfSession createWithId(@NonNull String sessionId) {
String prunedSessionId = sessionId.replace("-", "");
PerfSession session = new PerfSession(prunedSessionId, new Clock());
session.setGaugeAndEventCollectionEnabled(shouldCollectGaugesAndEvents());

public static PerfSession createWithId(@Nullable String aqsSessionId) {
String sessionId;
Boolean isAqsReady;
if (aqsSessionId != null) {
sessionId = aqsSessionId;
isAqsReady = true;
} else {
sessionId = UUID.randomUUID().toString().replace("-", "");
isAqsReady = false;
}
PerfSession session = new PerfSession(sessionId, new Clock(), isAqsReady);
session.setGaugeAndEventCollectionEnabled(shouldCollectGaugesAndEvents(sessionId));
return session;
}

/** Creates a PerfSession with the provided {@code sessionId} and {@code clock}. */
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public PerfSession(String sessionId, Clock clock) {
public PerfSession(String sessionId, Clock clock, boolean isAqsReady) {
this.sessionId = sessionId;
this.isAqsReady = isAqsReady;
creationTime = clock.getTime();
}

private PerfSession(@NonNull Parcel in) {
super();
sessionId = in.readString();
isGaugeAndEventCollectionEnabled = in.readByte() != 0;
isAqsReady = in.readByte() != 0;
creationTime = in.readParcelable(Timer.class.getClassLoader());
}

/** Returns the sessionId of the session. */
/** Returns the sessionId for the given session. */
public String sessionId() {
return sessionId;
}

/** Returns the AQS sessionId for the given session. */
@Nullable
public String aqsSessionId() {
return aqsSessionId;
}

/** Sets the AQS sessionId for the given session. */
public void setAQSId(SessionSubscriber.SessionDetails aqs) {
if (aqsSessionId == null) {
aqsSessionId = aqs.getSessionId();
}
}

/**
* Returns a timer object that has been seeded with the system time at which the session began.
*/
Expand All @@ -105,18 +99,6 @@ public boolean isVerbose() {
return isGaugeAndEventCollectionEnabled;
}

/** Checks if the current {@link com.google.firebase.perf.v1.PerfSession} is verbose or not. */
@VisibleForTesting
static boolean isVerbose(@NonNull com.google.firebase.perf.v1.PerfSession perfSession) {
for (SessionVerbosity sessionVerbosity : perfSession.getSessionVerbosityList()) {
if (sessionVerbosity == SessionVerbosity.GAUGES_AND_SYSTEM_EVENTS) {
return true;
}
}

return false;
}

/**
* Checks if it has been more than {@link ConfigResolver#getSessionsMaxDurationMinutes()} time
* since the creation time of the current session.
Expand All @@ -128,7 +110,6 @@ public boolean isSessionRunningTooLong() {

/** Creates and returns the proto object for PerfSession object. */
public com.google.firebase.perf.v1.PerfSession build() {
// TODO(b/394127311): Switch to using AQS.
com.google.firebase.perf.v1.PerfSession.Builder sessionMetric =
com.google.firebase.perf.v1.PerfSession.newBuilder().setSessionId(sessionId);

Expand Down Expand Up @@ -179,11 +160,10 @@ public static com.google.firebase.perf.v1.PerfSession[] buildAndSort(
}

/** If true, Session Gauge collection is enabled. */
public static boolean shouldCollectGaugesAndEvents() {
public static boolean shouldCollectGaugesAndEvents(String sessionId) {
ConfigResolver configResolver = ConfigResolver.getInstance();

return configResolver.isPerformanceMonitoringEnabled()
&& Math.random() < configResolver.getSessionsSamplingRate();
&& (Math.abs(sessionId.hashCode() % 100) < configResolver.getSessionsSamplingRate() * 100);
}

/**
Expand All @@ -207,6 +187,7 @@ public int describeContents() {
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeString(sessionId);
out.writeByte((byte) (isGaugeAndEventCollectionEnabled ? 1 : 0));
out.writeByte((byte) (isAqsReady ? 1 : 0));
out.writeParcelable(creationTime, 0);
}

Expand All @@ -224,4 +205,16 @@ public PerfSession[] newArray(int size) {
return new PerfSession[size];
}
};

/** Checks if the current {@link com.google.firebase.perf.v1.PerfSession} is verbose or not. */
@VisibleForTesting
static boolean isVerbose(@NonNull com.google.firebase.perf.v1.PerfSession perfSession) {
for (SessionVerbosity sessionVerbosity : perfSession.getSessionVerbosityList()) {
if (sessionVerbosity == SessionVerbosity.GAUGES_AND_SYSTEM_EVENTS) {
return true;
}
}

return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import androidx.annotation.Keep;
import androidx.annotation.VisibleForTesting;
import com.google.firebase.perf.application.AppStateMonitor;
import com.google.firebase.perf.logging.DebugEnforcementCheck;
import com.google.firebase.perf.session.gauges.GaugeManager;
import com.google.firebase.perf.v1.ApplicationProcessState;
import com.google.firebase.perf.v1.GaugeMetadata;
Expand All @@ -28,12 +29,10 @@
import java.util.Iterator;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

/** Session manager to generate sessionIDs and broadcast to the application. */
@Keep // Needed because of b/117526359.
public class SessionManager {

@SuppressLint("StaticFieldLeak")
private static final SessionManager instance = new SessionManager();

Expand All @@ -50,15 +49,15 @@ public static SessionManager getInstance() {

/** Returns the currently active PerfSession. */
public final PerfSession perfSession() {
DebugEnforcementCheck.Companion.checkSession(
perfSession.isAqsReady, "Access perf session from manger without aqs ready");

return perfSession;
}

private SessionManager() {
// Generate a new sessionID for every cold start.
this(
GaugeManager.getInstance(),
PerfSession.createWithId(UUID.randomUUID().toString()),
AppStateMonitor.getInstance());
// session should quickly updated by session subscriber.
this(GaugeManager.getInstance(), PerfSession.createWithId(null), AppStateMonitor.getInstance());
}

@VisibleForTesting
Expand All @@ -83,6 +82,10 @@ public void setApplicationContext(final Context appContext) {
* @see PerfSession#isSessionRunningTooLong()
*/
public void stopGaugeCollectionIfSessionRunningTooLong() {
DebugEnforcementCheck.Companion.checkSession(
perfSession.isAqsReady,
"Session is not ready while trying to stopGaugeCollectionIfSessionRunningTooLong");

if (perfSession.isSessionRunningTooLong()) {
gaugeManager.stopCollectingGauges();
}
Expand Down Expand Up @@ -156,6 +159,9 @@ public void unregisterForSessionUpdates(WeakReference<SessionAwareObject> client
}

private void startOrStopCollectingGauges(ApplicationProcessState appState) {
DebugEnforcementCheck.Companion.checkSession(
perfSession.isAqsReady, "Session is not ready while trying to startOrStopCollectingGauges");

if (perfSession.isGaugeAndEventCollectionEnabled()) {
gaugeManager.startCollectingGauges(perfSession, appState);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import com.google.firebase.perf.config.DeviceCacheManager;
import com.google.firebase.perf.metrics.NetworkRequestMetricBuilder;
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;
Expand Down Expand Up @@ -80,6 +82,7 @@ public class AppStateMonitorTest extends FirebasePerformanceTestBase {
@Before
public void setUp() {
currentTime = 0;
SessionManager.getInstance().updatePerfSession(PerfSession.createWithId("sessionId"));
initMocks(this);
doAnswer((Answer<Timer>) invocationOnMock -> new Timer(currentTime)).when(clock).getTime();

Expand Down
Loading
Loading