Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Commit

Permalink
for #12573, added startup type and hasSavedInstance keys to app_start…
Browse files Browse the repository at this point in the history
…up_type telemetry
  • Loading branch information
sraturi committed Aug 27, 2020
1 parent 179a86c commit 18fcdb7
Show file tree
Hide file tree
Showing 12 changed files with 480 additions and 174 deletions.
37 changes: 29 additions & 8 deletions app/metrics.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,44 @@ events:
startups, not just cold startup. Note: There is a short gap
between the time application goes into background and the time
android reports the application going into the background.
Note: This metric does not cover the following cases:
Case # 1 -> a). open a link(for example, gmail) with in-app
Browser (metric report custom_tab startup) b). press home button
c). open gmail again (which brings us back to in app browser).
Step c will not report startup metric. Case # 2 -> a). open fenix
b). press home button c). launch fenix through app switcher/recent
apps. step c will not report startup type.
Note: This metric does not record souce when app opened from
task switcher: open application -> press home button -> open
recent tasks -> choose fenix. In this case will report
[source = unknown, type = hot, has_saved_instance_state = false].
extra_keys:
type:
description: |
the startup type for opening fenix. the application and HomeActivity
either needs to be created or started again. possible values are
`cold`, `warm`, `hot` or `error`. Error is for impossible cases.
Please file a bug if you see the error case.
app created AND HomeActivity created = cold
app started AND HomeActivity created = warm
app started AND HomeActivity started = hot
app created AND HomeActivity started = error
source:
description: |
The method used to open Fenix. Possible values are `app_icon`,
`custom_tab`, `link` or `unknown`
`custom_tab`, `link` or `unknown`. unknown is for startup sources
where we can't pinpoint the cause. One UNKNOWN case is the app
switcher where we don't know what variables to check to ensure this
startup wasn't caused by something else.
has_saved_instance_state:
description: |
boolean value whether or not startup type has a savedInstance.
using savedInstance, HomeActivity's previous state can be restored.
This is an optional key since it is not applicable to all the cases.
for example, when we are doing a hot start up, we cant have a
savedInstanceState therefore we report only [APP_ICON, HOT] instead
of [APP_ICON, HOT, false].
bugs:
- https://github.com/mozilla-mobile/fenix/issues/11830
- https://github.com/mozilla-mobile/fenix/issues/12573
- https://github.com/mozilla-mobile/fenix/pull/13494
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/12114#pullrequestreview-445245341
- https://github.com/mozilla-mobile/fenix/pull/13958#issuecomment-676857877
- https://github.com/mozilla-mobile/fenix/pull/13494#pullrequestreview-474050499
data_sensitivity:
- interaction
notification_emails:
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/org/mozilla/fenix/FenixApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ open class FenixApplication : LocaleAwareApplication(), Provider {
// }

initVisualCompletenessQueueAndQueueTasks()

components.appStartupTelemetry.onFenixApplicationOnCreate()
}

private fun initVisualCompletenessQueueAndQueueTasks() {
Expand Down
16 changes: 12 additions & 4 deletions app/src/main/java/org/mozilla/fenix/HomeActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,19 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {

captureSnapshotTelemetryMetrics()

setAppAllStartTelemetry(intent.toSafeIntent())
startupTelemetryOnCreateCalled(intent.toSafeIntent(), savedInstanceState != null)

StartupTimeline.onActivityCreateEndHome(this) // DO NOT MOVE ANYTHING BELOW HERE.
}

protected open fun setAppAllStartTelemetry(safeIntent: SafeIntent) {
components.appAllSourceStartTelemetry.receivedIntentInHomeActivity(safeIntent)
protected open fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
components.appStartupTelemetry.onHomeActivityOnCreate(safeIntent, hasSavedInstanceState)
}

override fun onRestart() {
super.onRestart()

components.appStartupTelemetry.onHomeActivityOnRestart()
}

@CallSuper
Expand All @@ -249,6 +255,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
message = "onResume()"
)

components.appStartupTelemetry.onHomeActivityOnResume()

components.backgroundServices.accountManagerAvailableQueue.runIfReadyOrQueue {
lifecycleScope.launch {
// Make sure accountManager is initialized.
Expand Down Expand Up @@ -398,7 +406,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
.let(::getIntentAllSource)
?.also { components.analytics.metrics.track(Event.AppReceivedIntent(it)) }

setAppAllStartTelemetry(intent.toSafeIntent())
components.appStartupTelemetry.onHomeActivityOnNewIntent(intent.toSafeIntent())
}

/**
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/org/mozilla/fenix/components/Components.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import mozilla.components.support.migration.state.MigrationStore
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.components.metrics.AppAllSourceStartTelemetry
import org.mozilla.fenix.components.metrics.AppStartupTelemetry
import org.mozilla.fenix.utils.ClipboardHandler
import org.mozilla.fenix.utils.Mockable
import org.mozilla.fenix.utils.Settings
Expand Down Expand Up @@ -83,7 +83,7 @@ class Components(private val context: Context) {
}
}

val appAllSourceStartTelemetry by lazy { AppAllSourceStartTelemetry(analytics.metrics) }
val appStartupTelemetry by lazy { AppStartupTelemetry(analytics.metrics) }

@Suppress("MagicNumber")
val addonUpdater by lazy {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/* 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 http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.components.metrics

import android.content.Intent
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import androidx.lifecycle.ProcessLifecycleOwner
import mozilla.components.support.utils.SafeIntent
import org.mozilla.fenix.components.metrics.Event.AppAllStartup
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.APP_ICON
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.CUSTOM_TAB
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.LINK
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Source.UNKNOWN
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.ERROR
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.HOT
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.COLD
import org.mozilla.fenix.components.metrics.Event.AppAllStartup.Type.WARM

/**
* Tracks application startup source, type, and whether or not activity has savedInstance to restore
* the activity from. Sample metric = [source = COLD, type = APP_ICON, hasSavedInstance = false]
* The basic idea is to collect these metrics from different phases of startup through
* [AppAllStartup] and finally report them on Activity's onResume() function.
*/
@Suppress("TooManyFunctions")
class AppStartupTelemetry(private val metrics: MetricController) : LifecycleObserver {

init {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}

private var isMetricRecordedSinceAppWasForegrounded = false
private var wasAppCreateCalledBeforeActivityCreate = false

private var onCreateData: AppAllStartup? = null
private var onRestartData: Pair<Type, Boolean?>? = null
private var onNewIntentData: Source? = null

fun onFenixApplicationOnCreate() {
wasAppCreateCalledBeforeActivityCreate = true
}

fun onHomeActivityOnCreate(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
setOnCreateData(safeIntent, hasSavedInstanceState, false)
}

fun onExternalAppBrowserOnCreate(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
setOnCreateData(safeIntent, hasSavedInstanceState, true)
}

fun onHomeActivityOnRestart() {
// we are not setting [Source] in this method since source is derived from an intent.
// therefore source gets set in onNewIntent().
onRestartData = Pair(HOT, null)
}

fun onHomeActivityOnNewIntent(safeIntent: SafeIntent) {
// we are only setting [Source] in this method since source is derived from an intent].
// other metric fields are set in onRestart()
onNewIntentData = getStartupSourceFromIntent(safeIntent, false)
}

private fun setOnCreateData(
safeIntent: SafeIntent,
hasSavedInstanceState: Boolean,
isExternalAppBrowserActivity: Boolean
) {
onCreateData = AppAllStartup(
getStartupSourceFromIntent(safeIntent, isExternalAppBrowserActivity),
getAppStartupType(),
hasSavedInstanceState
)
wasAppCreateCalledBeforeActivityCreate = false
}

private fun getAppStartupType(): Type {
return if (wasAppCreateCalledBeforeActivityCreate) COLD else WARM
}

private fun getStartupSourceFromIntent(
intent: SafeIntent,
isExternalAppBrowserActivity: Boolean
): Source {
return when {
// since the intent action is same (ACTION_VIEW) for both CUSTOM_TAB and LINK.
// we have to make sure that we are checking for CUSTOM_TAB condition first as this
// check does not rely on intent action
isExternalAppBrowserActivity -> CUSTOM_TAB
intent.isLauncherIntent -> APP_ICON
intent.action == Intent.ACTION_VIEW -> LINK
// one of the unknown case is app switcher, where we go to the recent tasks to launch
// Fenix.
else -> UNKNOWN
}
}

/**
* The reason we record metric on resume is because we need to wait for onNewIntent(), and
* we are not guaranteed that onNewIntent() will be called before or after onStart() / onRestart().
* However we are guaranteed onResume() will be called after onNewIntent() and onStart(). Source:
* https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)
*/
fun onHomeActivityOnResume() {
recordMetric()
}

private fun recordMetric() {
if (!isMetricRecordedSinceAppWasForegrounded) {
val appAllStartup: AppAllStartup = if (onCreateData != null) {
onCreateData!!
} else {
mergeOnRestartAndOnNewIntentIntoStartup()
}
metrics.track(appAllStartup)
isMetricRecordedSinceAppWasForegrounded = true
}
// we don't want any weird previous states to persist on our next metric record.
onCreateData = null
onNewIntentData = null
onRestartData = null
}

private fun mergeOnRestartAndOnNewIntentIntoStartup(): AppAllStartup {
return AppAllStartup(
onNewIntentData ?: UNKNOWN,
onRestartData?.first ?: ERROR,
onRestartData?.second
)
}

@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun onApplicationOnStop() {
// application was backgrounded, we need to record the new metric type if
// application was to come to foreground again.
// Therefore we set the isMetricRecorded flag to false.
isMetricRecordedSinceAppWasForegrounded = false
}
}
21 changes: 19 additions & 2 deletions app/src/main/java/org/mozilla/fenix/components/metrics/Event.kt
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,28 @@ sealed class Event {
get() = hashMapOf(Events.appOpenedAllStartupKeys.source to source.name)
}

data class AppOpenedAllSourceStartup(val source: Source) : Event() {
data class AppAllStartup(
val source: Source,
val type: Type,
val hasSavedInstanceState: Boolean? = null
) : Event() {
enum class Source { APP_ICON, LINK, CUSTOM_TAB, UNKNOWN }
enum class Type { COLD, WARM, HOT, ERROR }

override val extras: Map<Events.appOpenedAllStartupKeys, String>?
get() = hashMapOf(Events.appOpenedAllStartupKeys.source to source.name)
get() {
val extrasMap = hashMapOf(
Events.appOpenedAllStartupKeys.source to source.toString(),
Events.appOpenedAllStartupKeys.type to type.toString()
)
// we are only sending hasSavedInstanceState whenever we get data from
// activity's oncreate() method.
if (hasSavedInstanceState != null) {
extrasMap[Events.appOpenedAllStartupKeys.hasSavedInstanceState] =
hasSavedInstanceState.toString()
}
return extrasMap
}
}

data class CollectionSaveButtonPressed(val fromScreen: String) : Event() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private val Event.wrapper: EventWrapper<*>?
{ Events.appReceivedIntent.record(it) },
{ Events.appReceivedIntentKeys.valueOf(it) }
)
is Event.AppOpenedAllSourceStartup -> EventWrapper(
is Event.AppAllStartup -> EventWrapper(
{ Events.appOpenedAllStartup.record(it) },
{ Events.appOpenedAllStartupKeys.valueOf(it) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ open class ExternalAppBrowserActivity : HomeActivity() {

final override fun getIntentSessionId(intent: SafeIntent) = intent.getSessionId()

override fun setAppAllStartTelemetry(safeIntent: SafeIntent) {
components.appAllSourceStartTelemetry.receivedIntentInExternalAppBrowserActivity(safeIntent)
override fun startupTelemetryOnCreateCalled(safeIntent: SafeIntent, hasSavedInstanceState: Boolean) {
components.appStartupTelemetry.onExternalAppBrowserOnCreate(safeIntent, hasSavedInstanceState)
}

override fun getNavDirections(
Expand Down

0 comments on commit 18fcdb7

Please sign in to comment.