Skip to content

Commit 35a3b4d

Browse files
tejasdmrober
authored andcommitted
Implement a SessionSubscriber for Firebase Performance (#6683)
This PR doesn't change the use of session ID to AQS - except in GaugeMetadata. I've added TODOs to identify the missing locations.
1 parent 0e58ef3 commit 35a3b4d

File tree

10 files changed

+103
-185
lines changed

10 files changed

+103
-185
lines changed

firebase-perf/firebase-perf.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ dependencies {
118118
api("com.google.firebase:firebase-components:18.0.0")
119119
api("com.google.firebase:firebase-config:21.5.0")
120120
api("com.google.firebase:firebase-installations:17.2.0")
121-
api("com.google.firebase:firebase-sessions:2.0.7") {
121+
api(project(":firebase-sessions")) {
122122
exclude group: 'com.google.firebase', module: 'firebase-common'
123123
exclude group: 'com.google.firebase', module: 'firebase-common-ktx'
124124
exclude group: 'com.google.firebase', module: 'firebase-components'

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import com.google.firebase.perf.injection.modules.FirebasePerformanceModule;
3131
import com.google.firebase.platforminfo.LibraryVersionComponent;
3232
import com.google.firebase.remoteconfig.RemoteConfigComponent;
33+
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
34+
import com.google.firebase.sessions.api.SessionSubscriber;
3335
import java.util.Arrays;
3436
import java.util.List;
3537
import java.util.concurrent.Executor;
@@ -47,6 +49,11 @@ public class FirebasePerfRegistrar implements ComponentRegistrar {
4749
private static final String LIBRARY_NAME = "fire-perf";
4850
private static final String EARLY_LIBRARY_NAME = "fire-perf-early";
4951

52+
static {
53+
// Add Firebase Performance as a dependency of Sessions when this class is loaded into memory.
54+
FirebaseSessionsDependencies.addDependency(SessionSubscriber.Name.PERFORMANCE);
55+
}
56+
5057
@Override
5158
@Keep
5259
public List<Component<?>> getComponents() {

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

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@
3636
import com.google.firebase.perf.logging.ConsoleUrlGenerator;
3737
import com.google.firebase.perf.metrics.HttpMetric;
3838
import com.google.firebase.perf.metrics.Trace;
39+
import com.google.firebase.perf.session.FirebasePerformanceSessionSubscriber;
3940
import com.google.firebase.perf.session.SessionManager;
4041
import com.google.firebase.perf.transport.TransportManager;
4142
import com.google.firebase.perf.util.Constants;
4243
import com.google.firebase.perf.util.ImmutableBundle;
4344
import com.google.firebase.perf.util.Timer;
4445
import com.google.firebase.remoteconfig.RemoteConfigComponent;
46+
import com.google.firebase.sessions.api.FirebaseSessionsDependencies;
4547
import java.lang.annotation.Retention;
4648
import java.lang.annotation.RetentionPolicy;
4749
import java.net.URL;
@@ -136,11 +138,6 @@ public static FirebasePerformance getInstance() {
136138
// to false if it's been force disabled or it is set to null if neither.
137139
@Nullable private Boolean mPerformanceCollectionForceEnabledState = null;
138140

139-
private final FirebaseApp firebaseApp;
140-
private final Provider<RemoteConfigComponent> firebaseRemoteConfigProvider;
141-
private final FirebaseInstallationsApi firebaseInstallationsApi;
142-
private final Provider<TransportFactory> transportFactoryProvider;
143-
144141
/**
145142
* Constructs the FirebasePerformance class and allows injecting dependencies.
146143
*
@@ -166,11 +163,6 @@ public static FirebasePerformance getInstance() {
166163
ConfigResolver configResolver,
167164
SessionManager sessionManager) {
168165

169-
this.firebaseApp = firebaseApp;
170-
this.firebaseRemoteConfigProvider = firebaseRemoteConfigProvider;
171-
this.firebaseInstallationsApi = firebaseInstallationsApi;
172-
this.transportFactoryProvider = transportFactoryProvider;
173-
174166
if (firebaseApp == null) {
175167
this.mPerformanceCollectionForceEnabledState = false;
176168
this.configResolver = configResolver;
@@ -191,6 +183,9 @@ public static FirebasePerformance getInstance() {
191183
sessionManager.setApplicationContext(appContext);
192184

193185
mPerformanceCollectionForceEnabledState = configResolver.getIsPerformanceCollectionEnabled();
186+
FirebaseSessionsDependencies.register(
187+
new FirebasePerformanceSessionSubscriber(isPerformanceCollectionEnabled()));
188+
194189
if (logger.isLogcatEnabled() && isPerformanceCollectionEnabled()) {
195190
logger.info(
196191
String.format(
@@ -281,7 +276,7 @@ public synchronized void setPerformanceCollectionEnabled(@Nullable Boolean enabl
281276
return;
282277
}
283278

284-
if (configResolver.getIsPerformanceCollectionDeactivated()) {
279+
if (Boolean.TRUE.equals(configResolver.getIsPerformanceCollectionDeactivated())) {
285280
logger.info("Firebase Performance is permanently disabled");
286281
return;
287282
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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.session.gauges.GaugeManager
20+
import com.google.firebase.perf.v1.ApplicationProcessState
21+
import com.google.firebase.sessions.api.SessionSubscriber
22+
import java.util.UUID
23+
24+
class FirebasePerformanceSessionSubscriber(override val isDataCollectionEnabled: Boolean) :
25+
SessionSubscriber {
26+
27+
override val sessionSubscriberName: SessionSubscriber.Name = SessionSubscriber.Name.PERFORMANCE
28+
29+
override fun onSessionChanged(sessionDetails: SessionSubscriber.SessionDetails) {
30+
val currentPerfSession = SessionManager.getInstance().perfSession()
31+
32+
// A [PerfSession] was created before a session was started.
33+
if (currentPerfSession.aqsSessionId() == null) {
34+
currentPerfSession.setAQSId(sessionDetails)
35+
GaugeManager.getInstance()
36+
.logGaugeMetadata(currentPerfSession.aqsSessionId(), ApplicationProcessState.FOREGROUND)
37+
return
38+
}
39+
40+
val updatedSession = PerfSession.createWithId(UUID.randomUUID().toString())
41+
updatedSession.setAQSId(sessionDetails)
42+
SessionManager.getInstance().updatePerfSession(updatedSession)
43+
GaugeManager.getInstance()
44+
.logGaugeMetadata(updatedSession.aqsSessionId(), ApplicationProcessState.FOREGROUND)
45+
}
46+
}

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.firebase.perf.util.Clock;
2424
import com.google.firebase.perf.util.Timer;
2525
import com.google.firebase.perf.v1.SessionVerbosity;
26+
import com.google.firebase.sessions.api.SessionSubscriber;
2627
import java.util.List;
2728
import java.util.concurrent.TimeUnit;
2829

@@ -31,6 +32,7 @@ public class PerfSession implements Parcelable {
3132

3233
private final String sessionId;
3334
private final Timer creationTime;
35+
@Nullable private String aqsSessionId;
3436

3537
private boolean isGaugeAndEventCollectionEnabled = false;
3638

@@ -59,11 +61,24 @@ private PerfSession(@NonNull Parcel in) {
5961
creationTime = in.readParcelable(Timer.class.getClassLoader());
6062
}
6163

62-
/** Returns the sessionId of the object. */
64+
/** Returns the sessionId of the session. */
6365
public String sessionId() {
6466
return sessionId;
6567
}
6668

69+
/** Returns the AQS sessionId for the given session. */
70+
@Nullable
71+
public String aqsSessionId() {
72+
return aqsSessionId;
73+
}
74+
75+
/** Sets the AQS sessionId for the given session. */
76+
public void setAQSId(SessionSubscriber.SessionDetails aqs) {
77+
if (aqsSessionId == null) {
78+
aqsSessionId = aqs.getSessionId();
79+
}
80+
}
81+
6782
/**
6883
* Returns a timer object that has been seeded with the system time at which the session began.
6984
*/
@@ -113,6 +128,7 @@ public boolean isSessionRunningTooLong() {
113128

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

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

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,20 @@
1919
import androidx.annotation.Keep;
2020
import androidx.annotation.VisibleForTesting;
2121
import com.google.firebase.perf.application.AppStateMonitor;
22-
import com.google.firebase.perf.application.AppStateUpdateHandler;
2322
import com.google.firebase.perf.session.gauges.GaugeManager;
2423
import com.google.firebase.perf.v1.ApplicationProcessState;
2524
import com.google.firebase.perf.v1.GaugeMetadata;
2625
import com.google.firebase.perf.v1.GaugeMetric;
2726
import java.lang.ref.WeakReference;
2827
import java.util.HashSet;
2928
import java.util.Iterator;
29+
import java.util.Objects;
3030
import java.util.Set;
3131
import java.util.UUID;
32-
import java.util.concurrent.ExecutorService;
33-
import java.util.concurrent.Executors;
34-
import java.util.concurrent.Future;
3532

3633
/** Session manager to generate sessionIDs and broadcast to the application. */
3734
@Keep // Needed because of b/117526359.
38-
public class SessionManager extends AppStateUpdateHandler {
35+
public class SessionManager {
3936

4037
@SuppressLint("StaticFieldLeak")
4138
private static final SessionManager instance = new SessionManager();
@@ -45,7 +42,6 @@ public class SessionManager extends AppStateUpdateHandler {
4542
private final Set<WeakReference<SessionAwareObject>> clients = new HashSet<>();
4643

4744
private PerfSession perfSession;
48-
private Future syncInitFuture;
4945

5046
/** Returns the singleton instance of SessionManager. */
5147
public static SessionManager getInstance() {
@@ -71,50 +67,14 @@ public SessionManager(
7167
this.gaugeManager = gaugeManager;
7268
this.perfSession = perfSession;
7369
this.appStateMonitor = appStateMonitor;
74-
registerForAppState();
7570
}
7671

7772
/**
7873
* Finalizes gauge initialization during cold start. This must be called before app start finishes
7974
* (currently that is before onResume finishes) to ensure gauge collection starts on time.
8075
*/
8176
public void setApplicationContext(final Context appContext) {
82-
// TODO(b/258263016): Migrate to go/firebase-android-executors
83-
@SuppressLint("ThreadPoolCreation")
84-
ExecutorService executorService = Executors.newSingleThreadExecutor();
85-
syncInitFuture =
86-
executorService.submit(
87-
() -> {
88-
gaugeManager.initializeGaugeMetadataManager(appContext);
89-
});
90-
}
91-
92-
@Override
93-
public void onUpdateAppState(ApplicationProcessState newAppState) {
94-
super.onUpdateAppState(newAppState);
95-
96-
if (appStateMonitor.isColdStart()) {
97-
// We want the Session to remain unchanged if this is a cold start of the app since we already
98-
// update the PerfSession in FirebasePerfProvider#onAttachInfo().
99-
return;
100-
}
101-
102-
if (newAppState == ApplicationProcessState.FOREGROUND) {
103-
// A new foregrounding of app will force a new sessionID generation.
104-
PerfSession session = PerfSession.createWithId(UUID.randomUUID().toString());
105-
updatePerfSession(session);
106-
} else {
107-
// If the session is running for too long, generate a new session and collect gauges as
108-
// necessary.
109-
if (perfSession.isSessionRunningTooLong()) {
110-
PerfSession session = PerfSession.createWithId(UUID.randomUUID().toString());
111-
updatePerfSession(session);
112-
} else {
113-
// For any other state change of the application, modify gauge collection state as
114-
// necessary.
115-
startOrStopCollectingGauges(newAppState);
116-
}
117-
}
77+
gaugeManager.initializeGaugeMetadataManager(appContext);
11878
}
11979

12080
/**
@@ -138,7 +98,7 @@ public void stopGaugeCollectionIfSessionRunningTooLong() {
13898
*/
13999
public void updatePerfSession(PerfSession perfSession) {
140100
// Do not update the perf session if it is the exact same sessionId.
141-
if (perfSession.sessionId() == this.perfSession.sessionId()) {
101+
if (Objects.equals(perfSession.sessionId(), this.perfSession.sessionId())) {
142102
return;
143103
}
144104

@@ -207,9 +167,4 @@ private void startOrStopCollectingGauges(ApplicationProcessState appState) {
207167
public void setPerfSession(PerfSession perfSession) {
208168
this.perfSession = perfSession;
209169
}
210-
211-
@VisibleForTesting
212-
public Future getSyncInitFuture() {
213-
return this.syncInitFuture;
214-
}
215170
}

firebase-perf/src/main/java/com/google/firebase/perf/session/gauges/GaugeManager.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ public void startCollectingGauges(
136136
final String sessionIdForScheduledTask = sessionId;
137137
final ApplicationProcessState applicationProcessStateForScheduledTask = applicationProcessState;
138138

139+
// TODO(b/394127311): Switch to using AQS.
139140
try {
140141
gaugeManagerDataCollectionJob =
141142
gaugeManagerExecutor
@@ -204,6 +205,7 @@ public void stopCollectingGauges() {
204205
gaugeManagerDataCollectionJob.cancel(false);
205206
}
206207

208+
// TODO(b/394127311): Switch to using AQS.
207209
// Flush any data that was collected for this session one last time.
208210
@SuppressWarnings("FutureReturnValueIgnored")
209211
ScheduledFuture unusedFuture =
@@ -242,6 +244,7 @@ private void syncFlush(String sessionId, ApplicationProcessState appState) {
242244
}
243245

244246
// Adding Session ID info.
247+
// TODO(b/394127311): Switch to using AQS.
245248
gaugeMetricBuilder.setSessionId(sessionId);
246249

247250
transportManager.log(gaugeMetricBuilder.build(), appState);
@@ -250,17 +253,16 @@ private void syncFlush(String sessionId, ApplicationProcessState appState) {
250253
/**
251254
* Log the Gauge Metadata information to the transport.
252255
*
253-
* @param sessionId The {@link PerfSession#sessionId()} to which the collected Gauge Metrics
256+
* @param aqsSessionId The {@link PerfSession#aqsSessionId()} ()} to which the collected Gauge Metrics
254257
* should be associated with.
255258
* @param appState The {@link ApplicationProcessState} for which these gauges are collected.
256259
* @return true if GaugeMetadata was logged, false otherwise.
257260
*/
258-
public boolean logGaugeMetadata(String sessionId, ApplicationProcessState appState) {
259-
// TODO(b/394127311): Re-introduce logging of metadata for AQS.
261+
public boolean logGaugeMetadata(String aqsSessionId, ApplicationProcessState appState) {
260262
if (gaugeMetadataManager != null) {
261263
GaugeMetric gaugeMetric =
262264
GaugeMetric.newBuilder()
263-
.setSessionId(sessionId)
265+
.setSessionId(aqsSessionId)
264266
.setGaugeMetadata(getGaugeMetadata())
265267
.build();
266268
transportManager.log(gaugeMetric, appState);

firebase-perf/src/main/java/com/google/firebase/perf/transport/TransportManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ public void log(final GaugeMetric gaugeMetric) {
354354
* {@link #isAllowedToDispatch(PerfMetric)}).
355355
*/
356356
public void log(final GaugeMetric gaugeMetric, final ApplicationProcessState appState) {
357+
// TODO(b/394127311): This *might* potentially be the right place to get AQS.
357358
executorService.execute(
358359
() -> syncLog(PerfMetric.newBuilder().setGaugeMetric(gaugeMetric), appState));
359360
}

0 commit comments

Comments
 (0)