blob: ee7afb7578043a493c0b5b1f1af32a0e74c6a71b [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/segmentation_platform/segmentation_platform_config.h"
#include <memory>
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/segmentation_platform/default_model/low_user_engagement_model.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/segmentation_platform/public/config.h"
#include "components/segmentation_platform/public/features.h"
#include "components/segmentation_platform/public/model_provider.h"
#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/feature_guide/notifications/feature_notification_guide_service.h"
#include "chrome/browser/flags/android/cached_feature_flags.h"
#include "chrome/browser/flags/android/chrome_feature_list.h"
#include "chrome/browser/segmentation_platform/default_model/chrome_start_model_android.h"
#include "chrome/browser/segmentation_platform/default_model/query_tiles_model.h"
#include "chrome/browser/ui/android/start_surface/start_surface_android.h"
#include "components/query_tiles/switches.h"
#endif
using optimization_guide::proto::OptimizationTarget;
namespace segmentation_platform {
namespace {
constexpr char kDefaultModelEnabledParam[] = "enable_default_model";
// Default TTL for segment selection and unknown selection:
constexpr int kDummyFeatureSelectionTTLDays = 1;
constexpr int kChromeLowUserEngagementSelectionTTLDays = 30;
#if BUILDFLAG(IS_ANDROID)
constexpr int kAdaptiveToolbarDefaultSelectionTTLDays = 28;
constexpr int kChromeStartDefaultSelectionTTLDays = 30;
constexpr int kChromeStartDefaultUnknownTTLDays = 7;
// See
// https://source.chromium.org/chromium/chromium/src/+/main:chrome/android/java/src/org/chromium/chrome/browser/query_tiles/QueryTileUtils.java
const char kNumDaysKeepShowingQueryTiles[] =
"num_days_keep_showing_query_tiles";
const char kNumDaysMVCkicksBelowThreshold[] =
"num_days_mv_clicks_below_threshold";
// DEFAULT_NUM_DAYS_KEEP_SHOWING_QUERY_TILES
constexpr int kQueryTilesDefaultSelectionTTLDays = 28;
// DEFAULT_NUM_DAYS_MV_CLICKS_BELOW_THRESHOLD
constexpr int kQueryTilesDefaultUnknownTTLDays = 7;
#endif // BUILDFLAG(IS_ANDROID)
#if BUILDFLAG(IS_ANDROID)
std::unique_ptr<Config> GetConfigForAdaptiveToolbar() {
auto config = std::make_unique<Config>();
config->segmentation_key = kAdaptiveToolbarSegmentationKey;
int segment_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
chrome::android::kAdaptiveButtonInTopToolbarCustomizationV2,
"segment_selection_ttl_days", kAdaptiveToolbarDefaultSelectionTTLDays);
config->segment_selection_ttl = base::Days(segment_selection_ttl_days);
// Do not set unknown TTL so that the platform ignores unknown results.
// A hardcoded list of segment IDs known to the segmentation platform.
config->segment_ids = {
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_NEW_TAB,
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_SHARE,
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_VOICE,
};
return config;
}
#endif
std::unique_ptr<Config> GetConfigForDummyFeature() {
auto config = std::make_unique<Config>();
config->segmentation_key = kDummySegmentationKey;
config->segment_ids = {
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_DUMMY,
};
config->segment_selection_ttl = base::Days(kDummyFeatureSelectionTTLDays);
config->unknown_selection_ttl = base::Days(kDummyFeatureSelectionTTLDays);
return config;
}
#if BUILDFLAG(IS_ANDROID)
std::unique_ptr<ModelProvider> GetChromeStartAndroidModel() {
if (!base::GetFieldTrialParamByFeatureAsBool(
chrome::android::kStartSurfaceAndroid, kDefaultModelEnabledParam,
false)) {
return nullptr;
}
return std::make_unique<ChromeStartModel>();
}
std::unique_ptr<Config> GetConfigForChromeStartAndroid() {
auto config = std::make_unique<Config>();
config->segmentation_key = kChromeStartAndroidSegmentationKey;
config->segment_ids = {
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID,
};
int segment_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
chrome::android::kStartSurfaceAndroid, "segment_selection_ttl_days",
kChromeStartDefaultSelectionTTLDays);
int unknown_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
chrome::android::kStartSurfaceAndroid,
"segment_unknown_selection_ttl_days", kChromeStartDefaultUnknownTTLDays);
config->segment_selection_ttl = base::Days(segment_selection_ttl_days);
config->unknown_selection_ttl = base::Days(unknown_selection_ttl_days);
return config;
}
std::unique_ptr<ModelProvider> GetQueryTilesDefaultModel() {
if (!base::GetFieldTrialParamByFeatureAsBool(
query_tiles::features::kQueryTilesSegmentation,
kDefaultModelEnabledParam, false)) {
return nullptr;
}
return std::make_unique<QueryTilesModel>();
}
std::unique_ptr<Config> GetConfigForQueryTiles() {
auto config = std::make_unique<Config>();
config->segmentation_key = kQueryTilesSegmentationKey;
config->segment_ids = {
OptimizationTarget::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES,
};
int segment_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
query_tiles::features::kQueryTilesSegmentation,
kNumDaysKeepShowingQueryTiles, kQueryTilesDefaultSelectionTTLDays);
int unknown_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
query_tiles::features::kQueryTilesSegmentation,
kNumDaysMVCkicksBelowThreshold, kQueryTilesDefaultUnknownTTLDays);
config->segment_selection_ttl = base::Days(segment_selection_ttl_days);
config->unknown_selection_ttl = base::Days(unknown_selection_ttl_days);
return config;
}
#endif // BUILDFLAG(IS_ANDROID)
std::unique_ptr<ModelProvider> GetLowEngagementDefaultModel() {
if (!base::GetFieldTrialParamByFeatureAsBool(
features::kSegmentationPlatformLowEngagementFeature,
kDefaultModelEnabledParam, false)) {
return nullptr;
}
return std::make_unique<LowUserEngagementModel>();
}
bool IsLowEngagementFeatureEnabled() {
// TODO(ssid): Remove this extra feature and change feature guide to use the
// segmentation defined feature.
#if BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(
feature_guide::features::kSegmentationModelLowEngagedUsers)) {
return true;
}
#endif
return base::FeatureList::IsEnabled(
features::kSegmentationPlatformLowEngagementFeature);
}
std::unique_ptr<Config> GetConfigForChromeLowUserEngagement() {
auto config = std::make_unique<Config>();
config->segmentation_key = kChromeLowUserEngagementSegmentationKey;
config->segment_ids = {
OptimizationTarget::
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT,
};
#if BUILDFLAG(IS_ANDROID)
int segment_selection_ttl_days = base::GetFieldTrialParamByFeatureAsInt(
feature_guide::features::kSegmentationModelLowEngagedUsers,
"segment_selection_ttl_days", kChromeLowUserEngagementSelectionTTLDays);
#else
int segment_selection_ttl_days = kChromeLowUserEngagementSelectionTTLDays;
#endif
config->segment_selection_ttl = base::Days(segment_selection_ttl_days);
config->unknown_selection_ttl = base::Days(segment_selection_ttl_days);
return config;
}
} // namespace
std::vector<std::unique_ptr<Config>> GetSegmentationPlatformConfig() {
std::vector<std::unique_ptr<Config>> configs;
if (base::FeatureList::IsEnabled(
segmentation_platform::features::kSegmentationPlatformDummyFeature)) {
configs.emplace_back(GetConfigForDummyFeature());
}
#if BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(
chrome::android::kAdaptiveButtonInTopToolbarCustomizationV2)) {
configs.emplace_back(GetConfigForAdaptiveToolbar());
}
if (IsStartSurfaceBehaviouralTargetingEnabled()) {
configs.emplace_back(GetConfigForChromeStartAndroid());
}
if (base::FeatureList::IsEnabled(
query_tiles::features::kQueryTilesSegmentation)) {
configs.emplace_back(GetConfigForQueryTiles());
}
#endif
if (IsLowEngagementFeatureEnabled()) {
configs.emplace_back(GetConfigForChromeLowUserEngagement());
}
return configs;
}
DefaultModelsRegister::DefaultModelsRegister() = default;
DefaultModelsRegister::~DefaultModelsRegister() = default;
DefaultModelsRegister& DefaultModelsRegister::GetInstance() {
static base::NoDestructor<DefaultModelsRegister> instance;
return *instance;
}
std::unique_ptr<ModelProvider> DefaultModelsRegister::GetModelProvider(
optimization_guide::proto::OptimizationTarget target) {
auto it = providers_.find(target);
if (it != providers_.end()) {
DCHECK(it->second);
return std::move(it->second);
}
#if BUILDFLAG(IS_ANDROID)
if (target ==
optimization_guide::proto::OPTIMIZATION_TARGET_SEGMENTATION_QUERY_TILES) {
return GetQueryTilesDefaultModel();
}
if (target == optimization_guide::proto::
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_START_ANDROID) {
return GetChromeStartAndroidModel();
}
#endif
if (target ==
optimization_guide::proto::
OPTIMIZATION_TARGET_SEGMENTATION_CHROME_LOW_USER_ENGAGEMENT) {
return GetLowEngagementDefaultModel();
}
return nullptr;
}
void DefaultModelsRegister::SetModelForTesting(
optimization_guide::proto::OptimizationTarget target,
std::unique_ptr<ModelProvider> provider) {
providers_[target] = std::move(provider);
}
FieldTrialRegisterImpl::FieldTrialRegisterImpl() = default;
FieldTrialRegisterImpl::~FieldTrialRegisterImpl() = default;
void FieldTrialRegisterImpl::RegisterFieldTrial(base::StringPiece trial_name,
base::StringPiece group_name) {
// The register method is called early in startup once the platform is
// initialized. So, in most cases the client will register the field trial
// before uploading the first UMA log of the current session. We do not want
// to annotate logs from the previous session. (These comes in two types:
// histograms persisted from the previous session or stability information
// about the previous session.) Groups are not stable across sessions; we
// don't know if the current segmentation applies to the previous session.
// Incidentally, the platform records metrics to track the movement between
// groups.
// TODO(ssid): Move to a MetricsProvider approach to fill the groups so we are
// able to track how often we miss the first session log due to delays in
// platform initialization.
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
trial_name, group_name,
variations::SyntheticTrialAnnotationMode::kCurrentLog);
}
} // namespace segmentation_platform