Skip to content

Commit b5deeb8

Browse files
committed
Update PerfSession and SessionManager to identify Legacy sessions. (#6867)
This is a subset of changes in [#6693](#6693).
1 parent 7bb1394 commit b5deeb8

13 files changed

+242
-149
lines changed

firebase-perf/firebase-perf.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,15 @@ android {
7070
buildConfigField("String", "TRANSPORT_LOG_SRC", "String.valueOf(\"FIREPERF\")")
7171
buildConfigField("Boolean", "ENFORCE_DEFAULT_LOG_SRC", "Boolean.valueOf(false)")
7272
buildConfigField("String", "FIREPERF_VERSION_NAME", "String.valueOf(\"" + property("version") + "\")")
73+
buildConfigField("Boolean", "ENFORCE_LEGACY_SESSIONS", "Boolean.valueOf(false)")
7374

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

8284
minSdkVersion project.minSdkVersion

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

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import com.google.firebase.perf.config.RemoteConfigManager;
3535
import com.google.firebase.perf.logging.AndroidLogger;
3636
import com.google.firebase.perf.logging.ConsoleUrlGenerator;
37-
import com.google.firebase.perf.logging.DebugEnforcementCheck;
37+
import com.google.firebase.perf.logging.FirebaseSessionsEnforcementCheck;
3838
import com.google.firebase.perf.metrics.HttpMetric;
3939
import com.google.firebase.perf.metrics.Trace;
4040
import com.google.firebase.perf.session.FirebasePerformanceSessionSubscriber;
@@ -44,8 +44,8 @@
4444
import com.google.firebase.perf.util.ImmutableBundle;
4545
import com.google.firebase.perf.util.Timer;
4646
import com.google.firebase.remoteconfig.RemoteConfigComponent;
47-
import com.google.firebase.sessions.BuildConfig;
4847
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
48+
import com.google.firebase.sessions.api.SessionSubscriber;
4949
import java.lang.annotation.Retention;
5050
import java.lang.annotation.RetentionPolicy;
5151
import java.net.URL;
@@ -96,6 +96,8 @@ public class FirebasePerformance implements FirebasePerformanceAttributable {
9696
// once during initialization and cache it.
9797
private final ImmutableBundle mMetadataBundle;
9898

99+
private final SessionSubscriber sessionSubscriber;
100+
99101
/** Valid HttpMethods for manual network APIs */
100102
@StringDef({
101103
HttpMethod.GET,
@@ -169,9 +171,10 @@ public static FirebasePerformance getInstance() {
169171
this.mPerformanceCollectionForceEnabledState = false;
170172
this.configResolver = configResolver;
171173
this.mMetadataBundle = new ImmutableBundle(new Bundle());
174+
this.sessionSubscriber = new FirebasePerformanceSessionSubscriber(false);
172175
return;
173176
}
174-
DebugEnforcementCheck.setEnforcement(BuildConfig.DEBUG);
177+
FirebaseSessionsEnforcementCheck.setEnforcement(BuildConfig.ENFORCE_LEGACY_SESSIONS);
175178

176179
TransportManager.getInstance()
177180
.initialize(firebaseApp, firebaseInstallationsApi, transportFactoryProvider);
@@ -186,8 +189,8 @@ public static FirebasePerformance getInstance() {
186189
sessionManager.setApplicationContext(appContext);
187190

188191
mPerformanceCollectionForceEnabledState = configResolver.getIsPerformanceCollectionEnabled();
189-
FirebaseSessionsDependencies.register(
190-
new FirebasePerformanceSessionSubscriber(isPerformanceCollectionEnabled()));
192+
sessionSubscriber = new FirebasePerformanceSessionSubscriber(isPerformanceCollectionEnabled());
193+
FirebaseSessionsDependencies.register(sessionSubscriber);
191194

192195
if (logger.isLogcatEnabled() && isPerformanceCollectionEnabled()) {
193196
logger.info(
@@ -463,4 +466,9 @@ private static ImmutableBundle extractMetadata(Context appContext) {
463466
Boolean getPerformanceCollectionForceEnabledState() {
464467
return mPerformanceCollectionForceEnabledState;
465468
}
469+
470+
@VisibleForTesting
471+
SessionSubscriber getSessionSubscriber() {
472+
return sessionSubscriber;
473+
}
466474
}

firebase-perf/src/main/java/com/google/firebase/perf/logging/DebugEnforcementCheck.kt renamed to firebase-perf/src/main/java/com/google/firebase/perf/logging/FirebaseSessionsEnforcementCheck.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,19 @@
1616

1717
package com.google.firebase.perf.logging
1818

19-
class DebugEnforcementCheck {
19+
import com.google.firebase.perf.session.PerfSession
20+
import com.google.firebase.perf.session.isLegacy
21+
22+
class FirebaseSessionsEnforcementCheck {
2023
companion object {
2124
/** When enabled, failed preconditions will cause assertion errors for debugging. */
2225
@JvmStatic var enforcement: Boolean = false
2326
private var logger: AndroidLogger = AndroidLogger.getInstance()
2427

25-
public fun checkSession(isAqsAvailable: Boolean, failureMessage: String) {
26-
if (!isAqsAvailable) {
27-
Companion.logger.debug(failureMessage)
28+
@JvmStatic
29+
fun checkSession(session: PerfSession, failureMessage: String) {
30+
if (session.isLegacy()) {
31+
logger.debug("legacy session ${session.sessionId()}: $failureMessage")
2832
assert(!enforcement) { failureMessage }
2933
}
3034
}

firebase-perf/src/main/java/com/google/firebase/perf/session/FirebasePerformanceSessionSubscriber.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@
1616

1717
package com.google.firebase.perf.session
1818

19+
import com.google.firebase.perf.logging.FirebaseSessionsEnforcementCheck
1920
import com.google.firebase.perf.session.gauges.GaugeManager
2021
import com.google.firebase.perf.v1.ApplicationProcessState
2122
import com.google.firebase.sessions.api.SessionSubscriber
22-
import java.util.UUID
2323

2424
class FirebasePerformanceSessionSubscriber(override val isDataCollectionEnabled: Boolean) :
2525
SessionSubscriber {
@@ -28,15 +28,10 @@ class FirebasePerformanceSessionSubscriber(override val isDataCollectionEnabled:
2828

2929
override fun onSessionChanged(sessionDetails: SessionSubscriber.SessionDetails) {
3030
val currentPerfSession = SessionManager.getInstance().perfSession()
31+
// TODO(b/394127311): Add logic to deal with app start gauges.
32+
FirebaseSessionsEnforcementCheck.checkSession(currentPerfSession, "onSessionChanged")
3133

32-
// A [PerfSession] was created before a session was started.
33-
if (!currentPerfSession.isAqsReady) {
34-
GaugeManager.getInstance()
35-
.logGaugeMetadata(currentPerfSession.sessionId(), ApplicationProcessState.FOREGROUND)
36-
return
37-
}
38-
39-
val updatedSession = PerfSession.createWithId(UUID.randomUUID().toString())
34+
val updatedSession = PerfSession.createWithId(sessionDetails.sessionId)
4035
SessionManager.getInstance().updatePerfSession(updatedSession)
4136
GaugeManager.getInstance()
4237
.logGaugeMetadata(updatedSession.sessionId(), ApplicationProcessState.FOREGROUND)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.perf.session
18+
19+
import com.google.firebase.perf.util.Constants
20+
import java.util.UUID
21+
22+
/** Identifies whether the [PerfSession] is legacy or not. */
23+
fun PerfSession.isLegacy(): Boolean {
24+
return this.sessionId().isLegacy()
25+
}
26+
27+
/** Identifies whether the string is from a legacy [PerfSession]. */
28+
fun String.isLegacy(): Boolean {
29+
return this.startsWith(Constants.UNDEFINED_AQS_ID_PREFIX)
30+
}
31+
32+
/** Creates a valid session ID for [PerfSession] that can be predictably identified as legacy. */
33+
fun createLegacySessionId(): String {
34+
val uuid = UUID.randomUUID().toString().replace("-", "")
35+
return uuid.replaceRange(
36+
0,
37+
Constants.UNDEFINED_AQS_ID_PREFIX.length,
38+
Constants.UNDEFINED_AQS_ID_PREFIX
39+
)
40+
}

firebase-perf/src/main/java/com/google/firebase/perf/session/PerfSession.java

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,51 +24,43 @@
2424
import com.google.firebase.perf.util.Timer;
2525
import com.google.firebase.perf.v1.SessionVerbosity;
2626
import java.util.List;
27-
import java.util.UUID;
2827
import java.util.concurrent.TimeUnit;
2928

3029
/** Details of a session including a unique Id and related information. */
3130
public class PerfSession implements Parcelable {
3231
private final Timer creationTime;
3332
private final String sessionId;
3433
private boolean isGaugeAndEventCollectionEnabled = false;
35-
public final boolean isAqsReady;
3634

3735
/*
3836
* Creates a PerfSession object and decides what metrics to collect.
3937
*/
4038
public static PerfSession createWithId(@Nullable String aqsSessionId) {
41-
String sessionId;
42-
Boolean isAqsReady;
43-
if (aqsSessionId != null) {
44-
sessionId = aqsSessionId;
45-
isAqsReady = true;
46-
} else {
47-
sessionId = UUID.randomUUID().toString().replace("-", "");
48-
isAqsReady = false;
39+
String sessionId = aqsSessionId;
40+
if (sessionId == null) {
41+
sessionId = FirebaseSessionsHelperKt.createLegacySessionId();
4942
}
50-
PerfSession session = new PerfSession(sessionId, new Clock(), isAqsReady);
51-
session.setGaugeAndEventCollectionEnabled(shouldCollectGaugesAndEvents(sessionId));
43+
PerfSession session = new PerfSession(sessionId, new Clock());
44+
session.setGaugeAndEventCollectionEnabled(session.shouldCollectGaugesAndEvents());
5245
return session;
5346
}
5447

5548
/** Creates a PerfSession with the provided {@code sessionId} and {@code clock}. */
5649
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
57-
public PerfSession(String sessionId, Clock clock, boolean isAqsReady) {
50+
public PerfSession(String sessionId, Clock clock) {
5851
this.sessionId = sessionId;
59-
this.isAqsReady = isAqsReady;
6052
creationTime = clock.getTime();
6153
}
6254

6355
private PerfSession(@NonNull Parcel in) {
6456
super();
6557
sessionId = in.readString();
6658
isGaugeAndEventCollectionEnabled = in.readByte() != 0;
67-
isAqsReady = in.readByte() != 0;
6859
creationTime = in.readParcelable(Timer.class.getClassLoader());
6960
}
7061

7162
/** Returns the sessionId for the given session. */
63+
@NonNull
7264
public String sessionId() {
7365
return sessionId;
7466
}
@@ -160,10 +152,11 @@ public static com.google.firebase.perf.v1.PerfSession[] buildAndSort(
160152
}
161153

162154
/** If true, Session Gauge collection is enabled. */
163-
public static boolean shouldCollectGaugesAndEvents(String sessionId) {
155+
public boolean shouldCollectGaugesAndEvents() {
164156
ConfigResolver configResolver = ConfigResolver.getInstance();
165157
return configResolver.isPerformanceMonitoringEnabled()
166-
&& (Math.abs(sessionId.hashCode() % 100) < configResolver.getSessionsSamplingRate() * 100);
158+
&& (Math.abs(this.sessionId.hashCode() % 100)
159+
< configResolver.getSessionsSamplingRate() * 100);
167160
}
168161

169162
/**
@@ -187,7 +180,6 @@ public int describeContents() {
187180
public void writeToParcel(@NonNull Parcel out, int flags) {
188181
out.writeString(sessionId);
189182
out.writeByte((byte) (isGaugeAndEventCollectionEnabled ? 1 : 0));
190-
out.writeByte((byte) (isAqsReady ? 1 : 0));
191183
out.writeParcelable(creationTime, 0);
192184
}
193185

firebase-perf/src/main/java/com/google/firebase/perf/session/SessionManager.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import androidx.annotation.Keep;
2020
import androidx.annotation.VisibleForTesting;
2121
import com.google.firebase.perf.application.AppStateMonitor;
22-
import com.google.firebase.perf.logging.DebugEnforcementCheck;
22+
import com.google.firebase.perf.logging.FirebaseSessionsEnforcementCheck;
2323
import com.google.firebase.perf.session.gauges.GaugeManager;
2424
import com.google.firebase.perf.v1.ApplicationProcessState;
2525
import com.google.firebase.perf.v1.GaugeMetadata;
@@ -49,8 +49,8 @@ public static SessionManager getInstance() {
4949

5050
/** Returns the currently active PerfSession. */
5151
public final PerfSession perfSession() {
52-
DebugEnforcementCheck.Companion.checkSession(
53-
perfSession.isAqsReady, "Access perf session from manger without aqs ready");
52+
FirebaseSessionsEnforcementCheck.checkSession(
53+
perfSession, "Access perf session from manger without aqs ready");
5454

5555
return perfSession;
5656
}
@@ -82,8 +82,8 @@ public void setApplicationContext(final Context appContext) {
8282
* @see PerfSession#isSessionRunningTooLong()
8383
*/
8484
public void stopGaugeCollectionIfSessionRunningTooLong() {
85-
DebugEnforcementCheck.Companion.checkSession(
86-
perfSession.isAqsReady,
85+
FirebaseSessionsEnforcementCheck.checkSession(
86+
perfSession,
8787
"Session is not ready while trying to stopGaugeCollectionIfSessionRunningTooLong");
8888

8989
if (perfSession.isSessionRunningTooLong()) {
@@ -107,6 +107,8 @@ public void updatePerfSession(PerfSession perfSession) {
107107

108108
this.perfSession = perfSession;
109109

110+
// TODO(b/394127311): Update/verify behavior for Firebase Sessions.
111+
110112
synchronized (clients) {
111113
for (Iterator<WeakReference<SessionAwareObject>> i = clients.iterator(); i.hasNext(); ) {
112114
SessionAwareObject callback = i.next().get();
@@ -159,8 +161,8 @@ public void unregisterForSessionUpdates(WeakReference<SessionAwareObject> client
159161
}
160162

161163
private void startOrStopCollectingGauges(ApplicationProcessState appState) {
162-
DebugEnforcementCheck.Companion.checkSession(
163-
perfSession.isAqsReady, "Session is not ready while trying to startOrStopCollectingGauges");
164+
FirebaseSessionsEnforcementCheck.checkSession(
165+
perfSession, "Session is not ready while trying to startOrStopCollectingGauges");
164166

165167
if (perfSession.isGaugeAndEventCollectionEnabled()) {
166168
gaugeManager.startCollectingGauges(perfSession, appState);

firebase-perf/src/main/java/com/google/firebase/perf/util/Constants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ public class Constants {
2222
public static final String PREFS_NAME = "FirebasePerfSharedPrefs";
2323
public static final String ENABLE_DISABLE = "isEnabled";
2424

25+
// A non-hex character guarantees it isn't an AQS generated UUID.
26+
// https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.uuid/-uuid/
27+
public static final String UNDEFINED_AQS_ID_PREFIX = "z";
28+
2529
public static final double MIN_SAMPLING_RATE = 0.0;
2630
public static final double MAX_SAMPLING_RATE = 1.0;
2731

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.perf.session
18+
19+
import com.google.firebase.perf.util.Clock
20+
21+
fun createTestSession(suffix: Int): PerfSession {
22+
// TODO(b/394127311): Add a method to verify legacy behavior.
23+
// only hex characters and so it's AQS.
24+
return PerfSession(testSessionId(suffix), Clock())
25+
}
26+
27+
fun testSessionId(suffix: Int): String = "abc$suffix"

0 commit comments

Comments
 (0)