blob: 7d9de7d95ec14b2f0e061f447e133af3977ab2f8 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include <memory>
#include <optional>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
#include "base/system/sys_info.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/component_updater/optimization_guide_on_device_model_installer.h"
#include "chrome/browser/download/background_download_service_factory.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/optimization_guide/chrome_hints_manager.h"
#include "chrome/browser/optimization_guide/chrome_model_quality_logs_uploader_service.h"
#include "chrome/browser/optimization_guide/chrome_prediction_model_store.h"
#include "chrome/browser/optimization_guide/model_execution/chrome_on_device_model_service_controller.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_key.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/sync/sync_service_factory.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_paths.h"
#include "components/component_updater/component_updater_paths.h"
#include "components/component_updater/pref_names.h"
#include "components/leveldb_proto/public/proto_database_provider.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/version_utils.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/optimization_guide/core/delivery/model_util.h"
#include "components/optimization_guide/core/delivery/prediction_manager.h"
#include "components/optimization_guide/core/hints/command_line_top_host_provider.h"
#include "components/optimization_guide/core/hints/hints_processing_util.h"
#include "components/optimization_guide/core/hints/optimization_guide_navigation_data.h"
#include "components/optimization_guide/core/hints/optimization_guide_store.h"
#include "components/optimization_guide/core/hints/tab_url_provider.h"
#include "components/optimization_guide/core/hints/top_host_provider.h"
#include "components/optimization_guide/core/model_execution/feature_keys.h"
#include "components/optimization_guide/core/model_execution/model_broker_client.h"
#include "components/optimization_guide/core/model_execution/model_execution_features_controller.h"
#include "components/optimization_guide/core/model_execution/model_execution_manager.h"
#include "components/optimization_guide/core/model_execution/on_device_asset_manager.h"
#include "components/optimization_guide/core/model_execution/on_device_model_component.h"
#include "components/optimization_guide/core/model_execution/on_device_model_service_controller.h"
#include "components/optimization_guide/core/model_execution/performance_class.h"
#include "components/optimization_guide/core/model_quality/model_quality_log_entry.h"
#include "components/optimization_guide/core/model_quality/model_quality_logs_uploader_service.h"
#include "components/optimization_guide/core/model_quality/model_quality_util.h"
#include "components/optimization_guide/core/optimization_guide_constants.h"
#include "components/optimization_guide/core/optimization_guide_features.h"
#include "components/optimization_guide/core/optimization_guide_logger.h"
#include "components/optimization_guide/core/optimization_guide_model_executor.h"
#include "components/optimization_guide/core/optimization_guide_prefs.h"
#include "components/optimization_guide/core/optimization_guide_switches.h"
#include "components/optimization_guide/core/optimization_guide_util.h"
#include "components/optimization_guide/proto/hints.pb.h"
#include "components/optimization_guide/proto/models.pb.h"
#include "components/prefs/pref_service.h"
#include "components/services/unzip/content/unzip_service.h"
#include "components/user_prefs/user_prefs.h"
#include "components/variations/service/variations_service.h"
#include "components/variations/synthetic_trials.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/commerce/price_tracking/android/price_tracking_notification_bridge.h"
#include "chrome/browser/optimization_guide/android/optimization_guide_bridge.h"
#include "chrome/browser/optimization_guide/android/optimization_guide_tab_url_provider_android.h"
#else
#include "chrome/browser/optimization_guide/optimization_guide_tab_url_provider.h"
#endif
namespace {
using ::optimization_guide::ModelExecutionFeaturesController;
using ::optimization_guide::OnDeviceModelComponentStateManager;
using ::optimization_guide::OnDeviceModelPerformanceClass;
using ::optimization_guide::OnDeviceModelServiceController;
// Used to override the value of `version_info::IsOfficialBuild()` for tests.
std::optional<bool> g_is_official_build_for_testing;
// Returns the profile to use for when setting up the keyed service when the
// profile is Off-The-Record. For guest profiles, returns a loaded profile if
// one exists, otherwise just the original profile of the OTR profile. Note:
// guest profiles are off-the-record and "original" profiles.
Profile* GetProfileForOTROptimizationGuide(Profile* profile) {
DCHECK(profile);
DCHECK(profile->IsOffTheRecord());
if (profile->IsGuestSession()) {
// Guest sessions need to rely on the stores from real profiles
// as guest profiles cannot fetch or store new models. Note: only
// loaded profiles should be used as we do not want to force load
// another profile as that can lead to start up regressions.
std::vector<Profile*> profiles =
g_browser_process->profile_manager()->GetLoadedProfiles();
if (!profiles.empty()) {
return profiles[0];
}
}
return profile->GetOriginalProfile();
}
scoped_refptr<optimization_guide::OnDeviceModelServiceController>
GetOnDeviceModelServiceController(
base::WeakPtr<optimization_guide::OnDeviceModelComponentStateManager>
on_device_component_manager) {
scoped_refptr<optimization_guide::OnDeviceModelServiceController>
service_controller = optimization_guide::
ChromeOnDeviceModelServiceController::GetSingleInstanceMayBeNull();
if (!service_controller) {
service_controller = base::MakeRefCounted<
optimization_guide::ChromeOnDeviceModelServiceController>(
std::move(on_device_component_manager));
service_controller->Init();
}
return service_controller;
}
class OnDeviceModelComponentStateManagerDelegate
: public OnDeviceModelComponentStateManager::Delegate {
public:
~OnDeviceModelComponentStateManagerDelegate() override = default;
base::FilePath GetInstallDirectory() override {
base::FilePath local_install_path;
base::PathService::Get(component_updater::DIR_COMPONENT_USER,
&local_install_path);
return local_install_path;
}
void GetFreeDiskSpace(const base::FilePath& path,
base::OnceCallback<void(int64_t)> callback) override {
base::TaskTraits traits = {base::MayBlock(),
base::TaskPriority::BEST_EFFORT};
if (optimization_guide::switches::
ShouldGetFreeDiskSpaceWithUserVisiblePriorityTask()) {
traits.UpdatePriority(base::TaskPriority::USER_VISIBLE);
}
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, traits,
base::BindOnce(&base::SysInfo::AmountOfFreeDiskSpace, path),
std::move(callback));
}
void RegisterInstaller(
scoped_refptr<OnDeviceModelComponentStateManager> state_manager,
bool is_already_installing) override {
if (!g_browser_process) {
return;
}
component_updater::RegisterOptimizationGuideOnDeviceModelComponent(
g_browser_process->component_updater(), state_manager->GetWeakPtr(),
is_already_installing);
}
void Uninstall(scoped_refptr<OnDeviceModelComponentStateManager>
state_manager) override {
component_updater::UninstallOptimizationGuideOnDeviceModelComponent(
state_manager->GetWeakPtr());
}
};
} // namespace
// static
std::unique_ptr<optimization_guide::PushNotificationManager>
OptimizationGuideKeyedService::MaybeCreatePushNotificationManager(
Profile* profile) {
if (optimization_guide::features::IsPushNotificationsEnabled()) {
auto push_notification_manager =
std::make_unique<optimization_guide::PushNotificationManager>();
#if BUILDFLAG(IS_ANDROID)
push_notification_manager->AddObserver(
PriceTrackingNotificationBridge::GetForBrowserContext(profile));
#endif
return push_notification_manager;
}
return nullptr;
}
// static
void OptimizationGuideKeyedService::SetIsOfficialBuildForTesting(
bool is_official_build) {
g_is_official_build_for_testing = is_official_build;
}
OptimizationGuideKeyedService::OptimizationGuideKeyedService(
content::BrowserContext* browser_context)
: browser_context_(browser_context) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (browser_context_) { // Null in MockOptimizationGuideKeyedService.
Initialize();
}
}
OptimizationGuideKeyedService::~OptimizationGuideKeyedService() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
void OptimizationGuideKeyedService::BindModelBroker(
mojo::PendingReceiver<optimization_guide::mojom::ModelBroker> receiver) {
if (!base::FeatureList::IsEnabled(
optimization_guide::features::
kBrokerModelSessionsForUntrustedProcesses)) {
return;
}
if (!base::FeatureList::IsEnabled(
optimization_guide::features::kOptimizationGuideModelExecution)) {
return;
}
if (!base::FeatureList::IsEnabled(
optimization_guide::features::kOptimizationGuideOnDeviceModel)) {
return;
}
GetOnDeviceModelServiceController(on_device_component_manager_->GetWeakPtr())
->BindBroker(std::move(receiver));
}
std::unique_ptr<optimization_guide::ModelBrokerClient>
OptimizationGuideKeyedService::CreateModelBrokerClient() {
mojo::PendingRemote<optimization_guide::mojom::ModelBroker> remote;
GetOnDeviceModelServiceController(on_device_component_manager_->GetWeakPtr())
->BindBroker(remote.InitWithNewPipeAndPassReceiver());
return std::make_unique<optimization_guide::ModelBrokerClient>(
std::move(remote), optimization_guide::CreateSessionArgs(
optimization_guide_logger_->GetWeakPtr(), {}));
}
#if BUILDFLAG(IS_ANDROID)
base::android::ScopedJavaLocalRef<jobject>
OptimizationGuideKeyedService::GetJavaObject() {
if (!android_bridge_) {
android_bridge_ =
std::make_unique<optimization_guide::android::OptimizationGuideBridge>(
this);
}
return android_bridge_->GetJavaObject();
}
#endif
download::BackgroundDownloadService*
OptimizationGuideKeyedService::BackgroundDownloadServiceProvider() {
Profile* profile = Profile::FromBrowserContext(browser_context_);
return BackgroundDownloadServiceFactory::GetForKey(profile->GetProfileKey());
}
void OptimizationGuideKeyedService::Initialize() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
Profile* profile = Profile::FromBrowserContext(browser_context_);
base::FilePath profile_path = profile->GetOriginalProfile()->GetPath();
// We have different behavior if |this| is created for an incognito profile.
// For incognito profiles, we act in "read-only" mode of the original
// profile's store and do not fetch any new hints or models.
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory;
base::WeakPtr<optimization_guide::OptimizationGuideStore> hint_store;
if (profile->IsOffTheRecord()) {
OptimizationGuideKeyedService* original_ogks =
OptimizationGuideKeyedServiceFactory::GetForProfile(
GetProfileForOTROptimizationGuide(profile));
DCHECK(original_ogks);
hint_store = original_ogks->GetHintsManager()->hint_store();
} else {
// Use the database associated with the original profile.
auto* proto_db_provider = profile->GetOriginalProfile()
->GetDefaultStoragePartition()
->GetProtoDatabaseProvider();
url_loader_factory = profile->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
// Only create a top host provider from the command line if provided.
top_host_provider_ =
optimization_guide::CommandLineTopHostProvider::CreateIfEnabled();
bool optimization_guide_fetching_enabled =
optimization_guide::IsUserPermittedToFetchFromRemoteOptimizationGuide(
profile->IsOffTheRecord(), profile->GetPrefs());
UMA_HISTOGRAM_BOOLEAN("OptimizationGuide.RemoteFetchingEnabled",
optimization_guide_fetching_enabled);
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
"SyntheticOptimizationGuideRemoteFetching",
optimization_guide_fetching_enabled ? "Enabled" : "Disabled",
variations::SyntheticTrialAnnotationMode::kCurrentLog);
#if BUILDFLAG(IS_ANDROID)
tab_url_provider_ = std::make_unique<
optimization_guide::android::OptimizationGuideTabUrlProviderAndroid>(
profile);
#else
tab_url_provider_ =
std::make_unique<OptimizationGuideTabUrlProvider>(profile);
#endif
hint_store_ =
optimization_guide::features::ShouldPersistHintsToDisk()
? std::make_unique<optimization_guide::OptimizationGuideStore>(
proto_db_provider,
profile_path.Append(
optimization_guide::kOptimizationGuideHintStore),
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT}))
: nullptr;
hint_store = hint_store_ ? hint_store_->AsWeakPtr() : nullptr;
}
optimization_guide_logger_ = OptimizationGuideLogger::GetInstance();
DCHECK(optimization_guide_logger_);
hints_manager_ = std::make_unique<optimization_guide::ChromeHintsManager>(
profile, profile->GetPrefs(), hint_store, top_host_provider_.get(),
tab_url_provider_.get(), url_loader_factory,
MaybeCreatePushNotificationManager(profile),
IdentityManagerFactory::GetForProfile(profile),
optimization_guide_logger_.get());
prediction_manager_ = std::make_unique<optimization_guide::PredictionManager>(
optimization_guide::ChromePredictionModelStore::GetInstance(),
g_browser_process->shared_url_loader_factory(), profile->GetPrefs(),
g_browser_process->GetApplicationLocale(),
optimization_guide_logger_.get(),
base::BindRepeating(&unzip::LaunchUnzipper));
InitializeModelExecution(profile);
// Register for profile initialization event to initialize the model
// downloads.
profile_observation_.Observe(profile);
OPTIMIZATION_GUIDE_LOG(
optimization_guide_common::mojom::LogSource::SERVICE_AND_SETTINGS,
optimization_guide_logger_,
"OptimizationGuide: KeyedService is initalized");
optimization_guide::LogFeatureFlagsInfo(optimization_guide_logger_.get(),
profile->IsOffTheRecord(),
profile->GetPrefs());
}
void OptimizationGuideKeyedService::InitializeModelExecution(Profile* profile) {
if (!base::FeatureList::IsEnabled(
optimization_guide::features::kOptimizationGuideModelExecution)) {
return;
}
auto url_loader_factory = profile->GetDefaultStoragePartition()
->GetURLLoaderFactoryForBrowserProcess();
on_device_component_manager_ =
optimization_guide::OnDeviceModelComponentStateManager::CreateOrGet(
g_browser_process->local_state(),
std::make_unique<OnDeviceModelComponentStateManagerDelegate>());
on_device_component_manager_->OnStartup();
if (!profile->IsOffTheRecord() && !profile->IsGuestSession()) {
// With multiple profiles we only want to fetch the performance class
// once. This bool helps avoid fetching multiple times.
static bool performance_class_fetched = false;
if (!performance_class_fetched &&
(base::FeatureList::IsEnabled(
optimization_guide::features::kLogOnDeviceMetricsOnStartup) ||
optimization_guide::features::IsOnDeviceExecutionEnabled()) &&
on_device_component_manager_->NeedsPerformanceClassUpdate()) {
performance_class_fetched = true;
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
&OptimizationGuideKeyedService::EnsurePerformanceClassAvailable,
weak_factory_.GetWeakPtr(), base::DoNothing()),
optimization_guide::features::GetOnDeviceStartupMetricDelay());
}
// If the perf class was previously determined, register that.
GetOnDeviceModelServiceController(
on_device_component_manager_->GetWeakPtr())
->RegisterPerformanceClassSyntheticTrial(
optimization_guide::PerformanceClassFromPref(
*g_browser_process->local_state()));
auto* variations_service = g_browser_process->variations_service();
auto dogfood_status =
variations_service && variations_service->IsLikelyDogfoodClient()
? ModelExecutionFeaturesController::DogfoodStatus::DOGFOOD
: ModelExecutionFeaturesController::DogfoodStatus::NON_DOGFOOD;
bool is_official_build = g_is_official_build_for_testing.value_or(
version_info::IsOfficialBuild());
model_execution_features_controller_ =
std::make_unique<optimization_guide::ModelExecutionFeaturesController>(
profile->GetPrefs(), IdentityManagerFactory::GetForProfile(profile),
g_browser_process->local_state(), dogfood_status,
is_official_build);
// Don't create logs uploader service when feature is disabled. All the
// logs upload get route through this service which exists one per
// profile.
if (base::FeatureList::IsEnabled(
optimization_guide::features::kModelQualityLogging)) {
model_quality_logs_uploader_service_ = std::make_unique<
optimization_guide::ChromeModelQualityLogsUploaderService>(
url_loader_factory, g_browser_process->local_state(),
model_execution_features_controller_
? model_execution_features_controller_->GetWeakPtr()
: nullptr);
}
RecordModelExecutionFeatureSyntheticFieldTrial(
optimization_guide::UserVisibleFeatureKey::kHistorySearch,
"HistorySearch");
}
scoped_refptr<optimization_guide::OnDeviceModelServiceController>
service_controller;
if (base::FeatureList::IsEnabled(
optimization_guide::features::kOptimizationGuideOnDeviceModel)) {
service_controller = GetOnDeviceModelServiceController(
on_device_component_manager_->GetWeakPtr());
on_device_asset_manager_ =
std::make_unique<optimization_guide::OnDeviceAssetManager>(
g_browser_process->local_state(), service_controller->GetWeakPtr(),
on_device_component_manager_->GetWeakPtr(), this);
}
model_execution_manager_ =
std::make_unique<optimization_guide::ModelExecutionManager>(
url_loader_factory, IdentityManagerFactory::GetForProfile(profile),
std::move(service_controller), optimization_guide_logger_.get(),
model_quality_logs_uploader_service_
? model_quality_logs_uploader_service_->GetWeakPtr()
: nullptr);
}
optimization_guide::ChromeHintsManager*
OptimizationGuideKeyedService::GetHintsManager() {
return hints_manager_.get();
}
void OptimizationGuideKeyedService::OnNavigationStartOrRedirect(
OptimizationGuideNavigationData* navigation_data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::flat_set<optimization_guide::proto::OptimizationType>
registered_optimization_types =
hints_manager_->registered_optimization_types();
if (!registered_optimization_types.empty()) {
hints_manager_->OnNavigationStartOrRedirect(navigation_data,
base::DoNothing());
}
if (navigation_data) {
navigation_data->set_registered_optimization_types(
hints_manager_->registered_optimization_types());
navigation_data->set_registered_optimization_targets(
prediction_manager_->GetRegisteredOptimizationTargets());
}
}
void OptimizationGuideKeyedService::OnNavigationFinish(
const std::vector<GURL>& navigation_redirect_chain) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
hints_manager_->OnNavigationFinish(navigation_redirect_chain);
}
void OptimizationGuideKeyedService::AddObserverForOptimizationTargetModel(
optimization_guide::proto::OptimizationTarget optimization_target,
const std::optional<optimization_guide::proto::Any>& model_metadata,
optimization_guide::OptimizationTargetModelObserver* observer) {
prediction_manager_->AddObserverForOptimizationTargetModel(
optimization_target, model_metadata, observer);
}
void OptimizationGuideKeyedService::RemoveObserverForOptimizationTargetModel(
optimization_guide::proto::OptimizationTarget optimization_target,
optimization_guide::OptimizationTargetModelObserver* observer) {
prediction_manager_->RemoveObserverForOptimizationTargetModel(
optimization_target, observer);
}
void OptimizationGuideKeyedService::RegisterOptimizationTypes(
const std::vector<optimization_guide::proto::OptimizationType>&
optimization_types) {
hints_manager_->RegisterOptimizationTypes(optimization_types);
}
optimization_guide::OptimizationGuideDecision
OptimizationGuideKeyedService::CanApplyOptimization(
const GURL& url,
optimization_guide::proto::OptimizationType optimization_type,
optimization_guide::OptimizationMetadata* optimization_metadata) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
optimization_guide::OptimizationTypeDecision optimization_type_decision =
hints_manager_->CanApplyOptimization(url, optimization_type,
optimization_metadata);
base::UmaHistogramEnumeration(
"OptimizationGuide.ApplyDecision." +
optimization_guide::GetStringNameForOptimizationType(
optimization_type),
optimization_type_decision);
return optimization_guide::ChromeHintsManager::
GetOptimizationGuideDecisionFromOptimizationTypeDecision(
optimization_type_decision);
}
void OptimizationGuideKeyedService::CanApplyOptimization(
const GURL& url,
optimization_guide::proto::OptimizationType optimization_type,
optimization_guide::OptimizationGuideDecisionCallback callback) {
hints_manager_->CanApplyOptimization(url, optimization_type,
std::move(callback));
}
void OptimizationGuideKeyedService::CanApplyOptimizationOnDemand(
const std::vector<GURL>& urls,
const base::flat_set<optimization_guide::proto::OptimizationType>&
optimization_types,
optimization_guide::proto::RequestContext request_context,
optimization_guide::OnDemandOptimizationGuideDecisionRepeatingCallback
callback,
std::optional<optimization_guide::proto::RequestContextMetadata>
request_context_metadata) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(request_context !=
optimization_guide::proto::RequestContext::CONTEXT_UNSPECIFIED);
hints_manager_->CanApplyOptimizationOnDemand(urls, optimization_types,
request_context, callback,
request_context_metadata);
}
std::unique_ptr<optimization_guide::OptimizationGuideModelExecutor::Session>
OptimizationGuideKeyedService::StartSession(
optimization_guide::ModelBasedCapabilityKey feature,
const std::optional<optimization_guide::SessionConfigParams>&
config_params) {
if (!model_execution_manager_) {
return nullptr;
}
return model_execution_manager_->StartSession(feature, config_params);
}
void OptimizationGuideKeyedService::ExecuteModel(
optimization_guide::ModelBasedCapabilityKey feature,
const google::protobuf::MessageLite& request_metadata,
const std::optional<base::TimeDelta>& execution_timeout,
optimization_guide::OptimizationGuideModelExecutionResultCallback
callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!model_execution_manager_) {
std::move(callback).Run(
optimization_guide::OptimizationGuideModelExecutionResult(
base::unexpected(
optimization_guide::OptimizationGuideModelExecutionError::
FromModelExecutionError(
optimization_guide::
OptimizationGuideModelExecutionError::
ModelExecutionError::kGenericFailure)),
nullptr),
nullptr);
return;
}
model_execution_manager_->ExecuteModel(
feature, request_metadata, execution_timeout,
/*log_ai_data_request=*/nullptr, std::move(callback));
}
void OptimizationGuideKeyedService::AddOnDeviceModelAvailabilityChangeObserver(
optimization_guide::ModelBasedCapabilityKey feature,
optimization_guide::OnDeviceModelAvailabilityObserver* observer) {
if (!on_device_component_manager_) {
return;
}
auto service_controller = GetOnDeviceModelServiceController(
on_device_component_manager_->GetWeakPtr());
if (service_controller) {
service_controller->AddOnDeviceModelAvailabilityChangeObserver(feature,
observer);
}
}
void OptimizationGuideKeyedService::
RemoveOnDeviceModelAvailabilityChangeObserver(
optimization_guide::ModelBasedCapabilityKey feature,
optimization_guide::OnDeviceModelAvailabilityObserver* observer) {
if (!on_device_component_manager_) {
return;
}
auto service_controller = GetOnDeviceModelServiceController(
on_device_component_manager_->GetWeakPtr());
if (service_controller) {
service_controller->RemoveOnDeviceModelAvailabilityChangeObserver(feature,
observer);
}
}
on_device_model::Capabilities
OptimizationGuideKeyedService::GetOnDeviceCapabilities() {
if (!model_execution_manager_) {
return {};
}
auto capabilities = model_execution_manager_->GetOnDeviceCapabilities();
capabilities.RetainAll(GetPossibleOnDeviceCapabilities());
return capabilities;
}
void OptimizationGuideKeyedService::OnProfileInitializationComplete(
Profile* profile) {
DCHECK(profile_observation_.IsObservingSource(profile));
profile_observation_.Reset();
if (!optimization_guide::features::IsModelDownloadingEnabled()) {
return;
}
if (profile->IsOffTheRecord()) {
return;
}
GetPredictionManager()->MaybeInitializeModelDownloads(
g_browser_process->local_state(), BackgroundDownloadServiceProvider());
}
void OptimizationGuideKeyedService::AddHintForTesting(
const GURL& url,
optimization_guide::proto::OptimizationType optimization_type,
const std::optional<optimization_guide::OptimizationMetadata>& metadata) {
hints_manager_->AddHintForTesting(url, optimization_type, metadata);
}
void OptimizationGuideKeyedService::AddOnDemandHintForTesting(
const GURL& url,
optimization_guide::proto::OptimizationType optimization_type,
const optimization_guide::OptimizationGuideDecisionWithMetadata& decision) {
hints_manager_->AddOnDemandHintForTesting(url, optimization_type, // IN-TEST
decision);
}
void OptimizationGuideKeyedService::AddExecutionResultForTesting(
optimization_guide::ModelBasedCapabilityKey feature,
optimization_guide::OptimizationGuideModelExecutionResult result) {
model_execution_manager_->AddExecutionResultForTesting(feature, // IN-TEST
std::move(result));
}
void OptimizationGuideKeyedService::ClearData() {
hints_manager_->ClearFetchedHints();
}
void OptimizationGuideKeyedService::Shutdown() {
hints_manager_->Shutdown();
if (model_execution_manager_) {
model_execution_manager_->Shutdown();
}
}
void OptimizationGuideKeyedService::OverrideTargetModelForTesting(
optimization_guide::proto::OptimizationTarget optimization_target,
std::unique_ptr<optimization_guide::ModelInfo> model_info) {
prediction_manager_->OverrideTargetModelForTesting( // IN-TEST
optimization_target, std::move(model_info));
}
void OptimizationGuideKeyedService::
SetModelQualityLogsUploaderServiceForTesting(
std::unique_ptr<optimization_guide::ModelQualityLogsUploaderService>
uploader) {
model_quality_logs_uploader_service_ = std::move(uploader);
}
optimization_guide::ModelExecutionFeaturesController*
OptimizationGuideKeyedService::GetModelExecutionFeaturesController() {
return model_execution_features_controller_.get();
}
void OptimizationGuideKeyedService::AllowUnsignedUserForTesting(
optimization_guide::UserVisibleFeatureKey feature) {
model_execution_features_controller_->AllowUnsignedUserForTesting(
feature); // IN-TEST
}
bool OptimizationGuideKeyedService::ShouldFeatureBeCurrentlyEnabledForUser(
optimization_guide::UserVisibleFeatureKey feature) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!model_execution_features_controller_) {
return false;
}
return model_execution_features_controller_
->ShouldFeatureBeCurrentlyEnabledForUser(feature);
}
bool OptimizationGuideKeyedService::
ShouldFeatureAllowModelExecutionForSignedInUser(
optimization_guide::UserVisibleFeatureKey feature) const {
if (!model_execution_features_controller_) {
return false;
}
return model_execution_features_controller_
->ShouldFeatureAllowModelExecutionForSignedInUser(feature);
}
bool OptimizationGuideKeyedService::ShouldFeatureBeCurrentlyAllowedForFeedback(
optimization_guide::proto::LogAiDataRequest::FeatureCase feature) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// If logging is enabled, feedback is always also enabled.
const optimization_guide::MqlsFeatureMetadata* metadata =
optimization_guide::MqlsFeatureRegistry::GetInstance().GetFeature(
feature);
DCHECK(metadata);
if (model_execution_features_controller_ &&
model_execution_features_controller_
->ShouldFeatureBeCurrentlyAllowedForLogging(metadata)) {
return true;
}
// Otherwise, feedback is disabled, with one exception: On dogfood clients,
// feedback is always enabled (as long as the feature is enabled).
auto* variations_service = g_browser_process->variations_service();
return !!variations_service && variations_service->IsLikelyDogfoodClient();
}
bool OptimizationGuideKeyedService::ShouldModelExecutionBeAllowedForUser()
const {
return model_execution_features_controller_ &&
model_execution_features_controller_
->ShouldModelExecutionBeAllowedForUser();
}
bool OptimizationGuideKeyedService::IsSettingVisible(
optimization_guide::UserVisibleFeatureKey feature) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!model_execution_features_controller_) {
return false;
}
#if !BUILDFLAG(IS_ANDROID)
if (base::FeatureList::IsEnabled(
optimization_guide::features::kAiSettingsPageForceAvailable)) {
return true;
}
#endif
return model_execution_features_controller_->IsSettingVisible(feature);
}
void OptimizationGuideKeyedService::AddModelExecutionSettingsEnabledObserver(
optimization_guide::SettingsEnabledObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!model_execution_features_controller_) {
return;
}
model_execution_features_controller_->AddObserver(observer);
}
void OptimizationGuideKeyedService::RemoveModelExecutionSettingsEnabledObserver(
optimization_guide::SettingsEnabledObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!model_execution_features_controller_) {
return;
}
model_execution_features_controller_->RemoveObserver(observer);
}
void OptimizationGuideKeyedService::
RecordModelExecutionFeatureSyntheticFieldTrial(
optimization_guide::UserVisibleFeatureKey feature,
std::string_view feature_name) {
ChromeMetricsServiceAccessor::RegisterSyntheticFieldTrial(
base::StrCat({"SyntheticModelExecutionFeature", feature_name}),
ShouldFeatureBeCurrentlyEnabledForUser(feature) ? "Enabled" : "Disabled",
variations::SyntheticTrialAnnotationMode::kCurrentLog);
}
optimization_guide::OnDeviceModelEligibilityReason
OptimizationGuideKeyedService::GetOnDeviceModelEligibility(
optimization_guide::ModelBasedCapabilityKey feature) {
if (!model_execution_manager_) {
return optimization_guide::OnDeviceModelEligibilityReason::
kFeatureNotEnabled;
}
return model_execution_manager_->GetOnDeviceModelEligibility(feature);
}
void OptimizationGuideKeyedService::GetOnDeviceModelEligibilityAsync(
optimization_guide::ModelBasedCapabilityKey feature,
const on_device_model::Capabilities& capabilities,
base::OnceCallback<void(optimization_guide::OnDeviceModelEligibilityReason)>
callback) {
EnsurePerformanceClassAvailable(base::BindOnce(
&OptimizationGuideKeyedService::FinishGetOnDeviceModelEligibility,
weak_factory_.GetWeakPtr(), feature, capabilities, std::move(callback)));
}
std::optional<optimization_guide::SamplingParamsConfig>
OptimizationGuideKeyedService::GetSamplingParamsConfig(
optimization_guide::ModelBasedCapabilityKey feature) {
if (!model_execution_manager_) {
return std::nullopt;
}
return model_execution_manager_->GetSamplingParamsConfig(feature);
}
std::optional<const optimization_guide::proto::Any>
OptimizationGuideKeyedService::GetFeatureMetadata(
optimization_guide::ModelBasedCapabilityKey feature) {
if (!model_execution_manager_) {
return std::nullopt;
}
return model_execution_manager_->GetFeatureMetadata(feature);
}
void OptimizationGuideKeyedService::EnsurePerformanceClassAvailable(
base::OnceClosure complete) {
GetOnDeviceModelServiceController(on_device_component_manager_->GetWeakPtr())
->EnsurePerformanceClassAvailable(std::move(complete));
}
void OptimizationGuideKeyedService::FinishGetOnDeviceModelEligibility(
optimization_guide::ModelBasedCapabilityKey feature,
const on_device_model::Capabilities& capabilities,
base::OnceCallback<void(optimization_guide::OnDeviceModelEligibilityReason)>
callback) {
// If this device will never support the requested capabilities, return not
// available.
if (!GetPossibleOnDeviceCapabilities().HasAll(capabilities)) {
std::move(callback).Run(optimization_guide::OnDeviceModelEligibilityReason::
kModelAdaptationNotAvailable);
return;
}
std::move(callback).Run(GetOnDeviceModelEligibility(feature));
}
on_device_model::Capabilities
OptimizationGuideKeyedService::GetPossibleOnDeviceCapabilities() const {
if (!on_device_component_manager_) {
return {};
}
on_device_model::Capabilities capabilities;
if (on_device_component_manager_->SupportsImageInput()) {
capabilities.Put(on_device_model::CapabilityFlags::kImageInput);
}
if (on_device_component_manager_->SupportsAudioInput()) {
capabilities.Put(on_device_model::CapabilityFlags::kAudioInput);
}
return capabilities;
}