[fenix] Bug 1814231 - revert growth data removal from github https://github.com/mozil...
[gecko.git] / mobile / android / fenix / app / src / main / java / org / mozilla / fenix / telemetry / TelemetryMiddleware.kt
blob822f0ade6f13b5afa99e05e858b71c3c14ea4e78
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 package org.mozilla.fenix.telemetry
7 import mozilla.components.browser.state.action.BrowserAction
8 import mozilla.components.browser.state.action.ContentAction
9 import mozilla.components.browser.state.action.DownloadAction
10 import mozilla.components.browser.state.action.EngineAction
11 import mozilla.components.browser.state.action.TabListAction
12 import mozilla.components.browser.state.selector.findTab
13 import mozilla.components.browser.state.selector.findTabOrCustomTab
14 import mozilla.components.browser.state.selector.normalTabs
15 import mozilla.components.browser.state.state.BrowserState
16 import mozilla.components.browser.state.state.EngineState
17 import mozilla.components.browser.state.state.SessionState
18 import mozilla.components.concept.base.crash.CrashReporting
19 import mozilla.components.lib.state.Middleware
20 import mozilla.components.lib.state.MiddlewareContext
21 import mozilla.components.support.base.android.Clock
22 import mozilla.components.support.base.log.logger.Logger
23 import mozilla.telemetry.glean.private.NoExtras
24 import org.mozilla.fenix.Config
25 import org.mozilla.fenix.GleanMetrics.Events
26 import org.mozilla.fenix.GleanMetrics.Metrics
27 import org.mozilla.fenix.components.metrics.Event
28 import org.mozilla.fenix.components.metrics.MetricController
29 import org.mozilla.fenix.utils.Settings
30 import org.mozilla.fenix.GleanMetrics.EngineTab as EngineMetrics
32 /**
33  * [Middleware] to record telemetry in response to [BrowserAction]s.
34  *
35  * @property settings reference to the application [Settings].
36  * @property metrics [MetricController] to pass events that have been mapped from actions
37  */
38 class TelemetryMiddleware(
39     private val settings: Settings,
40     private val metrics: MetricController,
41     private val crashReporting: CrashReporting? = null,
42 ) : Middleware<BrowserState, BrowserAction> {
44     private val logger = Logger("TelemetryMiddleware")
46     @Suppress("TooGenericExceptionCaught", "ComplexMethod", "NestedBlockDepth")
47     override fun invoke(
48         context: MiddlewareContext<BrowserState, BrowserAction>,
49         next: (BrowserAction) -> Unit,
50         action: BrowserAction,
51     ) {
52         // Pre process actions
53         when (action) {
54             is ContentAction.UpdateLoadingStateAction -> {
55                 context.state.findTab(action.sessionId)?.let { tab ->
56                     // Record UriOpened event when a non-private page finishes loading
57                     if (tab.content.loading && !action.loading) {
58                         Events.normalAndPrivateUriCount.add()
59                     }
60                 }
61             }
62             is DownloadAction.AddDownloadAction -> { /* NOOP */ }
63             is EngineAction.KillEngineSessionAction -> {
64                 val tab = context.state.findTabOrCustomTab(action.tabId)
65                 onEngineSessionKilled(context.state, tab)
66             }
67             is ContentAction.CheckForFormDataExceptionAction -> {
68                 Events.formDataFailure.record(NoExtras())
69                 if (Config.channel.isNightlyOrDebug) {
70                     crashReporting?.submitCaughtException(action.throwable)
71                 }
72                 return
73             }
74             is EngineAction.LoadUrlAction -> {
75                 metrics.track(Event.GrowthData.FirstUriLoadForDay)
76             }
77             else -> {
78                 // no-op
79             }
80         }
82         next(action)
84         // Post process actions
85         when (action) {
86             is TabListAction.AddTabAction,
87             is TabListAction.AddMultipleTabsAction,
88             is TabListAction.RemoveTabAction,
89             is TabListAction.RemoveAllNormalTabsAction,
90             is TabListAction.RemoveAllTabsAction,
91             is TabListAction.RestoreAction,
92             -> {
93                 // Update/Persist tabs count whenever it changes
94                 settings.openTabsCount = context.state.normalTabs.count()
95                 if (context.state.normalTabs.isNotEmpty()) {
96                     Metrics.hasOpenTabs.set(true)
97                 } else {
98                     Metrics.hasOpenTabs.set(false)
99                 }
100             }
101             else -> {
102                 // no-op
103             }
104         }
105     }
107     /**
108      * Collecting some engine-specific (GeckoView) telemetry.
109      * https://github.com/mozilla-mobile/android-components/issues/9366
110      */
111     private fun onEngineSessionKilled(state: BrowserState, tab: SessionState?) {
112         if (tab == null) {
113             logger.debug("Could not find tab for killed engine session")
114             return
115         }
117         val isSelected = tab.id == state.selectedTabId
118         val age = tab.engineState.age()
120         // Increment the counter of killed foreground/background tabs
121         val tabKillLabel = if (isSelected) { "foreground" } else { "background" }
122         EngineMetrics.kills[tabKillLabel].add()
124         // Record the age of the engine session of the killed foreground/background tab.
125         if (isSelected && age != null) {
126             EngineMetrics.killForegroundAge.accumulateSamples(listOf(age))
127         } else if (age != null) {
128             EngineMetrics.killBackgroundAge.accumulateSamples(listOf(age))
129         }
130     }
133 @Suppress("MagicNumber")
134 private fun EngineState.age(): Long? {
135     val timestamp = (timestamp ?: return null)
136     val now = Clock.elapsedRealtime()
137     return (now - timestamp)