From 74047cdceb16990a6a843ce2824ca5b7696fb0d3 Mon Sep 17 00:00:00 2001 From: Roger Yang Date: Wed, 5 May 2021 11:11:02 -0400 Subject: [PATCH] Closes #19147: Move startup metrics to right after Glean initialization (#19252) --- app/metrics.yaml | 60 +++--- .../org/mozilla/fenix/FenixApplication.kt | 185 ++++++++++++++++- .../org/mozilla/fenix/components/Analytics.kt | 2 +- .../components/metrics/GleanMetricsService.kt | 188 +----------------- .../org/mozilla/fenix/FenixApplicationTest.kt | 103 ++++++++++ .../metrics/GleanMetricsServiceTest.kt | 66 +----- docs/metrics.md | 30 +-- 7 files changed, 332 insertions(+), 302 deletions(-) diff --git a/app/metrics.yaml b/app/metrics.yaml index 8eb6325b85..19531db662 100644 --- a/app/metrics.yaml +++ b/app/metrics.yaml @@ -1269,8 +1269,8 @@ metrics: expires: "2021-08-11" preferences: - show_search_suggestions: - type: string_list + search_suggestions_enabled: + type: boolean description: > Whether or not the user has search suggestions enabled default: true @@ -1286,8 +1286,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - remote_debugging: - type: string_list + remote_debugging_enabled: + type: boolean description: > Whether or not the user has remote debugging enabled default: false @@ -1303,8 +1303,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - telemetry: - type: string_list + telemetry_enabled: + type: boolean description: > Whether or not the user has telemetry enabled. Note we should never receive a "false" value for this since telemetry would @@ -1322,8 +1322,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - tracking_protection: - type: string_list + enhanced_tracking_protection: + type: string description: > What type of enhanced tracking protection the user has enabled. "standard," "strict," "custom," or "" (if disabled) @@ -1340,8 +1340,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - search_bookmarks: - type: string_list + bookmarks_suggestion: + type: boolean description: > Whether or not the user has enabled bookmark search suggestions default: true @@ -1357,8 +1357,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - search_browsing_history: - type: string_list + browsing_history_suggestion: + type: boolean description: > Whether or not the user has enabled browsing history suggestions. default: true @@ -1374,8 +1374,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - show_clipboard_suggestions: - type: string_list + clipboard_suggestions_enabled: + type: boolean description: > Whether or not the user has enabled clipboard search suggestions. default: true @@ -1391,8 +1391,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - show_search_shortcuts: - type: string_list + search_shortcuts_enabled: + type: boolean description: > Whether or not the user has enabled search shortcuts. default: true @@ -1408,8 +1408,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - open_links_in_a_private_tab: - type: string_list + open_links_in_private: + type: boolean description: > Whether or not the user has enabled open links in a private tab. default: false @@ -1425,8 +1425,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - sync: - type: string_list + signed_in_sync: + type: boolean description: > Whether or not the user is signed into FxA default: false @@ -1461,8 +1461,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - show_voice_search: - type: string_list + voice_search_enabled: + type: boolean description: > Whether or not the user has enabled the voice search button. default: true @@ -1478,8 +1478,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - search_suggestions_private: - type: string_list + private_search_suggestions: + type: boolean description: > Whether or not the user has enabled showing search suggestions in private mode. @@ -1496,8 +1496,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - toolbar_position: - type: string_list + toolbar_position_setting: + type: string description: > The position of the toolbar default: bottom (defaults to top if the user has accessibility services) @@ -1531,8 +1531,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - open_links_in_app: - type: string_list + open_links_in_app_enabled: + type: boolean description: > Whether or not the user has the open links in apps feature enabled. default: false @@ -1548,8 +1548,8 @@ preferences: notification_emails: - fenix-core@mozilla.com expires: "2021-08-01" - theme: - type: string_list + user_theme: + type: string description: > The theme the user has enabled. "light," "dark," "system," or "battery" default: "system" for API 28+, else "light" diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 212b6e92ce..ef45de5b40 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -63,6 +63,17 @@ import org.mozilla.fenix.session.VisibilityLifecycleCallback import org.mozilla.fenix.telemetry.TelemetryLifecycleObserver import org.mozilla.fenix.utils.BrowsersCache import java.util.concurrent.TimeUnit +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.feature.search.ext.buildSearchUrl +import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine +import mozilla.components.service.fxa.manager.SyncEnginesStorage +import org.mozilla.fenix.GleanMetrics.Addons +import org.mozilla.fenix.GleanMetrics.Preferences +import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MozillaProductDetector +import org.mozilla.fenix.components.toolbar.ToolbarPosition +import org.mozilla.fenix.utils.Settings /** *The main application class for Fenix. Records data to measure initialization performance. @@ -130,13 +141,7 @@ open class FenixApplication : LocaleAwareApplication(), Provider { buildInfo = GleanBuildInfo.buildInfo ) - // Set this early to guarantee it's in every ping from here on. - Metrics.distributionId.set( - when (Config.channel.isMozillaOnline) { - true -> "MozillaOnline" - false -> "Mozilla" - } - ) + setStartupMetrics(components.core.store, settings()) } @CallSuper @@ -499,6 +504,172 @@ open class FenixApplication : LocaleAwareApplication(), Provider { } } + /** + * This function is called right after Glean is initialized. Part of this function depends on + * shared preferences to be updated so the correct value is sent with the metrics ping. + * + * The reason we're using shared preferences to track these values is due to the limitations of + * the current metrics ping design. The values set here will be sent in every metrics ping even + * if these values have not changed since the last startup. + */ + @Suppress("ComplexMethod", "LongMethod") + @VisibleForTesting + internal fun setStartupMetrics( + browserStore: BrowserStore, + settings: Settings, + browsersCache: BrowsersCache = BrowsersCache, + mozillaProductDetector: MozillaProductDetector = MozillaProductDetector + ) { + setPreferenceMetrics(settings) + with(Metrics) { + // Set this early to guarantee it's in every ping from here on. + distributionId.set( + when (Config.channel.isMozillaOnline) { + true -> "MozillaOnline" + false -> "Mozilla" + } + ) + + defaultBrowser.set(browsersCache.all(applicationContext).isDefaultBrowser) + mozillaProductDetector.getMozillaBrowserDefault(applicationContext)?.also { + defaultMozBrowser.set(it) + } + + mozillaProducts.set(mozillaProductDetector.getInstalledMozillaProducts(applicationContext)) + + adjustCampaign.set(settings.adjustCampaignId) + adjustAdGroup.set(settings.adjustAdGroup) + adjustCreative.set(settings.adjustCreative) + adjustNetwork.set(settings.adjustNetwork) + + searchWidgetInstalled.set(settings.searchWidgetInstalled) + + val openTabsCount = settings.openTabsCount + hasOpenTabs.set(openTabsCount > 0) + if (openTabsCount > 0) { + tabsOpenCount.add(openTabsCount) + } + + val topSitesSize = settings.topSitesSize + hasTopSites.set(topSitesSize > 0) + if (topSitesSize > 0) { + topSitesCount.add(topSitesSize) + } + + val installedAddonSize = settings.installedAddonsCount + Addons.hasInstalledAddons.set(installedAddonSize > 0) + if (installedAddonSize > 0) { + Addons.installedAddons.set(settings.installedAddonsList.split(',')) + } + + val enabledAddonSize = settings.enabledAddonsCount + Addons.hasEnabledAddons.set(enabledAddonSize > 0) + if (enabledAddonSize > 0) { + Addons.enabledAddons.set(settings.enabledAddonsList.split(',')) + } + + val desktopBookmarksSize = settings.desktopBookmarksSize + hasDesktopBookmarks.set(desktopBookmarksSize > 0) + if (desktopBookmarksSize > 0) { + desktopBookmarksCount.add(desktopBookmarksSize) + } + + val mobileBookmarksSize = settings.mobileBookmarksSize + hasMobileBookmarks.set(mobileBookmarksSize > 0) + if (mobileBookmarksSize > 0) { + mobileBookmarksCount.add(mobileBookmarksSize) + } + + toolbarPosition.set( + when (settings.toolbarPosition) { + ToolbarPosition.BOTTOM -> Event.ToolbarPositionChanged.Position.BOTTOM.name + ToolbarPosition.TOP -> Event.ToolbarPositionChanged.Position.TOP.name + } + ) + + tabViewSetting.set(settings.getTabViewPingString()) + closeTabSetting.set(settings.getTabTimeoutPingString()) + } + + browserStore.waitForSelectedOrDefaultSearchEngine { searchEngine -> + if (searchEngine != null) { + SearchDefaultEngine.apply { + code.set(searchEngine.id) + name.set(searchEngine.name) + submissionUrl.set(searchEngine.buildSearchUrl("")) + } + } + } + } + + @Suppress("ComplexMethod") + private fun setPreferenceMetrics( + settings: Settings + ) { + with(Preferences) { + searchSuggestionsEnabled.set(settings.shouldShowSearchSuggestions) + remoteDebuggingEnabled.set(settings.isRemoteDebuggingEnabled) + telemetryEnabled.set(settings.isTelemetryEnabled) + browsingHistorySuggestion.set(settings.shouldShowHistorySuggestions) + bookmarksSuggestion.set(settings.shouldShowBookmarkSuggestions) + clipboardSuggestionsEnabled.set(settings.shouldShowClipboardSuggestions) + searchShortcutsEnabled.set(settings.shouldShowSearchShortcuts) + openLinksInPrivate.set(settings.openLinksInAPrivateTab) + privateSearchSuggestions.set(settings.shouldShowSearchSuggestionsInPrivate) + voiceSearchEnabled.set(settings.shouldShowVoiceSearch) + openLinksInAppEnabled.set(settings.openLinksInExternalApp) + + val isLoggedIn = + components.backgroundServices.accountManager.accountProfile() != null + signedInSync.set(isLoggedIn) + + val syncedItems = SyncEnginesStorage(applicationContext).getStatus().entries.filter { + it.value + }.map { it.key.nativeName } + syncItems.set(syncedItems) + + toolbarPositionSetting.set( + when { + settings.shouldUseFixedTopToolbar -> "fixed_top" + settings.shouldUseBottomToolbar -> "bottom" + else -> "top" + } + ) + + enhancedTrackingProtection.set( + when { + !settings.shouldUseTrackingProtection -> "" + settings.useStandardTrackingProtection -> "standard" + settings.useStrictTrackingProtection -> "strict" + settings.useCustomTrackingProtection -> "custom" + else -> "" + } + ) + + val accessibilitySelection = mutableListOf() + + if (settings.switchServiceIsEnabled) { + accessibilitySelection.add("switch") + } + + if (settings.touchExplorationIsEnabled) { + accessibilitySelection.add("touch exploration") + } + + accessibilityServices.set(accessibilitySelection.toList()) + + userTheme.set( + when { + settings.shouldUseLightTheme -> "light" + settings.shouldUseDarkTheme -> "dark" + settings.shouldFollowDeviceTheme -> "system" + settings.shouldUseAutoBatteryTheme -> "battery" + else -> "" + } + ) + } + } + 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. diff --git a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt index 31beb0fa3c..d875dd63d8 100644 --- a/app/src/main/java/org/mozilla/fenix/components/Analytics.kt +++ b/app/src/main/java/org/mozilla/fenix/components/Analytics.kt @@ -92,7 +92,7 @@ class Analytics( val metrics: MetricController by lazyMonitored { MetricController.create( listOf( - GleanMetricsService(context, lazy { context.components.core.store }), + GleanMetricsService(context), AdjustMetricsService(context as Application) ), isDataTelemetryEnabled = { context.settings().isTelemetryEnabled }, diff --git a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt index 23a36fade9..74f49ccfad 100644 --- a/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt +++ b/app/src/main/java/org/mozilla/fenix/components/metrics/GleanMetricsService.kt @@ -5,10 +5,6 @@ package org.mozilla.fenix.components.metrics import android.content.Context -import mozilla.components.browser.state.store.BrowserStore -import mozilla.components.feature.search.ext.buildSearchUrl -import mozilla.components.feature.search.ext.waitForSelectedOrDefaultSearchEngine -import mozilla.components.service.fxa.manager.SyncEnginesStorage import mozilla.components.service.glean.Glean import mozilla.components.service.glean.private.NoExtraKeys import mozilla.components.support.base.log.logger.Logger @@ -46,12 +42,10 @@ import org.mozilla.fenix.GleanMetrics.Metrics import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.Pings import org.mozilla.fenix.GleanMetrics.Pocket -import org.mozilla.fenix.GleanMetrics.Preferences import org.mozilla.fenix.GleanMetrics.PrivateBrowsingMode import org.mozilla.fenix.GleanMetrics.PrivateBrowsingShortcut import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.ReaderMode -import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine import org.mozilla.fenix.GleanMetrics.SearchShortcuts import org.mozilla.fenix.GleanMetrics.SearchSuggestions import org.mozilla.fenix.GleanMetrics.SearchWidget @@ -70,11 +64,7 @@ import org.mozilla.fenix.GleanMetrics.TopSites import org.mozilla.fenix.GleanMetrics.TrackingProtection import org.mozilla.fenix.GleanMetrics.UserSpecifiedSearchEngines import org.mozilla.fenix.GleanMetrics.VoiceSearch -import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.ext.components -import org.mozilla.fenix.ext.settings -import org.mozilla.fenix.utils.BrowsersCache -import org.mozilla.fenix.utils.Settings private class EventWrapper>( private val recorder: ((Map?) -> Unit), @@ -855,11 +845,11 @@ private val Event.wrapper: EventWrapper<*>? is Event.SyncAuthFromSharedReuse, Event.SyncAuthFromSharedCopy -> null } +/** + * Service responsible for sending the activation and installation pings. + */ class GleanMetricsService( - private val context: Context, - private val store: Lazy, - private val browsersCache: BrowsersCache = BrowsersCache, - private val mozillaProductDetector: MozillaProductDetector = MozillaProductDetector + private val context: Context ) : MetricsService { override val type = MetricServiceType.Data @@ -887,181 +877,11 @@ class GleanMetricsService( // can handle events being recorded before it's initialized. Glean.registerPings(Pings) - // setStartupMetrics is not a fast function. It does not need to be done before we can consider - // ourselves initialized. So, let's do it, well, later. - setStartupMetrics(context.settings()) - } - } - - /** - * This function is called before the metrics ping is sent. Part of this function depends on - * shared preferences to be updated so the correct value is sent with the metrics ping. - * - * The reason we're using shared preferences to track some of these values is due to the - * limitations of the metrics ping. Events are only sent in a metrics ping if the user have made - * changes between each ping. However, in some cases we want current values to be sent even if - * the user have not changed anything between pings. - */ - internal fun setStartupMetrics(settings: Settings) { - setPreferenceMetrics() - with(Metrics) { - defaultBrowser.set(browsersCache.all(context).isDefaultBrowser) - mozillaProductDetector.getMozillaBrowserDefault(context)?.also { - defaultMozBrowser.set(it) - } - - mozillaProducts.set(mozillaProductDetector.getInstalledMozillaProducts(context)) - - adjustCampaign.set(settings.adjustCampaignId) - adjustAdGroup.set(settings.adjustAdGroup) - adjustCreative.set(settings.adjustCreative) - adjustNetwork.set(settings.adjustNetwork) - - searchWidgetInstalled.set(settings.searchWidgetInstalled) - - val openTabsCount = settings.openTabsCount - hasOpenTabs.set(openTabsCount > 0) - if (openTabsCount > 0) { - tabsOpenCount.add(openTabsCount) - } - - val topSitesSize = settings.topSitesSize - hasTopSites.set(topSitesSize > 0) - if (topSitesSize > 0) { - topSitesCount.add(topSitesSize) - } - - val installedAddonSize = settings.installedAddonsCount - Addons.hasInstalledAddons.set(installedAddonSize > 0) - if (installedAddonSize > 0) { - Addons.installedAddons.set(settings.installedAddonsList.split(',')) - } - - val enabledAddonSize = settings.enabledAddonsCount - Addons.hasEnabledAddons.set(enabledAddonSize > 0) - if (enabledAddonSize > 0) { - Addons.enabledAddons.set(settings.enabledAddonsList.split(',')) - } - - val desktopBookmarksSize = settings.desktopBookmarksSize - hasDesktopBookmarks.set(desktopBookmarksSize > 0) - if (desktopBookmarksSize > 0) { - desktopBookmarksCount.add(desktopBookmarksSize) - } - - val mobileBookmarksSize = settings.mobileBookmarksSize - hasMobileBookmarks.set(mobileBookmarksSize > 0) - if (mobileBookmarksSize > 0) { - mobileBookmarksCount.add(mobileBookmarksSize) - } - - toolbarPosition.set( - when (settings.toolbarPosition) { - ToolbarPosition.BOTTOM -> Event.ToolbarPositionChanged.Position.BOTTOM.name - ToolbarPosition.TOP -> Event.ToolbarPositionChanged.Position.TOP.name - } - ) - - tabViewSetting.set(settings.getTabViewPingString()) - closeTabSetting.set(settings.getTabTimeoutPingString()) - } - - store.value.waitForSelectedOrDefaultSearchEngine { searchEngine -> - if (searchEngine != null) { - SearchDefaultEngine.apply { - code.set(searchEngine.id) - name.set(searchEngine.name) - submissionUrl.set(searchEngine.buildSearchUrl("")) - } - } - activationPing.checkAndSend() installationPing.checkAndSend() } } - private fun setPreferenceMetrics() { - // We purposefully make all of our preferences the string_list format to make data analysis - // simpler. While it makes things like booleans a bit more complicated, it means all our - // preferences can be analyzed with the same dashboard and compared. - with(Preferences) { - showSearchSuggestions.set(context.settings().shouldShowSearchSuggestions.toStringList()) - remoteDebugging.set(context.settings().isRemoteDebuggingEnabled.toStringList()) - telemetry.set(context.settings().isTelemetryEnabled.toStringList()) - searchBrowsingHistory.set(context.settings().shouldShowHistorySuggestions.toStringList()) - searchBookmarks.set(context.settings().shouldShowBookmarkSuggestions.toStringList()) - showClipboardSuggestions.set(context.settings().shouldShowClipboardSuggestions.toStringList()) - showSearchShortcuts.set(context.settings().shouldShowSearchShortcuts.toStringList()) - openLinksInAPrivateTab.set(context.settings().openLinksInAPrivateTab.toStringList()) - searchSuggestionsPrivate.set(context.settings().shouldShowSearchSuggestionsInPrivate.toStringList()) - showVoiceSearch.set(context.settings().shouldShowVoiceSearch.toStringList()) - openLinksInApp.set(context.settings().openLinksInExternalApp.toStringList()) - - val isLoggedIn = - context.components.backgroundServices.accountManager.accountProfile() != null - sync.set(isLoggedIn.toStringList()) - - val syncedItems = SyncEnginesStorage(context).getStatus().entries.filter { - it.value - }.map { it.key.nativeName } - - syncItems.set(syncedItems) - - val toolbarPositionSelection = - if (context.settings().shouldUseFixedTopToolbar) { - "fixed_top" - } else if (context.settings().shouldUseBottomToolbar) { - "bottom" - } else { - "top" - } - - toolbarPosition.set(listOf(toolbarPositionSelection)) - - val etpSelection = - if (!context.settings().shouldUseTrackingProtection) { - "" - } else if (context.settings().useStandardTrackingProtection) { - "standard" - } else if (context.settings().useStrictTrackingProtection) { - "strict" - } else if (context.settings().useCustomTrackingProtection) { - "custom" - } else { - "" - } - - trackingProtection.set(listOf(etpSelection)) - - val accessibilitySelection = mutableListOf() - - if (context.settings().switchServiceIsEnabled) { - accessibilitySelection.add("switch") - } - - if (context.settings().touchExplorationIsEnabled) { - accessibilitySelection.add("touch exploration") - } - - accessibilityServices.set(accessibilitySelection.toList()) - - val themeSelection = - if (context.settings().shouldUseLightTheme) { - "light" - } else if (context.settings().shouldUseDarkTheme) { - "dark" - } else if (context.settings().shouldFollowDeviceTheme) { - "system" - } else if (context.settings().shouldUseAutoBatteryTheme) { - "battery" - } else { - "" - } - - theme.set(listOf(themeSelection)) - } - } - override fun stop() { Glean.setUploadEnabled(false) } diff --git a/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt b/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt index f1c5089603..492c24f140 100644 --- a/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt +++ b/app/src/test/java/org/mozilla/fenix/FenixApplicationTest.kt @@ -8,19 +8,30 @@ import androidx.test.core.app.ApplicationProvider import io.mockk.every import io.mockk.mockk import io.mockk.verify +import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.webextension.DisabledFlags import mozilla.components.concept.engine.webextension.Metadata import mozilla.components.concept.engine.webextension.WebExtension import mozilla.components.feature.addons.migration.DefaultSupportedAddonsChecker import mozilla.components.service.glean.testing.GleanTestRule +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mozilla.fenix.GleanMetrics.Addons +import org.mozilla.fenix.GleanMetrics.Metrics import org.mozilla.fenix.GleanMetrics.PerfStartup +import org.mozilla.fenix.GleanMetrics.Preferences +import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine +import org.mozilla.fenix.components.metrics.MozillaProductDetector +import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.utils.BrowsersCache +import org.mozilla.fenix.utils.Settings @RunWith(FenixRobolectricTestRunner::class) class FenixApplicationTest { @@ -28,10 +39,16 @@ class FenixApplicationTest { @get:Rule val gleanTestRule = GleanTestRule(ApplicationProvider.getApplicationContext()) private lateinit var application: FenixApplication + private lateinit var browsersCache: BrowsersCache + private lateinit var mozillaProductDetector: MozillaProductDetector + private lateinit var browserStore: BrowserStore @Before fun setUp() { application = ApplicationProvider.getApplicationContext() + browsersCache = mockk(relaxed = true) + mozillaProductDetector = mockk(relaxed = true) + browserStore = BrowserStore() } @Ignore("See https://github.com/mozilla-mobile/fenix/issues/18102") @@ -69,4 +86,90 @@ class FenixApplicationTest { verify { checker.unregisterForChecks() } } + + @Test + fun `WHEN setStartupMetrics is called THEN sets some base metrics`() { + val expectedAppName = "org.mozilla.fenix" + val settings: Settings = mockk() + every { browsersCache.all(any()).isDefaultBrowser } returns true + every { mozillaProductDetector.getMozillaBrowserDefault(any()) } returns expectedAppName + every { mozillaProductDetector.getInstalledMozillaProducts(any()) } returns listOf(expectedAppName) + every { settings.adjustCampaignId } returns "ID" + every { settings.adjustAdGroup } returns "group" + every { settings.adjustCreative } returns "creative" + every { settings.adjustNetwork } returns "network" + every { settings.searchWidgetInstalled } returns true + every { settings.openTabsCount } returns 1 + every { settings.topSitesSize } returns 2 + every { settings.installedAddonsCount } returns 3 + every { settings.installedAddonsList } returns "test1,test2,test3" + every { settings.enabledAddonsCount } returns 2 + every { settings.enabledAddonsList } returns "test1,test2" + every { settings.desktopBookmarksSize } returns 4 + every { settings.mobileBookmarksSize } returns 5 + every { settings.toolbarPosition } returns ToolbarPosition.BOTTOM + every { settings.getTabViewPingString() } returns "test" + every { settings.getTabTimeoutPingString() } returns "test" + every { settings.shouldShowSearchSuggestions } returns true + every { settings.shouldUseTrackingProtection } returns true + every { settings.isRemoteDebuggingEnabled } returns true + every { settings.isTelemetryEnabled } returns true + every { settings.shouldShowHistorySuggestions } returns true + every { settings.shouldShowBookmarkSuggestions } returns true + every { settings.shouldShowClipboardSuggestions } returns true + every { settings.shouldShowSearchShortcuts } returns true + every { settings.openLinksInAPrivateTab } returns true + every { settings.shouldShowSearchSuggestionsInPrivate } returns true + every { settings.shouldShowVoiceSearch } returns true + every { settings.openLinksInExternalApp } returns true + every { settings.shouldUseFixedTopToolbar } returns true + every { settings.useStandardTrackingProtection } returns true + every { settings.switchServiceIsEnabled } returns true + every { settings.touchExplorationIsEnabled } returns true + every { settings.shouldUseLightTheme } returns true + + application.setStartupMetrics(browserStore, settings, browsersCache, mozillaProductDetector) + + // Verify that browser defaults metrics are set. + assertEquals("Mozilla", Metrics.distributionId.testGetValue()) + assertEquals(true, Metrics.defaultBrowser.testGetValue()) + assertEquals(expectedAppName, Metrics.defaultMozBrowser.testGetValue()) + assertEquals(listOf(expectedAppName), Metrics.mozillaProducts.testGetValue()) + assertEquals("ID", Metrics.adjustCampaign.testGetValue()) + assertEquals("group", Metrics.adjustAdGroup.testGetValue()) + assertEquals("creative", Metrics.adjustCreative.testGetValue()) + assertEquals("network", Metrics.adjustNetwork.testGetValue()) + assertEquals(true, Metrics.searchWidgetInstalled.testGetValue()) + assertEquals(true, Metrics.hasOpenTabs.testGetValue()) + assertEquals(1, Metrics.tabsOpenCount.testGetValue()) + assertEquals(true, Metrics.hasTopSites.testGetValue()) + assertEquals(2, Metrics.topSitesCount.testGetValue()) + assertEquals(true, Addons.hasInstalledAddons.testGetValue()) + assertEquals(listOf("test1", "test2", "test3"), Addons.installedAddons.testGetValue()) + assertEquals(true, Addons.hasEnabledAddons.testGetValue()) + assertEquals(listOf("test1", "test2"), Addons.enabledAddons.testGetValue()) + assertEquals(true, Preferences.searchSuggestionsEnabled.testGetValue()) + assertEquals(true, Preferences.remoteDebuggingEnabled.testGetValue()) + assertEquals(true, Preferences.telemetryEnabled.testGetValue()) + assertEquals(true, Preferences.browsingHistorySuggestion.testGetValue()) + assertEquals(true, Preferences.bookmarksSuggestion.testGetValue()) + assertEquals(true, Preferences.clipboardSuggestionsEnabled.testGetValue()) + assertEquals(true, Preferences.searchShortcutsEnabled.testGetValue()) + assertEquals(true, Preferences.openLinksInPrivate.testGetValue()) + assertEquals(true, Preferences.privateSearchSuggestions.testGetValue()) + assertEquals(true, Preferences.voiceSearchEnabled.testGetValue()) + assertEquals(true, Preferences.openLinksInAppEnabled.testGetValue()) + assertEquals(true, Preferences.signedInSync.testGetValue()) + assertEquals(emptyList(), Preferences.syncItems.testGetValue()) + assertEquals("fixed_top", Preferences.toolbarPositionSetting.testGetValue()) + assertEquals("standard", Preferences.enhancedTrackingProtection.testGetValue()) + assertEquals(listOf("switch", "touch exploration"), Preferences.accessibilityServices.testGetValue()) + assertEquals("light", Preferences.userTheme.testGetValue()) + + // Verify that search engine defaults are NOT set. This test does + // not mock most of the objects telemetry is collected from. + assertFalse(SearchDefaultEngine.code.testHasValue()) + assertFalse(SearchDefaultEngine.name.testHasValue()) + assertFalse(SearchDefaultEngine.submissionUrl.testHasValue()) + } } diff --git a/app/src/test/java/org/mozilla/fenix/components/metrics/GleanMetricsServiceTest.kt b/app/src/test/java/org/mozilla/fenix/components/metrics/GleanMetricsServiceTest.kt index 3ae76d2a17..be12b174a3 100644 --- a/app/src/test/java/org/mozilla/fenix/components/metrics/GleanMetricsServiceTest.kt +++ b/app/src/test/java/org/mozilla/fenix/components/metrics/GleanMetricsServiceTest.kt @@ -5,10 +5,6 @@ package org.mozilla.fenix.components.metrics import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.MockK -import io.mockk.mockk -import mozilla.components.browser.state.store.BrowserStore import mozilla.components.service.glean.testing.GleanTestRule import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals @@ -23,15 +19,10 @@ import org.mozilla.fenix.GleanMetrics.Awesomebar import org.mozilla.fenix.GleanMetrics.BookmarksManagement import org.mozilla.fenix.GleanMetrics.Events import org.mozilla.fenix.GleanMetrics.History -import org.mozilla.fenix.GleanMetrics.Metrics -import org.mozilla.fenix.GleanMetrics.SearchDefaultEngine import org.mozilla.fenix.GleanMetrics.SyncedTabs import org.mozilla.fenix.GleanMetrics.TabsTray import org.mozilla.fenix.GleanMetrics.TabsTrayCfr -import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.helpers.FenixRobolectricTestRunner -import org.mozilla.fenix.utils.BrowsersCache -import org.mozilla.fenix.utils.Settings @RunWith(FenixRobolectricTestRunner::class) class GleanMetricsServiceTest { @@ -40,66 +31,11 @@ class GleanMetricsServiceTest { private lateinit var gleanService: GleanMetricsService - @MockK private lateinit var browsersCache: BrowsersCache - @MockK private lateinit var mozillaProductDetector: MozillaProductDetector - @Before fun setup() { MockKAnnotations.init(this) - val store = BrowserStore() - gleanService = GleanMetricsService(testContext, lazy { store }, browsersCache, mozillaProductDetector) - } - - @Test - fun `setStartupMetrics sets some base metrics`() { - val expectedAppName = "org.mozilla.fenix" - val settings: Settings = mockk() - every { browsersCache.all(any()).isDefaultBrowser } returns true - every { mozillaProductDetector.getMozillaBrowserDefault(any()) } returns expectedAppName - every { mozillaProductDetector.getInstalledMozillaProducts(any()) } returns listOf(expectedAppName) - every { settings.adjustCampaignId } returns "ID" - every { settings.adjustAdGroup } returns "group" - every { settings.adjustCreative } returns "creative" - every { settings.adjustNetwork } returns "network" - every { settings.searchWidgetInstalled } returns true - every { settings.openTabsCount } returns 1 - every { settings.topSitesSize } returns 2 - every { settings.installedAddonsCount } returns 3 - every { settings.installedAddonsList } returns "test1,test2,test3" - every { settings.enabledAddonsCount } returns 2 - every { settings.enabledAddonsList } returns "test1,test2" - every { settings.desktopBookmarksSize } returns 4 - every { settings.mobileBookmarksSize } returns 5 - every { settings.toolbarPosition } returns ToolbarPosition.BOTTOM - every { settings.getTabViewPingString() } returns "test" - every { settings.getTabTimeoutPingString() } returns "test" - - gleanService.setStartupMetrics(settings) - - // Verify that browser defaults metrics are set. - assertEquals(true, Metrics.defaultBrowser.testGetValue()) - assertEquals(expectedAppName, Metrics.defaultMozBrowser.testGetValue()) - assertEquals(listOf(expectedAppName), Metrics.mozillaProducts.testGetValue()) - assertEquals("ID", Metrics.adjustCampaign.testGetValue()) - assertEquals("group", Metrics.adjustAdGroup.testGetValue()) - assertEquals("creative", Metrics.adjustCreative.testGetValue()) - assertEquals("network", Metrics.adjustNetwork.testGetValue()) - assertEquals(true, Metrics.searchWidgetInstalled.testGetValue()) - assertEquals(true, Metrics.hasOpenTabs.testGetValue()) - assertEquals(1, Metrics.tabsOpenCount.testGetValue()) - assertEquals(true, Metrics.hasTopSites.testGetValue()) - assertEquals(2, Metrics.topSitesCount.testGetValue()) - assertEquals(true, Addons.hasInstalledAddons.testGetValue()) - assertEquals(listOf("test1", "test2", "test3"), Addons.installedAddons.testGetValue()) - assertEquals(true, Addons.hasEnabledAddons.testGetValue()) - assertEquals(listOf("test1", "test2"), Addons.enabledAddons.testGetValue()) - - // Verify that search engine defaults are NOT set. This test does - // not mock most of the objects telemetry is collected from. - assertFalse(SearchDefaultEngine.code.testHasValue()) - assertFalse(SearchDefaultEngine.name.testHasValue()) - assertFalse(SearchDefaultEngine.submissionUrl.testHasValue()) + gleanService = GleanMetricsService(testContext) } @Test diff --git a/docs/metrics.md b/docs/metrics.md index 98a127f150..86d8670984 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -369,22 +369,22 @@ In addition to those built-in metrics, the following metrics are added to the pi | perf.startup.home_fragment_on_view_created |[timing_distribution](https://mozilla.github.io/glean/book/user/metrics/timing_distribution.html) |The duration of `HomeFragment.onViewCreated`. |[mozilla-mobile/fenix#18558](https://github.com/mozilla-mobile/fenix/pull/18558#issue-596791848)||2021-08-11 |1 | | perf.startup.startup_type |[labeled_counter](https://mozilla.github.io/glean/book/user/metrics/labeled_counters.html) |Indicates how the browser was started. The label is divided into two variables. `state` is how cached the browser is when started. `path` is what code path we are expected to take. Together, they create a combined label: `state_path`. For brevity, the specific states are documented in the [Fenix perf glossary](https://wiki.mozilla.org/index.php?title=Performance/Fenix/Glossary).

This implementation is intended to be simple, not comprehensive. We list the implications below.

These ways of opening the app undesirably adds events to our primary buckets (non-`unknown` cases):
- App switcher cold/warm: `cold/warm_` + duplicates path from previous launch
- Home screen shortcuts: `*_view`
- An Intent is sent internally that's uses `ACTION_MAIN` or `ACTION_VIEW` could be: `*_main/view` (unknown if this ever happens)
- A command-line launch uses `ACTION_MAIN` or `ACTION_VIEW` could be: `*_main/view`

These ways of opening the app undesirably do not add their events to our primary buckets:
- Close and reopen the app very quickly: no event is recorded.

These ways of opening the app don't affect our primary buckets:
- App switcher hot: `hot_unknown`
- PWA (all states): `unknown_unknown`
- Custom tab: `unknown_view`
- Cold start where a service or other non-activity starts the process (not manually tested) - this seems to happen if you have the homescreen widget: `unknown_*`
- Another activity is drawn before HomeActivity (e.g. widget voice search): `unknown_*`
- Widget text search: `*_unknown`

In addition to the events above, the `unknown` state may be chosen when we were unable to determine a cause due to implementation details or the API was used incorrectly. We may be able to record the events listed above into different buckets but we kept the implementation simple for now.

N.B.: for implementation simplicity, we duplicate the logic in app that determines `path` so it's not perfectly accurate. In one way, we record we is intended to happen rather than what actually happened (e.g. the user may click a link so we record VIEW but the app does a MAIN by going to the homescreen because the link was invalid). |[mozilla-mobile/fenix#19028](https://github.com/mozilla-mobile/fenix/pull/19028)|
  • cold_main
  • cold_view
  • cold_unknown
  • warm_main
  • warm_view
  • warm_unknown
  • hot_main
  • hot_view
  • hot_unknown
  • unknown_main
  • unknown_view
  • unknown_unknown
|2021-10-09 |2 | | preferences.accessibility_services |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has touch exploration or switch services enabled. These are built into the Android OS, not Fenix prefs. default: "" |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.open_links_in_a_private_tab |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has enabled open links in a private tab. default: false |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.open_links_in_app |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has the open links in apps feature enabled. default: false |[mozilla-mobile/fenix#11446](https://github.com/mozilla-mobile/fenix/pull/11446), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.remote_debugging |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has remote debugging enabled default: false |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.search_bookmarks |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has enabled bookmark search suggestions default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.search_browsing_history |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has enabled browsing history suggestions. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.search_suggestions_private |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has enabled showing search suggestions in private mode. default: false (we prompt the user, asking them to make a selection) |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.show_clipboard_suggestions |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has enabled clipboard search suggestions. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.show_search_shortcuts |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has enabled search shortcuts. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.show_search_suggestions |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has search suggestions enabled default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.show_voice_search |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has enabled the voice search button. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.sync |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user is signed into FxA default: false |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.bookmarks_suggestion |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled bookmark search suggestions default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.browsing_history_suggestion |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled browsing history suggestions. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.clipboard_suggestions_enabled |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled clipboard search suggestions. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.enhanced_tracking_protection |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |What type of enhanced tracking protection the user has enabled. "standard," "strict," "custom," or "" (if disabled) default: "standard" |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.open_links_in_app_enabled |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has the open links in apps feature enabled. default: false |[mozilla-mobile/fenix#11446](https://github.com/mozilla-mobile/fenix/pull/11446), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.open_links_in_private |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled open links in a private tab. default: false |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.private_search_suggestions |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled showing search suggestions in private mode. default: false (we prompt the user, asking them to make a selection) |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.remote_debugging_enabled |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has remote debugging enabled default: false |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.search_shortcuts_enabled |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled search shortcuts. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.search_suggestions_enabled |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has search suggestions enabled default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.signed_in_sync |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user is signed into FxA default: false |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | | preferences.sync_items |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |The list of items the user has chosen to sync with FxA. default: "" if the user is signed out. Otherwise defaults to whatever is set in their FxA account. New accounts set: [bookmarks, history, passwords, tabs] |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.telemetry |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |Whether or not the user has telemetry enabled. Note we should never receive a "false" value for this since telemetry would not send in that case. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.theme |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |The theme the user has enabled. "light," "dark," "system," or "battery" default: "system" for API 28+, else "light" |[mozilla-mobile/fenix#11446](https://github.com/mozilla-mobile/fenix/pull/11446), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.toolbar_position |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |The position of the toolbar default: bottom (defaults to top if the user has accessibility services) |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | -| preferences.tracking_protection |[string_list](https://mozilla.github.io/glean/book/user/metrics/string_list.html) |What type of enhanced tracking protection the user has enabled. "standard," "strict," "custom," or "" (if disabled) default: "standard" |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.telemetry_enabled |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has telemetry enabled. Note we should never receive a "false" value for this since telemetry would not send in that case. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.toolbar_position_setting |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The position of the toolbar default: bottom (defaults to top if the user has accessibility services) |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.user_theme |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |The theme the user has enabled. "light," "dark," "system," or "battery" default: "system" for API 28+, else "light" |[mozilla-mobile/fenix#11446](https://github.com/mozilla-mobile/fenix/pull/11446), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | +| preferences.voice_search_enabled |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Whether or not the user has enabled the voice search button. default: true |[mozilla-mobile/fenix#11211](https://github.com/mozilla-mobile/fenix/pull/11211), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |2 | | search.default_engine.code |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |If the search engine is pre-loaded with Fenix this value will be the search engine identifier. If it's a custom search engine (defined: https://github.com/mozilla-mobile/fenix/issues/1607) the value will be "custom" |[mozilla-mobile/fenix#1606](https://github.com/mozilla-mobile/fenix/pull/1606), [mozilla-mobile/fenix#5216](https://github.com/mozilla-mobile/fenix/pull/5216), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |1, 2 | | search.default_engine.name |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |If the search engine is pre-loaded with Fenix this value will be the search engine name. If it's a custom search engine (defined: https://github.com/mozilla-mobile/fenix/issues/1607) the value will be "custom" |[mozilla-mobile/fenix#1606](https://github.com/mozilla-mobile/fenix/pull/1606), [mozilla-mobile/fenix#5216](https://github.com/mozilla-mobile/fenix/pull/5216), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |1, 2 | | search.default_engine.submission_url |[string](https://mozilla.github.io/glean/book/user/metrics/string.html) |If the search engine is pre-loaded with Fenix this value will be he base URL we use to build the search query for the search engine. For example: https://mysearchengine.com/?query=%s. If it's a custom search engine (defined: https://github.com/mozilla-mobile/fenix/issues/1607) the value will be "custom" |[mozilla-mobile/fenix#1606](https://github.com/mozilla-mobile/fenix/pull/1606), [mozilla-mobile/fenix#5216](https://github.com/mozilla-mobile/fenix/pull/5216), [mozilla-mobile/fenix#15713](https://github.com/mozilla-mobile/fenix/pull/15713#issuecomment-703972068)||2021-08-01 |1, 2 |