18
18
import android .app .Application ;
19
19
import android .app .Application .ActivityLifecycleCallbacks ;
20
20
import android .content .Context ;
21
+ import android .os .Build ;
21
22
import android .os .Bundle ;
23
+ import android .os .Process ;
24
+ import android .view .View ;
25
+
22
26
import androidx .annotation .Keep ;
23
27
import androidx .annotation .NonNull ;
24
28
import androidx .annotation .Nullable ;
25
29
import com .google .android .gms .common .util .VisibleForTesting ;
30
+ import com .google .firebase .perf .config .ConfigResolver ;
26
31
import com .google .firebase .perf .logging .AndroidLogger ;
27
32
import com .google .firebase .perf .provider .FirebasePerfProvider ;
28
33
import com .google .firebase .perf .session .PerfSession ;
29
34
import com .google .firebase .perf .session .SessionManager ;
30
35
import com .google .firebase .perf .transport .TransportManager ;
31
36
import com .google .firebase .perf .util .Clock ;
32
37
import com .google .firebase .perf .util .Constants ;
38
+ import com .google .firebase .perf .util .FirstDrawDoneListener ;
33
39
import com .google .firebase .perf .util .Timer ;
34
40
import com .google .firebase .perf .v1 .ApplicationProcessState ;
35
41
import com .google .firebase .perf .v1 .TraceMetric ;
36
42
import java .lang .ref .WeakReference ;
37
43
import java .util .ArrayList ;
44
+ import java .util .Arrays ;
45
+ import java .util .Collections ;
38
46
import java .util .List ;
39
47
import java .util .concurrent .ExecutorService ;
40
48
import java .util .concurrent .LinkedBlockingQueue ;
@@ -89,6 +97,7 @@ public class AppStartTrace implements ActivityLifecycleCallbacks {
89
97
private Timer onCreateTime = null ;
90
98
private Timer onStartTime = null ;
91
99
private Timer onResumeTime = null ;
100
+ private Timer firstDrawDone = null ;
92
101
93
102
private PerfSession startSession ;
94
103
private boolean isStartedFromBackground = false ;
@@ -139,7 +148,7 @@ static AppStartTrace getInstance(TransportManager transportManager, Clock clock)
139
148
MAX_POOL_SIZE ,
140
149
/* keepAliveTime= */ MAX_LATENCY_BEFORE_UI_INIT + 10 ,
141
150
TimeUnit .SECONDS ,
142
- new LinkedBlockingQueue <>(1 )));
151
+ new LinkedBlockingQueue <>()));
143
152
}
144
153
}
145
154
}
@@ -178,6 +187,32 @@ public synchronized void unregisterActivityLifecycleCallbacks() {
178
187
isRegisteredForLifecycleCallbacks = false ;
179
188
}
180
189
190
+ /**
191
+ * Gets the timetamp that marks the beginning of app start, currently defined as the beginning of
192
+ * BIND_APPLICATION. Fallback to class-load time of {@link FirebasePerfProvider} when API < 24.
193
+ *
194
+ * @return {@link Timer} at the beginning of app start by Fireperf definition.
195
+ */
196
+ private static Timer getStartTimer () {
197
+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .N ) {
198
+ return Timer .ofElapsedRealtime (Process .getStartElapsedRealtime ());
199
+ }
200
+ return FirebasePerfProvider .getAppStartTime ();
201
+ }
202
+
203
+ private void recordFirstDrawDone () {
204
+ if (firstDrawDone != null ) {
205
+ return ;
206
+ }
207
+ this .firstDrawDone = clock .getTime ();
208
+ executorService .execute (() -> this .logColdStart (getStartTimer (), this .firstDrawDone , this .startSession ));
209
+
210
+ if (isRegisteredForLifecycleCallbacks ) {
211
+ // After AppStart trace is queued to be logged, we can unregister this callback.
212
+ unregisterActivityLifecycleCallbacks ();
213
+ }
214
+ }
215
+
181
216
@ Override
182
217
public synchronized void onActivityCreated (Activity activity , Bundle savedInstanceState ) {
183
218
if (isStartedFromBackground || onCreateTime != null // An activity already called onCreate()
@@ -207,11 +242,20 @@ public synchronized void onActivityStarted(Activity activity) {
207
242
@ Override
208
243
public synchronized void onActivityResumed (Activity activity ) {
209
244
if (isStartedFromBackground
210
- || onResumeTime != null // An activity already called onResume()
211
245
|| isTooLateToInitUI ) {
212
246
return ;
213
247
}
214
248
249
+ // Shadow-launch experiment of new app start time
250
+ if (ConfigResolver .getInstance ().getIsExperimentTTIDEnabled ()) {
251
+ View rootView = activity .findViewById (android .R .id .content );
252
+ FirstDrawDoneListener .registerForNextDraw (rootView , this ::recordFirstDrawDone );
253
+ }
254
+
255
+ if (onResumeTime != null ) { // An activity already called onResume()
256
+ return ;
257
+ }
258
+
215
259
appStartActivity = new WeakReference <Activity >(activity );
216
260
217
261
onResumeTime = clock .getTime ();
@@ -227,11 +271,24 @@ public synchronized void onActivityResumed(Activity activity) {
227
271
228
272
// Log the app start trace in a non-main thread.
229
273
executorService .execute (this ::logAppStartTrace );
274
+ }
230
275
231
- if (isRegisteredForLifecycleCallbacks ) {
232
- // After AppStart trace is logged, we can unregister this callback.
233
- unregisterActivityLifecycleCallbacks ();
234
- }
276
+ private void logColdStart (Timer start , Timer end , PerfSession session ) {
277
+ TraceMetric .Builder metric =
278
+ TraceMetric .newBuilder ()
279
+ .setName ("_experiment_as_ttid" )
280
+ .setClientStartTimeUs (start .getMicros ())
281
+ .setDurationUs (start .getDurationMicros (end ));
282
+
283
+ TraceMetric .Builder subtrace =
284
+ TraceMetric .newBuilder ()
285
+ .setName ("_experiment_classLoadTime" )
286
+ .setClientStartTimeUs (FirebasePerfProvider .getAppStartTime ().getMicros ())
287
+ .setDurationUs (FirebasePerfProvider .getAppStartTime ().getDurationMicros (end ));
288
+
289
+ metric .addSubtraces (subtrace ).addPerfSessions (this .startSession .build ());
290
+
291
+ transportManager .log (metric .build (), ApplicationProcessState .FOREGROUND_BACKGROUND );
235
292
}
236
293
237
294
private void logAppStartTrace () {
0 commit comments