1
- // Copyright 2021 Google LLC
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
1
package com .google .firebase .perf .application ;
16
2
17
3
import android .app .Activity ;
45
31
import java .util .WeakHashMap ;
46
32
import java .util .concurrent .atomic .AtomicInteger ;
47
33
48
- /** Trace timer implementation to send foreground and background session log. */
49
34
public class AppStateMonitor implements ActivityLifecycleCallbacks {
50
35
51
36
private static final AndroidLogger logger = AndroidLogger .getInstance ();
@@ -56,24 +41,22 @@ public class AppStateMonitor implements ActivityLifecycleCallbacks {
56
41
private final WeakHashMap <Activity , FrameMetricsRecorder > activityToRecorderMap =
57
42
new WeakHashMap <>();
58
43
59
- // Map for holding the fragment state monitor to remove receiving the fragment state callbacks
60
44
private final WeakHashMap <Activity , FragmentStateMonitor > activityToFragmentStateMonitorMap =
61
45
new WeakHashMap <>();
62
46
private final WeakHashMap <Activity , Trace > activityToScreenTraceMap = new WeakHashMap <>();
63
47
private final Map <String , Long > metricToCountMap = new HashMap <>();
64
48
private final Set <WeakReference <AppStateCallback >> appStateSubscribers = new HashSet <>();
65
49
private Set <AppColdStartCallback > appColdStartSubscribers = new HashSet <>();
66
50
67
- /* Count for TRACE_STARTED_NOT_STOPPED */
68
51
private final AtomicInteger tsnsCount = new AtomicInteger (0 );
69
52
70
53
private final TransportManager transportManager ;
71
54
private final ConfigResolver configResolver ;
72
55
private final Clock clock ;
73
56
private final boolean screenPerformanceRecordingSupported ;
74
57
75
- private Timer resumeTime ; // The time app comes to foreground
76
- private Timer stopTime ; // The time app goes to background
58
+ private Timer resumeTime ;
59
+ private Timer stopTime ;
77
60
78
61
private ApplicationProcessState currentAppState = ApplicationProcessState .BACKGROUND ;
79
62
@@ -112,7 +95,6 @@ public static AppStateMonitor getInstance() {
112
95
}
113
96
114
97
public synchronized void registerActivityLifecycleCallbacks (Context context ) {
115
- // Make sure the callback is registered only once.
116
98
if (isRegisteredForLifecycleCallbacks ) {
117
99
return ;
118
100
}
@@ -135,9 +117,6 @@ public synchronized void unregisterActivityLifecycleCallbacks(Context context) {
135
117
}
136
118
137
119
public void incrementCount (@ NonNull String name , long value ) {
138
- // This method is called by RateLimiter.java when a log exceeds rate limit and to be dropped
139
- // It can be on any thread. sendSessionLog() method is called in callback methods from main UI
140
- // thread, thus we need synchronized access on mMetrics.
141
120
synchronized (metricToCountMap ) {
142
121
Long v = metricToCountMap .get (name );
143
122
if (v == null ) {
@@ -152,7 +131,6 @@ public void incrementTsnsCount(int value) {
152
131
tsnsCount .addAndGet (value );
153
132
}
154
133
155
- // Starts tracking the frame metrics for an activity.
156
134
private void startFrameMonitoring (Activity activity ) {
157
135
if (isScreenTraceSupported () && configResolver .isPerformanceMonitoringEnabled ()) {
158
136
FrameMetricsRecorder recorder = new FrameMetricsRecorder (activity );
@@ -176,9 +154,7 @@ public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
176
154
177
155
@ Override
178
156
public void onActivityDestroyed (Activity activity ) {
179
- // Dereference FrameMetricsRecorder from the map because it holds an Activity reference
180
157
activityToRecorderMap .remove (activity );
181
- // Dereference FragmentStateMonitor because it holds a FrameMetricsRecorder reference
182
158
if (activityToFragmentStateMonitorMap .containsKey (activity )) {
183
159
FragmentActivity fragmentActivity = (FragmentActivity ) activity ;
184
160
fragmentActivity
@@ -191,13 +167,9 @@ public void onActivityDestroyed(Activity activity) {
191
167
public synchronized void onActivityStarted (Activity activity ) {
192
168
if (isScreenTraceSupported () && configResolver .isPerformanceMonitoringEnabled ()) {
193
169
if (!activityToRecorderMap .containsKey (activity )) {
194
- // If performance monitoring is disabled at start and enabled at runtime, start monitoring
195
- // the activity as the app comes to foreground.
196
170
startFrameMonitoring (activity );
197
171
}
198
- // Starts recording frame metrics for this activity.
199
172
activityToRecorderMap .get (activity ).start ();
200
- // Start the Trace
201
173
Trace screenTrace = new Trace (getScreenTraceName (activity ), transportManager , clock , this );
202
174
screenTrace .start ();
203
175
activityToScreenTraceMap .put (activity , screenTrace );
@@ -210,92 +182,60 @@ public synchronized void onActivityStopped(Activity activity) {
210
182
sendScreenTrace (activity );
211
183
}
212
184
213
- // Last activity has its onActivityStopped called, the app goes to background.
214
185
if (activityToResumedMap .containsKey (activity )) {
215
186
activityToResumedMap .remove (activity );
216
187
if (activityToResumedMap .isEmpty ()) {
217
- // no more activity in foreground, app goes to background.
218
188
stopTime = clock .getTime ();
219
189
sendSessionLog (Constants .TraceNames .FOREGROUND_TRACE_NAME .toString (), resumeTime , stopTime );
220
- // order is important to complete _fs before triggering a sessionId change b/204362742
221
190
updateAppState (ApplicationProcessState .BACKGROUND );
222
191
}
223
192
}
224
193
}
225
194
226
195
@ Override
227
196
public synchronized void onActivityResumed (Activity activity ) {
228
- // cases:
229
- // 1. At app startup, first activity comes to foreground.
230
- // 2. app switch from background to foreground.
231
- // 3. app already in foreground, current activity is replaced by another activity, or the
232
- // current activity was paused then resumed without onStop, for example by an AlertDialog
233
197
if (activityToResumedMap .isEmpty ()) {
234
- // The first resumed activity means app comes to foreground.
235
198
resumeTime = clock .getTime ();
236
199
activityToResumedMap .put (activity , true );
237
200
if (isColdStart ) {
238
- // case 1: app startup.
239
201
updateAppState (ApplicationProcessState .FOREGROUND );
240
202
sendAppColdStartUpdate ();
241
203
isColdStart = false ;
242
204
} else {
243
- // case 2: app switch from background to foreground.
244
205
sendSessionLog (Constants .TraceNames .BACKGROUND_TRACE_NAME .toString (), stopTime , resumeTime );
245
- // order is important to complete _bs before triggering a sessionId change b/204362742
246
206
updateAppState (ApplicationProcessState .FOREGROUND );
247
207
}
248
208
} else {
249
- // case 3: app already in foreground, current activity is replaced by another activity, or the
250
- // current activity was paused then resumed without onStop, for example by an AlertDialog
251
209
activityToResumedMap .put (activity , true );
252
210
}
253
211
}
254
212
255
- /** Returns if this is the cold start of the app. */
256
213
public boolean isColdStart () {
257
214
return isColdStart ;
258
215
}
259
216
260
- /** @return current app state. */
261
217
public ApplicationProcessState getAppState () {
262
218
return currentAppState ;
263
219
}
264
220
265
- /**
266
- * Register a subscriber to receive app state update.
267
- *
268
- * @param subscriber an AppStateCallback instance.
269
- */
270
221
public void registerForAppState (WeakReference <AppStateCallback > subscriber ) {
271
222
synchronized (appStateSubscribers ) {
272
223
appStateSubscribers .add (subscriber );
273
224
}
274
225
}
275
226
276
- /**
277
- * Unregister the subscriber to stop receiving app state update.
278
- *
279
- * @param subscriber an AppStateCallback instance.
280
- */
281
227
public void unregisterForAppState (WeakReference <AppStateCallback > subscriber ) {
282
228
synchronized (appStateSubscribers ) {
283
229
appStateSubscribers .remove (subscriber );
284
230
}
285
231
}
286
232
287
- /**
288
- * Register a subscriber to receive app cold start update.
289
- *
290
- * @param subscriber the {@link AppColdStartCallback} instance.
291
- */
292
233
public void registerForAppColdStart (AppColdStartCallback subscriber ) {
293
234
synchronized (appColdStartSubscribers ) {
294
235
appColdStartSubscribers .add (subscriber );
295
236
}
296
237
}
297
238
298
- /** Send update state update to registered subscribers. */
299
239
private void updateAppState (ApplicationProcessState newState ) {
300
240
currentAppState = newState ;
301
241
synchronized (appStateSubscribers ) {
@@ -305,15 +245,12 @@ private void updateAppState(ApplicationProcessState newState) {
305
245
if (callback != null ) {
306
246
callback .onUpdateAppState (currentAppState );
307
247
} else {
308
- // The object pointing by WeakReference has already been garbage collected.
309
- // Remove it from the Set.
310
248
i .remove ();
311
249
}
312
250
}
313
251
}
314
252
}
315
253
316
- /** Send cold start update to registered subscribers. */
317
254
private void sendAppColdStartUpdate () {
318
255
synchronized (appColdStartSubscribers ) {
319
256
for (Iterator <AppColdStartCallback > i = appColdStartSubscribers .iterator (); i .hasNext (); ) {
@@ -325,11 +262,6 @@ private void sendAppColdStartUpdate() {
325
262
}
326
263
}
327
264
328
- /**
329
- * Return app is in foreground or not.
330
- *
331
- * @return true if app is in foreground, false if in background.
332
- */
333
265
public boolean isForeground () {
334
266
return currentAppState == ApplicationProcessState .FOREGROUND ;
335
267
}
@@ -340,11 +272,6 @@ public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
340
272
@ Override
341
273
public void onActivityPaused (Activity activity ) {}
342
274
343
- /**
344
- * Sends the screen trace for the provided activity.
345
- *
346
- * @param activity activity object.
347
- */
348
275
private void sendScreenTrace (Activity activity ) {
349
276
Trace screenTrace = activityToScreenTraceMap .get (activity );
350
277
if (screenTrace == null ) {
@@ -358,78 +285,46 @@ private void sendScreenTrace(Activity activity) {
358
285
return ;
359
286
}
360
287
ScreenTraceUtil .addFrameCounters (screenTrace , perfFrameMetrics .get ());
361
- // Stop and record trace
362
288
screenTrace .stop ();
363
289
}
364
290
365
- /**
366
- * Send foreground/background session trace.
367
- *
368
- * @param name trace name.
369
- * @param startTime session trace start time.
370
- * @param endTime session trace end time.
371
- */
372
291
private void sendSessionLog (String name , Timer startTime , Timer endTime ) {
373
292
if (!configResolver .isPerformanceMonitoringEnabled ()) {
374
293
return ;
375
294
}
376
- // TODO(b/117776450): We should also capture changes in the Session ID.
377
295
TraceMetric .Builder metric =
378
296
TraceMetric .newBuilder ()
379
297
.setName (name )
380
298
.setClientStartTimeUs (startTime .getMicros ())
381
299
.setDurationUs (startTime .getDurationMicros (endTime ))
382
300
.addPerfSessions (SessionManager .getInstance ().perfSession ().build ());
383
- // Atomically get mTsnsCount and set it to zero.
384
301
int tsnsCount = this .tsnsCount .getAndSet (0 );
385
302
synchronized (metricToCountMap ) {
386
303
metric .putAllCounters (metricToCountMap );
387
304
if (tsnsCount != 0 ) {
388
305
metric .putCounters (CounterNames .TRACE_STARTED_NOT_STOPPED .toString (), tsnsCount );
389
306
}
390
-
391
- // reset metrics.
392
307
metricToCountMap .clear ();
393
308
}
394
- // The Foreground and Background trace marks the transition between the two states,
395
- // so we always specify the state to be ApplicationProcessState.FOREGROUND_BACKGROUND.
396
309
transportManager .log (metric .build (), ApplicationProcessState .FOREGROUND_BACKGROUND );
397
310
}
398
311
399
- /**
400
- * Only send screen trace if FrameMetricsAggregator exists.
401
- *
402
- * @return true if supported, false if not.
403
- */
404
312
protected boolean isScreenTraceSupported () {
405
313
return screenPerformanceRecordingSupported ;
406
314
}
407
315
408
- /**
409
- * FrameMetricsAggregator first appears in Android Support Library 26.1.0. Before GMSCore SDK is
410
- * updated to 26.1.0 (b/69954793), there will be ClassNotFoundException. This method is to check
411
- * if FrameMetricsAggregator exists to avoid ClassNotFoundException.
412
- */
413
316
private static boolean isScreenPerformanceRecordingSupported () {
414
317
return FrameMetricsRecorder .isFrameMetricsRecordingSupported ();
415
318
}
416
319
417
- /** An interface to be implemented by subscribers which needs to receive app state update. */
418
320
public static interface AppStateCallback {
419
321
public void onUpdateAppState (ApplicationProcessState newState );
420
322
}
421
323
422
- /** An interface to be implemented by subscribers which needs to receive app cold start update. */
423
324
public static interface AppColdStartCallback {
424
325
public void onAppColdStart ();
425
326
}
426
327
427
- /**
428
- * Screen trace name is prefix "_st_" concatenates with Activity's class name.
429
- *
430
- * @param activity activity object.
431
- * @return screen trace name.
432
- */
433
328
public static String getScreenTraceName (Activity activity ) {
434
329
return Constants .SCREEN_TRACE_PREFIX + activity .getClass ().getSimpleName ();
435
330
}
0 commit comments