Commit f49fc6da authored by Michael Comella's avatar Michael Comella Committed by Michael Comella
Browse files

For #8803: hook up frameworkStart metric.

parent dbf733d7
......@@ -38,6 +38,7 @@ import org.mozilla.fenix.FeatureFlags.webPushIntegration
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.metrics.MetricServiceType
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.perf.StartupTimeline
import org.mozilla.fenix.push.PushFxaIntegration
import org.mozilla.fenix.push.WebPushEngineIntegration
import org.mozilla.fenix.session.NotificationSessionObserver
......@@ -49,6 +50,10 @@ import org.mozilla.fenix.utils.Settings
@Suppress("TooManyFunctions", "LargeClass")
open class FenixApplication : LocaleAwareApplication() {
init {
recordOnInit() // DO NOT MOVE ANYTHING ABOVE HERE: the timing of this measurement is critical.
private val logger = Logger("FenixApplication")
open val components by lazy { Components(this) }
......@@ -365,4 +370,10 @@ open class FenixApplication : LocaleAwareApplication() {
Logger.error("Failed to initialize web extension support", e)
protected fun recordOnInit() {
// This gets called by more than one process. Ideally we'd only run this in the main process
// but the code to check which process we're in crashes because the Context isn't valid yet.
......@@ -33,6 +33,7 @@ import mozilla.components.concept.engine.EngineView
import mozilla.components.feature.contextmenu.ext.DefaultSelectionActionDelegate
import mozilla.components.service.fxa.sync.SyncReason
......@@ -146,7 +147,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity() {
......@@ -5,8 +5,17 @@
package org.mozilla.fenix.perf
import androidx.annotation.UiThread
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import mozilla.components.service.glean.private.NoReasonCodes
import mozilla.components.service.glean.private.PingType
import org.mozilla.fenix.GleanMetrics.Pings
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.home.sessioncontrol.viewholders.topsites.TopSiteItemViewHolder
import org.mozilla.fenix.perf.StartupTimeline.onApplicationInit
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupActivity
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupDestination
import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState
......@@ -20,12 +29,25 @@ import org.mozilla.fenix.perf.StartupTimelineStateMachine.StartupState
* This class, and its dependencies, may need to be modified for any changes in startup.
* This class is not thread safe and should only be called from the main thread.
* [onApplicationInit] is called from multiple processes. To minimize overhead, the class
* dependencies are lazily initialized.
object StartupTimeline {
private var state: StartupState = StartupState.Cold(StartupDestination.UNKNOWN)
private val reportFullyDrawn = StartupReportFullyDrawn()
private val reportFullyDrawn by lazy { StartupReportFullyDrawn() }
private val frameworkStartMeasurement by lazy { StartupFrameworkStartMeasurement() }
internal val homeActivityLifecycleObserver by lazy {
fun onApplicationInit() {
// This gets called from multiple processes: don't do anything expensive. See call site for details.
fun onActivityCreateEndIntentReceiver() {
......@@ -45,3 +67,29 @@ object StartupTimeline {
state = StartupTimelineStateMachine.getNextState(state, startingActivity)
* A [LifecycleObserver] for [HomeActivity] focused on startup performance measurement.
internal class StartupHomeActivityLifecycleObserver(
private val frameworkStartMeasurement: StartupFrameworkStartMeasurement,
private val startupTimeline: PingType<NoReasonCodes> = Pings.startupTimeline
) : LifecycleObserver {
fun onStop() {
GlobalScope.launch { // use background thread due to expensive metrics.
// Ensure any last metrics are set before submission.
// Startup metrics placed in the Activity should be re-recorded each time the Activity
// is started so we need to clear the ping lifetime by submitting once per each startup.
// It's less complex to add it here rather than the visual completeness task manager.
// N.B.: this submission location may need to be changed if we add metrics outside of the
// HomeActivity startup path (e.g. if the user goes directly to a separate activity and
// closes the app, they will never hit this) to appropriately adjust for the ping lifetimes.
......@@ -13,8 +13,9 @@ import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks
* An application class which knows how to migrate Fennec data.
class MigratingFenixApplication : FenixApplication() {
init {
recordOnInit() // DO NOT MOVE ANYTHING ABOVE HERE: the timing of this measurement is critical.
PerformanceActivityLifecycleCallbacks.isTransientActivityInMigrationVariant = {
if (it is MigrationDecisionActivity) true else false
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at */
package org.mozilla.fenix.perf
import io.mockk.MockKAnnotations
import io.mockk.impl.annotations.MockK
import io.mockk.verifySequence
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import mozilla.components.service.glean.private.NoReasonCodes
import mozilla.components.service.glean.private.PingType
import org.junit.Before
import org.junit.Test
class StartupHomeActivityLifecycleObserverTest {
private lateinit var observer: StartupHomeActivityLifecycleObserver
@MockK(relaxed = true) private lateinit var frameworkStartMeasurement: StartupFrameworkStartMeasurement
@MockK(relaxed = true) private lateinit var startupTimeline: PingType<NoReasonCodes>
fun setUp() {
observer = StartupHomeActivityLifecycleObserver(frameworkStartMeasurement, startupTimeline)
fun `WHEN onStop is called THEN the metrics are set and the ping is submitted`() = runBlockingTest {
verifySequence {
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment