From 2b19b28d25dc6f5b06d40f7d04fd6c7eae87b18b Mon Sep 17 00:00:00 2001 From: Emily Kager Date: Tue, 1 Oct 2019 16:24:53 -0700 Subject: [PATCH] For #5651 - Set up Standard vs Strict Tracking Protection Experiment --- .../org/mozilla/fenix/ExperimentsManager.kt | 43 +++++++++++++++++++ .../org/mozilla/fenix/FenixApplication.kt | 34 +-------------- .../java/org/mozilla/fenix/utils/Settings.kt | 40 ++++++++++++++--- docs/experiments.md | 38 ++++++++++++++++ 4 files changed, 116 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt create mode 100644 docs/experiments.md diff --git a/app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt b/app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt new file mode 100644 index 000000000..1a4246b2c --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ExperimentsManager.kt @@ -0,0 +1,43 @@ +/* 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 + +import android.content.Context +import mozilla.components.service.experiments.Experiments +import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.settings + +object ExperimentsManager { + fun initEtpExperiment(context: Context) { + // When the `fenix-etp-5651` experiment is active, set up ETP settings and GV policy. + // Note that this will not take effect the first time the application has launched, + // since there won't be enough time for the experiments library to get a list of experiments. + // It will take effect the second time the application is launched. + Experiments.withExperiment("fenix-etp-5651") { branchName -> + when (branchName) { + "control_strict" -> { + context.settings().setUseStrictTrackingProtection() + context.components.useCases.settingsUseCases.updateTrackingProtection( + context.components.core.createTrackingProtectionPolicy() + ) + } + "treatment_standard" -> { + context.settings().setUseStandardTrackingProtection() + context.components.core.createTrackingProtectionPolicy() + context.components.useCases.settingsUseCases.updateTrackingProtection( + context.components.core.createTrackingProtectionPolicy() + ) + } + else -> { + // No branch matches so we're defaulting to strict + context.settings().setUseStrictTrackingProtection() + context.components.useCases.settingsUseCases.updateTrackingProtection( + context.components.core.createTrackingProtectionPolicy() + ) + } + } + } + } +} diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 564e7d965..00e4c9393 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -17,7 +17,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking import mozilla.appservices.Megazord import mozilla.components.concept.push.PushProcessor import mozilla.components.service.experiments.Experiments @@ -101,6 +100,8 @@ open class FenixApplication : Application() { ExperimentsMetrics.activeExperiment.set(branchName) } + ExperimentsManager.initEtpExperiment(this) + setupLeakCanary() if (settings().isTelemetryEnabled) { components.analytics.metrics.start() @@ -145,37 +146,6 @@ open class FenixApplication : Application() { } } - /** - * Wait until all experiments are loaded - * - * This function will cause the caller to block until the experiments are loaded. - * It could be used in any number of reasons, but the most likely scenario is that - * a calling function needs to access the loaded experiments and wants to - * make sure that the experiments are loaded from the server before doing so. - * - * Because this function is synchronized, it can only be accessed by one thread - * at a time. Anyone trying to check the loaded status will wait if someone is - * already waiting. This is okay because the thread waiting for access to the - * function will immediately see that the loader is complete upon gaining the - * opportunity to run the function. - */ - @Synchronized - public fun waitForExperimentsToLoad() { - - // Do we know that we are already complete? - if (!experimentLoaderComplete) { - // No? Have we completed since the last call? - if (!experimentLoader.isCompleted) { - // No? Well, let's wait. - runBlocking { - experimentLoader.await() - } - } - // Set this so we don't have to wait on the next call. - experimentLoaderComplete = true - } - } - protected open fun setupLeakCanary() { // no-op, LeakCanary is disabled by default } diff --git a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt index 020afa581..e591425d5 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Settings.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Settings.kt @@ -32,7 +32,7 @@ import java.security.InvalidParameterException /** * A simple wrapper for SharedPreferences that makes reading preference a little bit easier. */ -@Suppress("LargeClass") +@Suppress("LargeClass", "TooManyFunctions") class Settings private constructor( context: Context, private val isCrashReportEnabledInBuild: Boolean @@ -107,10 +107,10 @@ class Settings private constructor( val isCrashReportingEnabled: Boolean get() = isCrashReportEnabledInBuild && - preferences.getBoolean( - appContext.getPreferenceKey(R.string.pref_key_crash_reporter), - true - ) + preferences.getBoolean( + appContext.getPreferenceKey(R.string.pref_key_crash_reporter), + true + ) val isRemoteDebuggingEnabled by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_remote_debugging), @@ -136,7 +136,7 @@ class Settings private constructor( val shouldShowTrackingProtectionOnboarding: Boolean get() = trackingProtectionOnboardingCount < trackingProtectionOnboardingMaximumCount && - !trackingProtectionOnboardingShownThisSession + !trackingProtectionOnboardingShownThisSession val shouldAutoBounceQuickActionSheet: Boolean get() = autoBounceQuickActionSheetCount < autoBounceMaximumCount @@ -201,6 +201,32 @@ class Settings private constructor( true ) + fun setUseStrictTrackingProtection() { + preferences.edit() + .putBoolean( + appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard), + false + ) + .putBoolean( + appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict), + true + ) + .apply() + } + + fun setUseStandardTrackingProtection() { + preferences.edit() + .putBoolean( + appContext.getPreferenceKey(R.string.pref_key_tracking_protection_standard), + true + ) + .putBoolean( + appContext.getPreferenceKey(R.string.pref_key_tracking_protection_strict), + false + ) + .apply() + } + var shouldDeleteBrowsingDataOnQuit by booleanPreference( appContext.getPreferenceKey(R.string.pref_key_delete_browsing_data_on_quit), default = false @@ -334,7 +360,7 @@ class Settings private constructor( val showCondition = (numTimesPrivateModeOpened == CFR_COUNT_CONDITION_FOCUS_INSTALLED && focusInstalled) || - (numTimesPrivateModeOpened == CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED && !focusInstalled) + (numTimesPrivateModeOpened == CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED && !focusInstalled) if (showCondition && !showedPrivateModeContextualFeatureRecommender) { showedPrivateModeContextualFeatureRecommender = true diff --git a/docs/experiments.md b/docs/experiments.md new file mode 100644 index 000000000..4b7d509f9 --- /dev/null +++ b/docs/experiments.md @@ -0,0 +1,38 @@ +# Experiments + +Fenix uses Mozilla's [Experiments service for experimentation](https://github.com/mozilla-mobile/android-components/blob/master/components/service/experiments/README.md). + +## Experiment Naming + +Naming must be unique and under 100 characters. + +Name experiments in the format of ""fenix_{experiment}_{issue}"", such as "fenix_etp_5651". Multiple word experiment titles should be snake cased. + +Branches should be descriptive of the control and treatment. + +Example: +Experiment: "fenix_buttonColor_1" +Branches: "control_blue", "treatment_green" + +## Applying Experiment Branches + +The Fenix engineer should [check for if a user is in and experiment and then apply the branches of this experiment](https://github.com/mozilla-mobile/android-components/blob/master/components/service/experiments/README.md#checking-if-a-user-is-part-of-an-experiment). +See [no-op experiment example](https://github.com/mozilla-mobile/fenix/pull/4551) and [ETP example](https://github.com/mozilla-mobile/fenix/pull/5723). + +## Experimenter + +[Experimenter](https://experimenter.services.mozilla.com/) is the web application for managing user experiments for Mozilla. It is intended to store information related to the design and status of experiments, and can be used to communicate about the experiment with other involved teams. +Once the Fenix engineer has added the experiment branches to Fenix, they should update the experiment in Experimenter with the unique experiment name, unique branch names, and version code for experiment to take place. +Once required steps have been completed and checked off, clicking the "ready to ship" button will alert the data team they can set up the experiment. + +## Data Review + +As long as the experiments is using existing telemetry probes, it does not need additional data review. If the experiment requires adding new telemetry/metrics, a data review will additionally have to be filled out and completed in Experimenter. + +## QA + +QA should test experiments before we turn them on and should verify opting out of experiments works as well. More details on testing experiments [here](https://github.com/mozilla-mobile/android-components/tree/master/components/service/experiments#testing-experiments) + +## Experiment Opt Out + +Users can opt out of experiments at any time via Settings -> Data collection